Posted by kecoak on Sep 8, 2009

CVE-2009-2698

It’s been sometimes ago, sejak terakhir kalinya isi blog ini bernuansa informasi teknikal, selebihnya bersifat informasi seperti berita dsb. Bukan sesuatu yang buruk kok, tapi seperti biasa, alasan klasik, real world, school, work, family, well…you know. Semakin sulit untuk tetap berkontribusi pada komunitas seiring pertambahan usia, sementara harapan untuk anggota baru yang lebih muda cukup miris, mereka lebih senang bertanya “bagaimana caranya untuk join?!” sementara telah tertulis jelas pada content blog procedure praktis untuk bisa join. I mean, c’mon guys, how can you write ’bout something while you can’t read properly?

Well, klo kata anak muda jaman sekarang ini adalah ‘curcol’, so…enough. Let’s talk something fishy.

Julien dan Taviso sepertinya akhir-akhir ini melakukan auditing terhadap kode-kode pada kernel linux khususnya yang berhubungan dengan implementasi networking. Sebelumnya kita telah melihat bug CVE-2009-2692, kali ini kita akan melihat lebih dalam bug CVE-2009-2698. Penjelasan singkat namun informatif dapat dilihat pada blognya Julien Tinnes.

Julien dan Taviso menemukan bahwa linux kernel < 2.6.19 memiliki masalah NULL pointer dereference pada implementasi protocol UDP, sebetulnya bug ini tidaklah terlalu bermasalah karena telah di patch oleh Herbert Xu pada bulan oktober 2006. Namun jika kalian menemukan server dengan spesifikasi kernel < 2.6.19 maka bug ini layak untuk dicoba ;).

Permasalahan terdapat pada routine / fungsi udp_sendmsg, lebih spesifik lagi pada implementasi suatu variable pointer yang disebut rt. rt merupakan variable pointer yang memiliki tipe data struktur rtable (routing table). Sebagai informasi, routine udp_sendmsg merupakan implementasi pengiriman paket udp melalui socket pada linux. Variable pointer rt didefinisikan sebagai NULL pada bagian awal routine udp_sendmsg, pada implementasi routine udp_sendmsg tersebut ditemukan cara agar variable rt tetap bernilai NULL saat memanggil fungsi ip_append_data dan ini disebabkan oleh bagian kode berikut ini:

if (up->pending) {
    /*
     * There are pending frames.
     * The socket lock must be held while it's corked.
     */
     lock_sock(sk);
     if (likely(up->pending)) {
        if (unlikely(up->pending != AF_INET)) {
            release_sock(sk);
            return -EINVAL;
        }
        goto do_append_data;
     }
     release_sock(sk);
}

Kode diatas seharusnya dieksekusi ketika terdapat pending frame yang akan di transmit, dan pada routine ip_append_data tidak terdapat pengecekan apakah variable pointer rt bernilai NULL karena secara logic kode berikut ini akan dijalankan:

794        if (skb_queue_empty(&sk->sk_write_queue)) {
795                /*
796                 * setup for corking.
797                 */
798                opt = ipc->opt;
...
...
       } else {
820                rt = inet->cork.rt;
821                if (inet->cork.flags & IPCORK_OPT)
822                        opt = inet->cork.opt;
823
824                transhdrlen = 0;
825                exthdrlen = 0;
826                mtu = inet->cork.fragsize;
827        }

Jadi jika alur program datang ke routine ip_append_data ketika variable rt bernilai NULL maka telah diasumsikan bahwa sebelumnya telah terdapat corking pada socket sehingga logic “else” yang akan dijalankan. Kita bisa lihat pada logic “else” variable pointer rt akan di-berikan inisialisasi dengan nilai dari variable inet->cork.rt, penjelasan singkat telah diberikan oleh Herber Xu walaupun pada saat patch kemungkinan dia tidak menyadari masalah security di bagian kode tersebut. Herber Xu lebih melihat dari sisi developer dengan memberikan statement:

UDP tracks corking status through the pending variable.  The
IP layer also tracks it through the socket write queue.  It
is possible for the two to get out of sync when MSG_PROBE is
used.

This patch changes UDP to check the write queue to ensure
that the two stay in sync.

Julien dan Taviso menemukan cara agar routine ip_append_data diatas (kernel < 2.6.19) dapat dieksekusi dengan variable pointer tetap bernilai NULL dan masuk pada logic “if”, dimana pada logic tersebut terdapat beberapa kode yang melakukan pointer dereference terhadap variable rt. Misalnya pada bagian berikut:

...
 809                dst_hold(&rt->u.dst);
 810                inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
 811                inet->cork.rt = rt;
...

Dan pada blog Julien diberikan cara untuk men-trigger bug tersebut:

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>

int main(int argc, char **argv)
{
int fd = socket(PF_INET, SOCK_DGRAM, 0);
char buf[1024] = {0};
struct sockaddr to = {
.sa_family = AF_UNSPEC,
.sa_data = "TavisIsAwesome",
};

sendto(fd, buf, 1024, MSG_PROXY | MSG_MORE, &to, sizeof(to));
sendto(fd, buf, 1024, 0, &to, sizeof(to));

return 0;
}

Masalah NULL ptr dereference kali ini berbeda dengan bug sebelumnya dimana kernel dapat langsung dibawa ke lokasi page 0 untuk mengesekusi kode-kode yang ditentukan oleh user. Bug NULL ptr deference memiliki beberapa sub-class, diantaranya tipe dereference (data read, data write, code execution) dan lokasi pointer ketika di-dereference (memory under direct userland control, kernel memory under indirect userland control, kernel memory under kernel control). Seperti yang dijelaskan oleh PaX team pada forum grsecurity, bug sock_sendpage merupakan “code execution” dan “memory under direct userland”. Sedangkan bug yang saat ini kita bahas masuk pada class “data read” dan “memory under direct userland control”.

