Searching...
April 15, 2014

Heartbleed All Mighty: How Can This Be?!

Sebelum saya mengupas source code OpenSSL yang menyebabkan HeartBleed menjadi mimpi buruk keamanan internet, ada baiknya kamu membaca Heartbleed Bug lebih jauh di sini.

Pada kesempatan kali ini, saya akan mencoba menganalisa bagian dari source code OpenSSL yang merealisasikan mimpi buruk para white hat hacker. Telah kita ketahui sebelumnya bahwa OpenSSL memiliki heartbeat bug pada versi OpenSSL 1.0.1 (release 14 Maret 2012) sampai dengan versi OpenSSL 1.0.1f (release 6 Januari 2014).

Untuk itu, pada analisa kali ini, saya akan membongkar source code OpenSSL 1.0.1f. Kamu bisa mendownload source code OpenSSL 1.0.1f di sini.

Bug

Melihat perbedaan source code yang telah di-commit pada OpenSSL 1.0.1g (versi yang tidak vulnerable) di sini, kita akan memulai perjalanan panjang kita dalam menyelidiki bagaimana bug ini meneror keamanan internet.

Setelah mendownload OpenSSL 1.0.1f, extract source code, kemudian buka file ssl/d1_both.c.  Dan saya menemukan perbedaan code antara 1.0.1f dan 1.0.1g di baris ke 1455.