Ada 3 buah public exploit yang beredar ketika bug tersebut dirilis, yang pertama dari p0c73n1, yang kedua oleh spender dan terakhir oleh [email protected]. Implementasi exploit dari p0c73n1 cukup sederhana, dia melemparkan lokasi dari kernel_code pada lokasi page 0 dan mentrigger bug NULL ptr, kemudian berharap bahwa eksekusi kernel akan membaca / mengeksekusi lokasi page 0. Spender melakukan analisis  yang lebih mendalam, dan exploit andi merupakan penyederhanaan dari exploit spender dengan memfokuskan pada proses exploitasi, namun intinya satu yaitu memanfaatkan (*output)(struct sk_buff*) dari dst_pointer yang digunakan oleh rtable sebagai callback pada exploit, posisi tersebut terdapat pada offset +0x74 dari rtable.

Variable pointer rt memiliki tipe data rtable, yang definisinya sebagai berikut:

...
  50struct rtable
  51{
  52        union
  53        {
  54                struct dst_entry        dst;
  55                struct rtable           *rt_next;
  56        } u;
  57
  58        struct in_device        *idev;
  59
  60        unsigned                rt_flags;
  61        __u16                   rt_type;
  62        __u16                   rt_multipath_alg;
  63
  64        __u32                   rt_dst; /* Path destination     */
  65        __u32                   rt_src; /* Path source          */
  66        int                     rt_iif;
  67
  68        /* Info on neighbour */
  69        __u32                   rt_gateway;
  70
  71        /* Cache lookup keys */
  72        struct flowi            fl;
  73
  74        /* Miscellaneous cached information */
  75        __u32                   rt_spec_dst; /* RFC1122 specific destination */
  76        struct inet_peer        *peer; /* long-living peer info */
  77};
  78
...

Struktur rtable mendefinisikan union yang salah satu anggotanya bertipe struct dst_entry. Berikut ini definisi struktur dst_entry:

...
  38struct dst_entry
  39{
  40        struct dst_entry        *next;
  41        atomic_t                __refcnt;       /* client references    */
  42        int                     __use;
  43        struct dst_entry        *child;
  44        struct net_device       *dev;
  45        short                   error;
  46        short                   obsolete;
  47        int                     flags;
  48#define DST_HOST                1
  49#define DST_NOXFRM              2
  50#define DST_NOPOLICY            4
  51#define DST_NOHASH              8
  52#define DST_BALANCED            0x10
  53        unsigned long           lastuse;
  54        unsigned long           expires;
  55
  56        unsigned short          header_len;     /* more space at head required */
  57        unsigned short          trailer_len;    /* space to reserve at tail */
  58
  59        u32                     metrics[RTAX_MAX];
  60        struct dst_entry        *path;
  61
  62        unsigned long           rate_last;      /* rate limiting for ICMP */
  63        unsigned long           rate_tokens;
  64
  65        struct neighbour        *neighbour;
  66        struct hh_cache         *hh;
  67        struct xfrm_state       *xfrm;
  68
  69        int                     (*input)(struct sk_buff*);
  70        int                     (*output)(struct sk_buff*);
  71
  72#ifdef CONFIG_NET_CLS_ROUTE
  73        __u32                   tclassid;
  74#endif
  75
  76        struct  dst_ops         *ops;
  77        struct rcu_head         rcu_head;
  78
  79        char                    info[0];
  80};
...

Pada baris ke-70 terdapat suatu variable pointer dengan tipe data struct sk_buff yang nantinya akan digunakan sebagai callback oleh fungsi NF_HOOK. Sehingga oleh exploit (setelah di-mapping pada page 0) pada posisi tersebut (NULL+0x74) akan dimasukan lokasi dari fungsi own_the_kernel (fungsi ini pada tiap exploit berbeda-beda namanya, namun intinya adalah memerintahkan kernel untuk memberikan uid=0 / root pada proses exploit). Ketika NF_HOOK mengakses lokasi callback tersebut maka secara tidak langsung akan dibawa untuk mengakses page 0 offset +0x74, dari sinilah kernel akan dituntun untuk mengeksekusi fungsi own_the_kernel dan memberikan rootshell pada kita.

Penjelasan mengenai fungsi NF_HOOK dapat dilihat disini. Secara singkat kita bisa mengatakan bahwa fungsi NF_HOOK akan selalu di eksekusi ketika kernel linux hendak mengirimkan data melalui jaringan. Linux mendukung beragam NIC (Network Interface Card) melalui bergam device drivers, dan NF_HOOK merupakan salah satu fungsi standard high level routing yang digunakan oleh linux dalam hal memanage networking.

Julien dan Taviso mengakhiri advisories mereka dengan menambahkan patch berupa proses checking terhadap variable pointer rt dalam fungsi ip_append_data, hal ini untuk mencegah NULL ptr deref dari variable rt dalam fungsi tersebut dengan anggapan bahwa fungsi ip_append_data memang tidak akan pernah mengakses variable rt dalam kondisi NULL.

Post a Comment

One Response to “CVE-2009-2698”

  1. sagan says:

    maaf bos masih belum mnegerti jalan kerjanya padahal dh tak coba???

Trackbacks/Pingbacks

  1. Hack Attempt ?!? | Freedom Renegade - [...] badly… go get one here. As like one of famous Indonesian security blog said recently, “c’mon guys, how can…
  2. kopandakan.com » CVE-2009-2698 - [...] sourceprint? 511.if (up->pending) { 512. /* 513. * There are pending frames. 514. * The socket lock must [...]

Leave a Reply

Your email address will not be published. Required fields are marked *