int            
dtls1_process_heartbeat(SSL *s)
    {          
    unsigned char *p = &s->s3->rrec.data[0], *pl;
    unsigned short hbtype;
    unsigned int payload;
    unsigned int padding = 16; /* Use minimum padding */


Dan kita bisa mendapatkan pointer ke data record SSLv3 yang dijelaskan di file ssl/ssl3.h di baris ke 348.

typedef struct ssl3_record_st
 {
/*r */ int type;               /* type of record */
/*rw*/ unsigned int length;    /* How many bytes available */
/*r */ unsigned int off;       /* read/write offset into 'buf' */
/*rw*/ unsigned char *data;    /* pointer to the record data */
/*rw*/ unsigned char *input;   /* where the decode bytes are */
/*r */ unsigned char *comp;    /* only used with decompression - malloc()ed */
/*r */  unsigned long epoch;    /* epoch number, needed by DTLS1 */
/*r */  unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */
 } SSL3_RECORD;


Record memiliki sebuah tipe, sebuah panjang, dan sebuah data. Kembali ke file ssl/d1_both.c baris ke 1455 - 1465.

dtls1_process_heartbeat(SSL *s)
 {
 unsigned char *p = &s->s3->rrec.data[0], *pl;
 unsigned short hbtype;
 unsigned int payload;
 unsigned int padding = 16; /* Use minimum padding */

 /* Read type and payload length first */
 hbtype = *p++;
 n2s(p, payload);
 pl = p;


Byte pertama dalam record adalah heartbeat type. Macro n2s mengambil 2 byte dari p, dan meletakannya di dalam payload. Sebenarnya ini adalah panjang dari payload. Perhatikan, panjang sesungguhnya dari record SSLv3 tidak dicek!

Variable pl adalah yang menyebabkan heartbeat data, disuplai dari yang meminta heartbeat data (user). Lebih lanjut dari fungsi code di atas, kita akan menemukan ini pada baris ke 1472 - 1482 (masih di file ssl/d1_both.c).

 if (hbtype == TLS1_HB_REQUEST)
  {
  unsigned char *buffer, *bp;
  int r;

  /* Allocate memory for the response, size is 1 byte
   * message type, plus 2 bytes payload length, plus
   * payload, plus padding
   */
  buffer = OPENSSL_malloc(1 + 2 + payload + padding);
  bp = buffer;


Kemudian kita alokasikan memori sebanyak yang diminta oleh user, lebih tepatnya di atas 65535 + 1 + 2 +16.  Variable bp akan menjadi pointer untuk mengakses memory seperti yang tertulis pada baris ke 1484 - 1490.

  /* Enter response type, length and copy payload */
  *bp++ = TLS1_HB_RESPONSE;
  s2n(payload, bp);
  memcpy(bp, pl, payload);
  bp += payload;
  /* Random padding */
  RAND_pseudo_bytes(bp, padding);


Kita akan fokus pada

memcpy(bp, pl, payload) ;

memcpy adalah command untuk mengkopi data, dan dia membutuhkan tiga informasi, yaitu bp, pl, dan payload. Bit pertama dari informasi (bp) adalah tujuan terakhir data yang akan dikopi. Bit kedua (pl) adalah lokasi data yang akan dikopi. Dan bit ketiga (payload) adalah besarnya data yang akan dicari tau oleh komputer, kapan data akan dikopi (misalkan: payload akan menentukan apakah data harus dikopi jika data sudah sebebesar n byte). Dalam kasus ini, bp adalah suatu TEMPAT, pl adalah DATA yang akan dikirim, dan PAYLOAD adalah UKURAN data.

Satu hal penting yang harus di ketahui adalah proses pengkopian tidak sesederhana yang kita bayangkan, karena sejatinya TIDAK ADA memori yang kosong. Maka dari itu, bp akan menemukan dimana (tujuan) data user yang akan dikopi sebenarnya tidak kosong, malah penuh dengan memori (not enough space). Kemudian komputer akan menganggap tempat itu "kosong" karena deret data yang memenuhi bagian itu akan di "mark for to be deleted" sampai bagian itu diisi dengan data yang baru. Dan tujuan bp sebenarnya data data lama yang tidak menjadi masalah jika di-overwrite.

Sekarang, idealnya ketika memcpy mengambil infomasi dari pl (data), dan memberikannya pada bp (pencari lokasi), informasi itu sebenarnya informasi lama (masuk akal kan? baca kembali paragraf sebelumnya!). Kemudian payload memberitau memcpy berapa besar datanya (pl), dan ruang yang harus dicari oleh bp harus sama ukurannya dengan ukuran yang diberitau oleh payload (ingat payload itu memberi informasi beruka data size). Data itulah yang akan dikirim ke user, tepat seperti yang user kirim pada tempat pertama, kurang lebih ini seperti: "Jika kamu melakukan transaksi 1:1 (adil) yang rapih, maka apa yang masuk, adalah apa yang akan keluar".

Sebenarnya ini belum menjadi masalah selama payload tidak berbohong. Bagaimana jika payload berbohong, bagaimana jika besarnya data yang diinfokan oleh payload sebenarnya adalah informasi palsu? Jika memory yang seharusnya dikatakan oleh payload adalah 0 kb, namun dia (payload) berbohong dengan mengatakan "kita membutuhkan 64 kb memory", simple nya, kita dalam masalah. Mengapa? Karena memcpy akan membuat landing strip pada bp berukuran 64 kb yang penuh dengan data lama. Data lama ini tidak akan di-overwrite, mengapa? Karena sebenarnya payload berukuran 0 KB, jadi tidak ada data yang perlu di-overwrite kan? Data lama ini akan dikirim kembali ke user. D'oh!

Makro s2n melakukan pembalikan atau perubahan n2s. Dia mengambil 16 bit memori, dan meletakannya ke dalam 2 byte. Jadi sebenarnya dia menaruh 2 payload yang diminta oleh user, yang sebenarnya memiliki panjang yang sama. Kemudian dia mengkopi byte payload dari pl (data yang akan disediakan untuk user), ke alokasi array bp yang baru. Setelah ini, dia mengirimkan semuanya kembali ke user. Jadi dimana bug nya?

Pengguna sebenarnya mengendalikan payload dan pl

Bagaimana jika user sebenarnya tidak mensupai payload, dan di sisi user "mengaku" dia sudah mensuplai payload? Bagaimana jika pl hanya bernilai 1 byte? Kemudian proses pembacaan dari memcpy , akan dilakukan dengan prosedur "saya akan membaca semua memory yang dekat dengan record SSLv3 dan dalam proses (OpenSSL) yang sama"?

Dan kelihatannya, ada banyak hal di sana (memory yang dekat dengan record SSLv3).

Ada dua cara bagaimana memori dapat dialokasikan di Linux, menggunakan sbrk, dan menggunakan mmap. Jika memory dialokasikan oleh sbrk, maka ia akan menggunakan aturan heap-grows-up yang lama (kuno) dan membatasi apa yang bisa ditemukan user (critical data) dengan metode ini, meskipun dengan request berulang kali secara simultan, kita akan tetap bisa menemukan critical data pada metode yang dilakukan sbrk.

Sebenarnya alokasi bp tidak menjadi masalah utama. Malah alokasi pl yang menjadikan masalah besar (sangat besar). Dia hampir pasti akan dialokasikan oleh sbrk, karena batasan yang dimiliki oleh mmap di dalam malloc. Bagaimanapun juga, critical data seperti password, pin, email, history, dan lain lain dapat dijangkau melalui pl. Request berulang kali secara simultan juga bisa menguak bagian critical ini.

Lalu, apa artinya? ALOKASI PATTERNS DARI pl AKAN MEDIKTEKAN KAMU APA YANG BISA KAMU BACA!

Written by: Omega Hanggara

1 comment:

 
Back to top!