==Phrack Inc.==
Volume 0x0b, Issue 0x3f, Phile #0x09 of 0x14
|=-------=[ Embedded ELF Debugging : the middle head of Cerberus ]=------=|
|=-----------------------------------------------------------------------=|
|=-------------=[ The ELF shell crew <elfsh@devhell.org> ]=--------------=|
|=-----------------------------------------------------------------------=|
I. Hardened software debugging introduction
a. Previous work & limits
b. Beyond PaX and ptrace()
c. Interface improvements
II. The embedded debugging playground
a. In-process injection
b. Altenate ondisk and memory ELF scripting (feat. linkmap)
c. Real debugging : stepping and backtracing
d. Dynamic analyzers generation
III. Better multiarchitecture ELF redirections
a. CFLOW: PaX-safe static functions redirection
b. ALTPLT technique revisited
c. ALTGOT technique : the RISC complement
d. EXTPLT technique : unknown function postlinking
e. IA32, SPARC32/64, ALPHA64, MIPS32 compliant algorithms
V. Constrained Debugging
a. ET_REL relocation in memory
b. ET_REL injection for Hardened Gentoo (ET_DYN + pie + ssp)
c. Extending static executables
VI. Past and present
VII. Greetings
VIII. References
[NOTE of ELFsh crew]
[This article is in beta version. The final release is for this week]
-------[ I. Hardened software debugging introduction
In the past, binary manipulation work has focussed on virii
writing, backdoors deployment, or creation of tiny or obfuscated
executables. Besides the tools from the GNU project such as the
GNU debugger [1] (which focuss more on portability than
functionalities), no major binary manipulation framework does
exist. For the last ten years, the ELF format has been a succes
and most UNIX Operating System and distributions rely on it.
However, the existing tools do not take advantage of the format
and most of the reverse engineering related software are either
very architecture specific, or simply do not care about binary
internals for extracting and redirecting information.
Since our first published work on the ELF shell, we improved so
much the new framework that it is now time to publish a second
deep article on advances on ELF techniques. We will explain in
great details the 8 new reverse engineering functionalities that
cut with the existing methodology of binary manipulation based
reverse engineering. Those techniques allow for a new type of
approach on debugging and extending closed source software. We
worked on many architectures (x86, alpha, sparc, mips) and
focussed on hardened environment where binaries are linked with
security protections enabled (such as hardened gentoo binaries)
in PaX [2] protected machines.
----[ A. Previous work & limits
In the first part of the Cerberus articles serie, we introduced
a new residency technique called ET_REL injection. It consisted
in compiling C code into relocatable (.o) files and inject them
into existing closed source binary programs. This technique was
proposed for INTEL and SPARC architectures on the ELF32 format.
We improved this technique so that both 32 and 64 bits binaries
are supported so we added alpha64 and sparc64 support. We also
worked on the MIPS r5000 architecture and now provide a nearly
complete environment for it as well. We now also allow for ET_REL
injection into ET_DYN objects (shared libraries) so that our
technique is compatible with fully randomized environments such
as provided by Hardened Gentoo with the PaX protection enabled
on the Linux Operating System. We also worked on other OS such as
BSD based ones, Solaris, and HP-UX and we claim portability on
those as well.
A major innovation of our debugging framework is the absence of
ptrace. We do not use kernel residency like in [8] so that even
unprivilegied users can use this and it is not Operating System
dependent.
Existing debuggers use to rely on the ptrace system call so that
the debugger process can attach the debuggee program and enable
various internal process manipulations such as dumping memory,
putting breakpoing, doing step by step analysis, and so
on. We propose the same features without using the system call
and we added new major contributions to that common interface.
The reasons why we do not use ptrace are multiple and simple.
First of all, a lot of hardened or embedded systems do not
implement it, or just disable it. That's the case for grsecurity
based systems or phone systems whoose Operating System is ELF
based but without a ptrace interface. The second major reason of
not using ptrace is the performance penalties of such a debugging
system. We do not suffer from performance penalties since the
debugger resides in the same process. We provide a full userland
technique that does not have to access the kernel memory, thus
it is useful in all stage of a penetration test when debugging
sensible software on hardened environment is needed and no system
update is possible. We allow for plain C code injection inside
new binary files (in the static perspective) and processes (in
the runtime mode) using a unified software. When possible, we
only use ELF techniques that reduce forensics evidence on the
disk and only works in memory.
----[ B. Beyond PaX and ptrace
Another key point in our framework are the greatly improved
redirection techniques. We can redirect almost all control flow,
wheter or not the function code is placed inside the binary
itself (CFLOW technique) or in a library on which the binary
depends. Our previous work presented new hijacking techniques
such that ALTPLT. We improved this techniques and passed though
many rewrites and now allow a complete architecture independant
implementation. We completed ALTPLT by a new technique called
ALTGOT so that hijacking function and calling back the original
one is possible on Alpha and Mips RISC machines as well. We also
created a new technique called EXTPLT which allow for unknown
function (for which no dynamic linking information is available
at all) using a new postlinking algorithm compatible with ET_EXEC
and ET_DYN objets.
----[ C. Interface improvements
Our Embedded ELF debugger implementation is a prototype.
Understand that it is really usable but we are still in the
development process. All the code presented here is known to work,
but you may encounted a problem. In that case drop us an email so
that we can go further and you can get a patch. The only
assumption that we made is the ability to read the debuggee
program. In all case, you can also debug in memory the unreadable
binaries on disk. However the debugger is enhanced when binary
files are readable. Because the debugger run in the same address
space, you can still read memory [3] [4] and restore the binary
program.
Then you can start a debugging session using the reconstructed
binary. We do not provide binary reconstruction facilities but
other related work suggest that it is really usable.
The central communication language in the Embedded ELF Debugger
(e2dbg) framework is our scripting facilities. We augmented the
scripting capabilities with loop, transparent support for lazy
typed variables (like in bash or perl). The source command and
user defined macros are also supported. We also developed a
peer2peer stack so called Distributed Updates Management Protocol
- DUMP - that allow for linking multiple debugger instances using
the network. For completeness, we also now support multiuser
(parallel or shared) sessions, and workspaces support. We will go
through the use of such interface in the first part of the paper,
besides the use of all the new techniques. In the second part, we
give technical details about the implementation of such features
on multiple architectures. The last part is dedicated to the most
recent and advanced techniques we developed in the last weeks for
constrained debugging in protected binaries.
-------[ II. The embedded debugging playground
---[ A. In-process injection
We have different techniques for injecting the debugger
inside the debuggee process. Thus it will share the address
space and the debugger will be able to read its own data
and code for getting (and changing) information in the
debuggee process.
Because the ELF shell is composed of 40000 lines of code,
we did not want to recode everything for allowing process
modification. We used some trick that allow us to select
wether the modifications are done in memory or on disk. The
trick consists in 10 lines of code, which are :
(libelfsh/section.c)
/* Fill an anonymous (unknown content) section */
void *elfsh_get_anonymous_section(elfshobj_t *file,
elfshsect_t *sect)
{
ELFSH_PROFILE_IN(__FILE__, __FUNCTION__, __LINE__);
/* Bad parameter checking */
if (file == NULL || sect == NULL)
ELFSH_PROFILE_ERR(__FILE__, __FUNCTION__, __LINE__,
"Invalid NULL parameter", NULL);
/* If the section is already loaded */
if (sect->data != NULL)
ELFSH_PROFILE_ROUT(__FILE__, __FUNCTION__, __LINE__,
(elfsh_get_raw(sect)));
sect->data = elfsh_load_section(file, sect->shdr);
ELFSH_PROFILE_ROUT(__FILE__, __FUNCTION__, __LINE__,
(elfsh_get_raw(sect)));
}
What is the technique about ? It is quite simple : if the debugger
internal flag is set to static mode (ondisk modification), then we
return the pointer on the data cache for the section we want to
modify. However if we are in dynamic mode (process modification),
then we just return the address of that section. The debugger runs
in the same process and thus will think that the returned address
is a readable (or writable) buffer. We can reuse all the ELF shell
API by just taking care of using the elfsh_get_raw() function when
accessing the ->data pointer. The process/ondisk selection is then
transparent for all the debugger/elfsh code.
<2 outputs : by ld_preload, by static injection>
---[ B. Altenate ondisk and memory ELF scripting (feat. linkmap)
Let's see how to use the embedded debugger and its mode
command that does the memory/disk selection. We there
print the Global Offset Table (.got). First the memory
got is displayed, then we get back in static mode and
the ondisk GOT is displayed:
========= BEGIN EXAMPLE 1 =========
(e2dbg-0.65) list
.::. Working files .::.
[001] Sun Jul 31 19:23:33 2005 D ID: 9 /lib/libncurses.so.5
[002] Sun Jul 31 19:23:33 2005 D ID: 8 /lib/libdl.so.2
[003] Sun Jul 31 19:23:33 2005 D ID: 7 /lib/libtermcap.so.2
[004] Sun Jul 31 19:23:33 2005 D ID: 6 /lib/libreadline.so.5
[005] Sun Jul 31 19:23:33 2005 D ID: 5 /lib/libelfsh.so
[006] Sun Jul 31 19:23:33 2005 D ID: 4 /lib/ld-linux.so.2
[007] Sun Jul 31 19:23:33 2005 D ID: 3 ./ibc.so.6
[008] Sun Jul 31 19:23:33 2005 D ID: 2 /lib/tls/libc.so.6
[009] Sun Jul 31 19:23:33 2005 *D ID: 1 ./a.out_e2dbg
.::. ELFsh modules .::.
[*] No loaded module
(e2dbg-0.65) mode
[*] e2dbg is in DYNAMIC MODE
(e2dbg-0.65) got
[Global Offset Table .::. GOT : .got ]
[Object ./a.out_e2dbg]
0x080498E4: [0] 0x00000000 <?>
[Global Offset Table .::. GOT : .got.plt ]
[Object ./a.out_e2dbg]
0x080498E8: [0] 0x0804981C <_DYNAMIC@a.out_e2dbg>
0x080498EC: [1] 0x00000000 <?>
0x080498F0: [2] 0x00000000 <?>
0x080498F4: [3] 0x0804839E <fflush@a.out_e2dbg>
0x080498F8: [4] 0x080483AE <puts@a.out_e2dbg>
0x080498FC: [5] 0x080483BE <malloc@a.out_e2dbg>
0x08049900: [6] 0x080483CE <strlen@a.out_e2dbg>
0x08049904: [7] 0x080483DE <__libc_start_main@a.out_e2dbg>
0x08049908: [8] 0x080483EE <printf@a.out_e2dbg>
0x0804990C: [9] 0x080483FE <free@a.out_e2dbg>
0x08049910: [10] 0x0804840E <read@a.out_e2dbg>
[Global Offset Table .::. GOT : .elfsh.altgot ]
[Object ./a.out_e2dbg]
0x08049928: [0] 0x0804981C <_DYNAMIC@a.out_e2dbg>
0x0804992C: [1] 0xB7F4A4E8 <_r_debug@ld-linux.so.2 + 24>
0x08049930: [2] 0xB7F3EEC0 <_dl_rtld_di_serinfo@ld-linux.so.2 + 477>
0x08049934: [3] 0x0804839E <fflush@a.out_e2dbg>
0x08049938: [4] 0x080483AE <puts@a.out_e2dbg>
0x0804993C: [5] 0xB7E515F0 <__libc_malloc@libc.so.6>
0x08049940: [6] 0x080483CE <strlen@a.out_e2dbg>
0x08049944: [7] 0xB7E01E50 <__libc_start_main@libc.so.6>
0x08049948: [8] 0x080483EE <printf@a.out_e2dbg>
0x0804994C: [9] 0x080483FE <free@a.out_e2dbg>
0x08049950: [10] 0x0804840E <read@a.out_e2dbg>
0x08049954: [11] 0xB7DAFFF6 <e2dbg_run@ibc.so.6>
(e2dbg-0.65) mode static
[*] e2dbg is now in STATIC mode
(e2dbg-0.65) got
[Global Offset Table .::. GOT : .got ]
[Object ./a.out_e2dbg]
0x080498E4: [0] 0x00000000 <?>
[Global Offset Table .::. GOT : .got.plt ]
[Object ./a.out_e2dbg]
0x080498E8: [0] 0x0804981C <_DYNAMIC>
0x080498EC: [1] 0x00000000 <?>
0x080498F0: [2] 0x00000000 <?>
0x080498F4: [3] 0x0804839E <fflush>
0x080498F8: [4] 0x080483AE <puts>
0x080498FC: [5] 0x080483BE <malloc>
0x08049900: [6] 0x080483CE <strlen>
0x08049904: [7] 0x080483DE <__libc_start_main>
0x08049908: [8] 0x080483EE <printf>
0x0804990C: [9] 0x080483FE <free>
0x08049910: [10] 0x0804840E <read>
[Global Offset Table .::. GOT : .elfsh.altgot ]
[Object ./a.out_e2dbg]
0x08049928: [0] 0x0804981C <_DYNAMIC>
0x0804992C: [1] 0x00000000 <?>
0x08049930: [2] 0x00000000 <?>
0x08049934: [3] 0x0804839E <fflush>
0x08049938: [4] 0x080483AE <puts>
0x0804993C: [5] 0x080483BE <malloc>
0x08049940: [6] 0x080483CE <strlen>
0x08049944: [7] 0x080483DE <__libc_start_main>
0x08049948: [8] 0x080483EE <printf>
0x0804994C: [9] 0x080483FE <free>
0x08049950: [10] 0x0804840E <read>
0x08049954: [11] 0x0804614A <e2dbg_run + 6>
========= END EXAMPLE 1 =========
There are many things to notice in this dump. First
you can verify that it actually does what it is
supposed to by looking the first GOT entries which
are reserved for the linkmap and the rtld resolve
function. Those entries are filled at runtime, so
the static GOT version contains NULL pointers for
them. However the GOT which stands in memory has
them filled.
Also, the new version of the GNU linker does insert
multiple GOT sections inside ELF binaries. Section
.got handles the pointer for external variables, while
.got.plt handles the external function pointers. In
earlier versions of LD, those 2 sections were merged.
Finally, you can see in last the .elfsh.altgot section.
That is part of the ALTGOT technique and it will be
explained as a standalone algorithm in the next parts
of this paper. The ALTGOT technique allow for a size
extension of the Global Offset Table. It allows different
things depending on the architecture. On x86, ALTGOT is
only used when EXTPLT is used, so that we can add extra
function to the host file. On MIPS and ALPHA, ALTGOT
allows to redirect an external function without losing
the real function address. We will develop both of these
techniques in the next parts.
---[ C. Real debugging : backtrace, breakpoints, and step
When performing debugging using a debugger embedded in the
debuggee process, we are not using ptrace so we cannot
modify so easily the process address space. That's why
we have to do small static changes : we add the debugger
as a DT_NEEDED dependancy. The debugger will overload some
signal handlers (SIGTRAP, SIGINT, SIGSEGV ..) so that it
can takes control on those events. We can redirect functions
as well using either the CFLOW or ALTPLT technique (ondisk
modification) so that we takes control at the desired moment.
Obviously we can also set breakpoints in runtime but that
need to mprotect the code zone if it was not writable (which
is incompatible with the mprotect option of PaX). Fortunately
we assume for now that we have read access to the debuggee
program, which means that we can copy the file and disable
that option.
========= BEGIN EXAMPLE 2 =========
elfsh@WTH $ cat inject_e2dbg.esh
#!../../vm/elfsh
load a.out
set 1.dynamic[08].val 0x2
set 1.dynamic[08].tag DT_NEEDED
redir main e2dbg_run
save a.out_e2dbg
========= END EXAMPLE 2 =========
Let's see the modified binary .dynamic section, where the
extra DT_NEEDED entries were added using the DT_DEBUG
technique that we published 2 years ago :
========= BEGIN EXAMPLE 3 =========
elfsh@WTH $ ../../vm/elfsh -f ./a.out -d DT_NEEDED
[*] Object ./a.out has been loaded (O_RDONLY)
[SHT_DYNAMIC]
[Object ./a.out]
[00] Name of needed library => libc.so.6 {DT_NEEDED}
[*] Object ./a.out unloaded
elfsh@WTH $ ../../vm/elfsh -f ./a.out_e2dbg -d DT_NEEDED
[*] Object ./a.out_e2dbg has been loaded (O_RDONLY)
[SHT_DYNAMIC]
[Object ./a.out_e2dbg]
[00] Name of needed library => libc.so.6 {DT_NEEDED}
[08] Name of needed library => ibc.so.6 {DT_NEEDED}
[*] Object ./a.out_e2dbg unloaded
========= END EXAMPLE 3 =========
Let's see how we redirected the main function to
the hook_main function. You can notice the
overwritten bytes between the 2 jmp of the
hook_main function. This technique is available
for x86 and MIPS architecture :
========= BEGIN EXAMPLE 4 =========
elfsh@WTH $ ../../vm/elfsh -f ./a.out_e2dbg -D main%40
[*] Object ./a.out_e2dbg has been loaded (O_RDONLY)
08045134 [foff: 308] hook_main + 0 jmp <e2dbg_run>
08045139 [foff: 313] hook_main + 5 push %ebp
0804513A [foff: 314] hook_main + 6 mov %esp,%ebp
0804513C [foff: 316] hook_main + 8 push %esi
0804513D [foff: 317] hook_main + 9 push %ebx
0804513E [foff: 318] hook_main + 10 jmp <main + 5>
08045139 [foff: 313] old_main + 0 push %ebp
0804513A [foff: 314] old_main + 1 mov %esp,%ebp
0804513C [foff: 316] old_main + 3 push %esi
0804513D [foff: 317] old_main + 4 push %ebx
0804513E [foff: 318] old_main + 5 jmp <main + 5>
08048530 [foff: 13616] main + 0 jmp <hook_main>
08048535 [foff: 13621] main + 5 sub $2010,%esp
0804853B [foff: 13627] main + 11 mov 8(%ebp),%ebx
0804853E [foff: 13630] main + 14 mov C(%ebp),%esi
08048541 [foff: 13633] main + 17 and $FFFFFFF0,%esp
08048544 [foff: 13636] main + 20 sub $10,%esp
08048547 [foff: 13639] main + 23 mov %ebx,4(%esp,1)
0804854B [foff: 13643] main + 27 mov $<_IO_stdin_used + 43>,(%esp,1)
08048552 [foff: 13650] main + 34 call <printf>
08048557 [foff: 13655] main + 39 mov (%esi),%eax
[*] No binary pattern was specified
[*] Object ./a.out_e2dbg unloaded
========= END EXAMPLE 4 =========
Let's now execute the debuggee program, in which the
debugger was injected.
========= BEGIN EXAMPLE 5 =========
elfsh@WTH $ ./a.out_e2dbg
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 17:56:52 2005 - New object ./a.out_e2dbg loaded
[*] Sun Jul 31 17:56:52 2005 - New object /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 17:56:53 2005 - New object ./ibc.so.6 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/ld-linux.so.2 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libelfsh/libelfsh.so loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libreadline.so.5 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libtermcap.so.2 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libdl.so.2 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libncurses.so.5 loaded
(e2dbg-0.65) b puts
[*] Breakpoint added at <puts@a.out_e2dbg> (0x080483A8)
(e2dbg-0.65) continue
[..: Embedded ELF Debugger returns to the grave :...]
[e2dbg_run] returning to 0x08045139
[host] main argc 1
[host] argv[0] is : ./a.out_e2dbg
First_printf test
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 17:57:03 2005 - New object /lib/tls/libc.so.6 loaded
(e2dbg-0.65) bt
.:: Backtrace ::.
[00] 0xB7DC1EC5 <vm_bt@ibc.so.6 + 208>
[01] 0xB7DC207F <cmd_bt@ibc.so.6 + 152>
[02] 0xB7DBC88C <vm_execmd@ibc.so.6 + 174>
[03] 0xB7DAB4DE <vm_loop@ibc.so.6 + 578>
[04] 0xB7DAB943 <vm_run@ibc.so.6 + 271>
[05] 0xB7DA5FF0 <e2dbg_entry@ibc.so.6 + 110>
[06] 0xB7DA68D6 <e2dbg_genericbp_ia32@ibc.so.6 + 183>
[07] 0xFFFFE440 <_r_debug@ld-linux.so.2 + 1208737648>
[08] 0xB7DF7F3B <__libc_start_main@libc.so.6 + 235>
[09] 0x08048441 <_start@a.out_e2dbg + 33>
(e2dbg-0.65) b
.:: Breakpoints ::.
[00] 0x080483A8 <puts@a.out_e2dbg>
(e2dbg-0.65) delete 0x080483A8
[*] Breakpoint at 080483A8 <puts@a.out_e2dbg> removed
(e2dbg-0.65) b
.:: Breakpoints ::.
[*] No breakpoints
(e2dbg-0.65) b printf
[*] Breakpoint added at <printf@a.out_e2dbg> (0x080483E8)
(e2dbg-0.65) dumpregs
.:: Registers ::.
[EAX] 00000000 (0000000000) <unknown>
[EBX] 08203F48 (0136331080) <.elfsh.relplt@a.out_e2dbg + 1811272>
[ECX] 00000000 (0000000000) <unknown>
[EDX] B7F0C7C0 (3086010304) <__guard@libc.so.6 + 1656>
[ESI] BFE3B7C4 (3219371972) <_r_debug@ld-linux.so.2 + 133149428>
[EDI] BFE3B750 (3219371856) <_r_debug@ld-linux.so.2 + 133149312>
[ESP] BFE3970C (3219363596) <_r_debug@ld-linux.so.2 + 133141052>
[EBP] BFE3B738 (3219371832) <_r_debug@ld-linux.so.2 + 133149288>
[EIP] 080483A9 (0134513577) <puts@a.out_e2dbg>
(e2dbg-0.65) stack 20
.:: Stack ::.
0xBFE37200 0x00000000 <(null)>
0xBFE37204 0xB7DC2091 <vm_dumpstack@ibc.so.6>
0xBFE37208 0xB7DDF5F0 <_GLOBAL_OFFSET_TABLE_@ibc.so.6>
0xBFE3720C 0xBFE3723C <_r_debug@ld-linux.so.2 + 133131628>
0xBFE37210 0xB7DC22E7 <cmd_stack@ibc.so.6 + 298>
0xBFE37214 0x00000014 <_r_debug@ld-linux.so.2 + 1208744772>
0xBFE37218 0xB7DDDD90 <__FUNCTION__.5@ibc.so.6 + 49>
0xBFE3721C 0xBFE37230 <_r_debug@ld-linux.so.2 + 133131616>
0xBFE37220 0xB7DB9DF9 <vm_implicit@ibc.so.6 + 304>
0xBFE37224 0xB7DE1A7C <world@ibc.so.6 + 92>
0xBFE37228 0xB7DA8176 <do_resolve@ibc.so.6>
0xBFE3722C 0x080530B8 <.elfsh.relplt@a.out_e2dbg + 38072>
0xBFE37230 0x00000014 <_r_debug@ld-linux.so.2 + 1208744772>
0xBFE37234 0x08264FF6 <.elfsh.relplt@a.out_e2dbg + 2208758>
0xBFE37238 0xB7DDF5F0 <_GLOBAL_OFFSET_TABLE_@ibc.so.6>
0xBFE3723C 0xBFE3726C <_r_debug@ld-linux.so.2 + 133131676>
0xBFE37240 0xB7DBC88C <vm_execmd@ibc.so.6 + 174>
0xBFE37244 0x0804F208 <.elfsh.relplt@a.out_e2dbg + 22024>
0xBFE37248 0x00000000 <(null)>
0xBFE3724C 0x00000000 <(null)>
(e2dbg-0.65) continue
[..: Embedded ELF Debugger returns to the grave :...]
First_puts
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 18:00:47 2005 - /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 18:00:47 2005 - /usr/lib/gconv/ISO8859-1.so loaded
(e2dbg-0.65) dumpregs
.:: Registers ::.
[EAX] 0000000B (0000000011) <_r_debug@ld-linux.so.2 + 1208744763>
[EBX] 08203F48 (0136331080) <.elfsh.relplt@a.out_e2dbg + 1811272>
[ECX] 0000000B (0000000011) <_r_debug@ld-linux.so.2 + 1208744763>
[EDX] B7F0C7C0 (3086010304) <__guard@libc.so.6 + 1656>
[ESI] BFE3B7C4 (3219371972) <_r_debug@ld-linux.so.2 + 133149428>
[EDI] BFE3B750 (3219371856) <_r_debug@ld-linux.so.2 + 133149312>
[ESP] BFE3970C (3219363596) <_r_debug@ld-linux.so.2 + 133141052>
[EBP] BFE3B738 (3219371832) <_r_debug@ld-linux.so.2 + 133149288>
[EIP] 080483E9 (0134513641) <printf@a.out_e2dbg>
(e2dbg-0.65) linkmap
.::. Linkmap entries .::.
[01] addr : 0x00000000 dyn : 0x0804981C -
[02] addr : 0x00000000 dyn : 0xFFFFE590 -
[03] addr : 0xB7DE3000 dyn : 0xB7F0AD3C - /lib/tls/libc.so.6
[04] addr : 0xB7D95000 dyn : 0xB7DDF01C - ./ibc.so.6
[05] addr : 0xB7F29000 dyn : 0xB7F3FF14 - /lib/ld-linux.so.2
[06] addr : 0xB7D62000 dyn : 0xB7D93018 - /lib/libelfsh.so
[07] addr : 0xB7D35000 dyn : 0xB7D5D46C - /lib/libreadline.so.5
[08] addr : 0xB7D31000 dyn : 0xB7D34BB4 - /lib/libtermcap.so.2
[09] addr : 0xB7D2D000 dyn : 0xB7D2FEEC - /lib/libdl.so.2
[10] addr : 0xB7CEB000 dyn : 0xB7D2A1C0 - /lib/libncurses.so.5
[11] addr : 0xB6D84000 dyn : 0xB6D85F28 - /usr/lib/gconv/ISO8859-1.so
(e2dbg-0.65) exit
[*] Unloading object 1 (/usr/lib/gconv/ISO8859-1.so)
[*] Unloading object 2 (/lib/tls/libc.so.6)
[*] Unloading object 3 (/lib/tls/libc.so.6)
[*] Unloading object 4 (/lib/libncurses.so.5)
[*] Unloading object 5 (/lib/libdl.so.2)
[*] Unloading object 6 (/lib/libtermcap.so.2)
[*] Unloading object 7 (/lib/libreadline.so.5)
[*] Unloading object 8 (/home/elfsh/WTH/elfsh/libelfsh/libelfsh.so)
[*] Unloading object 9 (/lib/ld-linux.so.2)
[*] Unloading object 10 (./ibc.so.6)
[*] Unloading object 11 (/lib/tls/libc.so.6)
[*] Unloading object 12 (./a.out_e2dbg) *
.:: Bye -:: The Embedded ELF Debugger 0.65
========= END EXAMPLE 5 =========
As you see, the use of the debugger is quite similar to other
debugger. The difference is about the implementation technique
which allows for hardened and embedded systems debugging where
ptrace is not present or disabled.
We were told [9] that the sigaction system call enables the
possibility of doing step by step execution without using
ptrace. We did not have time to implement it but we will
provide a step-capable debugger in the very near future. Since
that call is not filtered by grsecurity and seems to be quite
portable on Linux, BSD, Solaris and HP-UX, it is definitely
worth mentioning.
---[ D. Dynamic analyzers generation
Obviously, tools like ltrace [7] can be now done in elfsh scripts
for multiple architectures since all the redirection stuff is
available.
We also think that the framework can be used in dynamic software
instrumentation. Since we support multiple architectures, we let
the door open to other development team to develop such modules
or extension inside the ELF shell framework.
We did not have time to include an example script for now that
can do this, but we will soon. The kind of interresting stuff
that could be done and improved using the framework would
take its inspiration in projects like fenris [6].
We do not deal with encryption for now, but some promising API
[5] could be implemented as well for multiple architectures very
easily.
-------[ III. Better multiarchitecture ELF redirections
In the first issue of the Cerberus ELF interface, we presented
a redirection technique that we called ALTPLT. This technique
is not enough since it allows only for PLT redirection.
Morever, we noticed a bug in the previously released
implementation of the ALTPLT technique : On the SPARC
architecture, when calling the original function, the
redirection was removed and the program continued to work as if
no hook was installed. This bug came from the fact that Solaris
does not use the r_offset field for computing its relocation
but get the file offset by multiplying the PLT entry size by the
pushed relocation offset on the stack at the moment of dynamic
resolution.
We found a solution for this problem. That solution consisted in
adding some architecture specific fixes at the beginning of the
ALTPLT section. However, such a fix is too much architecture
dependant and we started to think about an alternative technique
for implementing ALTPLT. As we had implemented the DT_DEBUG
technique by modifying some entries in the .dynamic sections, we
discovered that many other entries are erasable and allow for
a very strong and architecture independant technique for
redirecting access to various sections. More precisely, when
patching the DT_PLTREL entry, we are able to provide our own
pointer. DT_PLTREL is an architecture dependant entry and the
documentation about it is quite weak, not to say inexistant.
It actually points on the section of the executable beeing
runtime relocated (e.g. GOT on x86 or MIPS, PLT on sparc and
alpha). By changing this entry we are able to provide our own
PLT or GOT, which leads to possibly extending it.
Let's first have look at the CFLOW technique and then comes
back on the PLT related redirections using the DT_PLTREL
modification.
---[ A. CFLOW: PaX-safe static functions redirection
CFLOW is a simple but efficient technique for
function redirection. Let's see the host file
that we use for this test:
========= BEGIN EXAMPLE 6 =========
elfsh@WTH $ cat host.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int legit_func(char *str)
{
printf("legit func (%s) !\n", str);
return (0);
}
int main()
{
char *str;
char buff[BUFSIZ];
read(0, buff, BUFSIZ-1);
str = malloc(10);
if (str == NULL)
goto err;
strcpy(str, "test");
printf("First_printf %s\n", str);
fflush(stdout);
puts("First_puts");
printf("Second_printf %s\n", str);
free(str);
puts("Second_puts");
fflush(stdout);
legit_func("test");
return (0);
err:
printf("Malloc problem\n");
return (-1);
}
========= END EXAMPLE 6 =========
Let's look at the relocatable file that we are going to inject
in the above binary.
========= BEGIN EXAMPLE 7 =========
elfsh@WTH $ cat rel.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glvar_testreloc = 42;
int glvar_testreloc_bss;
char glvar_testreloc_bss2;
short glvar_testreloc_bss3;
int hook_func(char *str)
{
printf("HOOK FUNC %s !\n", str);
return (old_legit_func(str));
}
int puts_troj(char *str)
{
int local = 1;
char *str2;
str2 = malloc(10);
*str2 = 'Z';
*(str2 + 1) = 0x00;
glvar_testreloc_bss = 43;
glvar_testreloc_bss2 = 44;
glvar_testreloc_bss3 = 45;
printf("Trojan injected ET_REL takes control now "
"[%s:%s:%u:%u:%hhu:%hu:%u] \n",
str2, str,
glvar_testreloc,
glvar_testreloc_bss,
glvar_testreloc_bss2,
glvar_testreloc_bss3,
local);
free(str2);
putchar('e');
putchar('x');
putchar('t');
putchar('c');
putchar('a');
putchar('l');
putchar('l');
putchar('!');
putchar('\n');
old_puts(str);
write(1, "calling write\n", 14);
fflush(stdout);
return (0);
}
int func2()
{
return (42);
}
========= END EXAMPLE 7 =========
As you can see, the relocatable object use of unknown functions
like write and putchar. Those functions do not have an symbol, plt
entry, got entry, or even relocatable entry in the host file.
We can call it however using the EXTPLT technique that will be
described as a standalone technique in the next part of this paper.
For now let's focuss on the CFLOW technique that allow for redirection
of the legit_func by the hook_func. This function does not have a PLT
entry and we cannot use simple PLT infection for this.
We developped a technique that is PaX safe for ondisk redirection of
this kind of function. It consists of putting the good old jmp
instruction at the beginning of the legit_func and redirect the flow
on our own code. ELFsh will take care of executing the overwritten
bytes somewhere else and gives back control to the redirected
function, just after the jmp hook.
Let's see more:
========= BEGIN EXAMPLE 8 =========
elfsh@WTH $ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, dynamically linked, \
not stripped
elfsh@WTH $ cat relinject.esh
#!../../../vm/elfsh
load a.out
load rel.o
reladd 1 2
redir puts puts_troj
redir legit_func hook_func
save fake_aout
quit
========= END EXAMPLE 8 =========
The result of the ORIGINAL binary is as follow:
========= BEGIN EXAMPLE 9 =========
elfsh@WTH $ ./a.out
First_printf test
First_puts
Second_printf test
Second_puts
LEGIT FUNC
legit func (test) !
========= END EXAMPLE 9 ===========
Now let's inject the stuff:
========= BEGIN EXAMPLE 10 ========
elfsh@WTH $ ./relinject.esh
The ELF shell 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
~load a.out
[*] Sun Jul 31 15:30:14 2005 - New object a.out loaded
~load rel.o
[*] Sun Jul 31 15:30:14 2005 - New object rel.o loaded
~reladd 1 2
Section Mirrored Successfully !
[*] ET_REL rel.o injected succesfully in ET_EXEC a.out
~redir puts puts_troj
[*] Function puts redirected to addr 0x08047164 <puts_troj>
~redir legit_func hook_func
[*] Function legit_func redirected to addr 0x08047134 <hook_func>
~save fake_aout
[*] Object fake_aout saved successfully
~quit
[*] Unloading object 1 (rel.o)
[*] Unloading object 2 (a.out) *
.:: Bye -:: The ELF shell 0.65
========= END EXAMPLE 10 =========
Let's now execute the modified binary.
========= BEGIN EXAMPLE 11 =========
elfsh@WTH $ ./fake_aout
First_printf test
Trojan injected ET_REL takes control now [Z:First_puts:42:43:44:45:1]
extcall!
First_puts
calling write
Second_printf test
Trojan injected ET_REL takes control now [Z:Second_puts:42:43:44:45:1]
extcall!
Second_puts
calling write
HOOK FUNC test !
Trojan injected ET_REL takes control now [Z:LEGIT FUNC:42:43:44:45:1]
extcall!
calling write
legit func (test) !
elfsh@WTH $
========= END EXAMPLE 11 =========
Fine. Clearly legit_func has been redirected on the hook
function, and hook_func takes care of calling back the
legit_func using the old symbol technique described in
the first issue of the Cerberus articles serie.
Let's see the original legit_func code which is redirected
using the CFLOW technique.
========= BEGIN EXAMPLE 12 =========
080484C0 legit_func + 0 push %ebp
080484C1 legit_func + 1 mov %esp,%ebp
080484C3 legit_func + 3 sub $8,%esp
080484C6 legit_func + 6 mov $<_IO_stdin_used + 4>,(%esp,1)
080484CD legit_func + 13 call <.plt + 32>
080484D2 legit_func + 18 mov $<_IO_stdin_used + 15>,(%esp,1)
========= END EXAMPLE 12 =========
Now the modified code:
========= BEGIN EXAMPLE 13 =========
080484C0 legit_func + 0 jmp <hook_legit_func>
080484C5 legit_func + 5 nop
080484C6 legit_func + 6 mov $<_IO_stdin_used + 4>,(%esp,1)
080484CD legit_func + 13 call <puts>
080484D2 legit_func + 18 mov $<_IO_stdin_used + 15>,(%esp,1)
080484D9 legit_func + 25 mov 8(%ebp),%eax
080484DC legit_func + 28 mov %eax,4(%esp,1)
080484E0 legit_func + 32 call <printf>
080484E5 legit_func + 37 leave
080484E6 legit_func + 38 xor %eax,%eax
========= END EXAMPLE 13 =========
We create a new section .elfsh.hooks whoose data is array
of hook code stubs like this one:
========= BEGIN EXAMPLE 14 =========
08042134 hook_legit_func + 0 jmp <hook_func>
08042139 old_legit_func + 0 push %ebp
0804213A old_legit_func + 1 mov %esp,%ebp
0804213C old_legit_func + 3 sub $8,%esp
0804213F old_legit_func + 6 jmp <legit_func + 6>
========= END EXAMPLE 14 =========
The MIPS logs are on Devhell Labs machine which is down
as we are at WTH and we will put them in the updated
version of that article as soon as we get back.
---[ B. ALTPLT technique revisited
ALTPLT technique v1 was presented in the Cerberus ELF Interface
paper. As already stated, it was not satisfying and we finally
found the .dynamic DT_PLTREL trick:
=============== BEGIN EXAMPLE 16 ================
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object fake_aout]
[000] 0x00000000 ------- foff:00000000 sz:00000000 link:00
[001] 0x08042134 a-x---- .elfsh.hooks foff:00000308 sz:00000016 link:00
[002] 0x08043134 a-x---- .elfsh.extplt foff:00004404 sz:00000048 link:00
[003] 0x08044134 a-x---- .elfsh.altplt foff:00008500 sz:00004096 link:00
[004] 0x08045134 a--ms-- rel.o.rodata.str1.32 foff:12596 sz:04096 link:00
[005] 0x08046134 a--ms-- rel.o.rodata.str1.1 foff:16692 sz:04096 link:00
[006] 0x08047134 a-x---- rel.o.text foff:00020788 sz:00004096 link:00
[007] 0x08048134 a------ .interp foff:00024884 sz:00000019 link:00
[008] 0x08048148 a------ .note.ABI-tag foff:00024904 sz:00000032 link:00
[009] 0x08048168 a------ .hash foff:00024936 sz:00000064 link:10
[010] 0x080481A8 a------ .dynsym foff:00025000 sz:00000176 link:11
[011] 0x08048258 a------ .dynstr foff:00025176 sz:00000112 link:00
[012] 0x080482C8 a------ .gnu.version foff:00025288 sz:00000022 link:10
[013] 0x080482E0 a------ .gnu.version_r foff:00025312 sz:00000032 link:11
[014] 0x08048300 a------ .rel.dyn foff:00025344 sz:00000016 link:10
[015] 0x08048310 a------ .rel.plt foff:00025360 sz:00000056 link:10
[016] 0x08048348 a-x---- .init foff:00025416 sz:00000023 link:00
[017] 0x08048360 a-x---- .plt foff:00025440 sz:00000128 link:00
[018] 0x08048400 a-x---- .text foff:00025600 sz:00000736 link:00
[019] 0x080486E0 a-x---- .fini foff:00026336 sz:00000027 link:00
[020] 0x080486FC a------ .rodata foff:00026364 sz:00000116 link:00
[021] 0x08048770 a------ .eh_frame foff:00026480 sz:00000004 link:00
[022] 0x08049774 aw----- .ctors foff:00026484 sz:00000008 link:00
[023] 0x0804977C aw----- .dtors foff:00026492 sz:00000008 link:00
[024] 0x08049784 aw----- .jcr foff:00026500 sz:00000004 link:00
[025] 0x08049788 aw----- .dynamic foff:00026504 sz:00000200 link:11
[026] 0x08049850 aw----- .got foff:00026704 sz:00000004 link:00
[027] 0x08049854 aw----- .got.plt foff:00026708 sz:00000040 link:00
[028] 0x0804987C aw----- .data foff:00026748 sz:00000012 link:00
[029] 0x08049888 aw----- .bss foff:00026760 sz:00000008 link:00
[030] 0x08049890 aw----- rel.o.bss foff:00026768 sz:00004096 link:00
[031] 0x0804A890 aw----- rel.o.data foff:00030864 sz:00000004 link:00
[032] 0x0804A894 aw----- .elfsh.altgot foff:00030868 sz:00000048 link:00
[033] 0x0804A8E4 aw----- .elfsh.dynsym foff:00030948 sz:00000208 link:34
[034] 0x0804AA44 aw----- .elfsh.dynstr foff:00031300 sz:00000127 link:33
[035] 0x0804AB24 aw----- .elfsh.reldyn foff:00031524 sz:00000016 link:00
[036] 0x0804AB34 aw----- .elfsh.relplt foff:00031540 sz:00000072 link:00
[037] 0x00000000 ------- .comment foff:00031652 sz:00000665 link:00
[038] 0x00000000 ------- .debug_aranges foff:00032324 sz:00000120 link:00
[039] 0x00000000 ------- .debug_pubnames foff:00032444 sz:00000042 link:00
[040] 0x00000000 ------- .debug_info foff:00032486 sz:00006871 link:00
[041] 0x00000000 ------- .debug_abbrev foff:00039357 sz:00000511 link:00
[042] 0x00000000 ------- .debug_line foff:00039868 sz:00000961 link:00
[043] 0x00000000 ------- .debug_frame foff:00040832 sz:00000072 link:00
[044] 0x00000000 ---ms-- .debug_str foff:00040904 sz:00008067 link:00
[045] 0x00000000 ------- .debug_macinfo foff:00048971 sz:00029295 link:00
[046] 0x00000000 ------- .shstrtab foff:00078266 sz:00000507 link:00
[047] 0x00000000 ------- .symtab foff:00080736 sz:00002368 link:48
[048] 0x00000000 ------- .strtab foff:00083104 sz:00001785 link:47
[SHT_DYNAMIC]
[Object ./testsuite/etrel_inject/etrel_original/fake_aout]
[00] Name of needed library => libc.so.6 {DT_NEEDED}
[01] Address of init function => 0x08048348 {DT_INIT}
[02] Address of fini function => 0x080486E0 {DT_FINI}
[03] Address of symbol hash table => 0x08048168 {DT_HASH}
[04] Address of dynamic string table => 0x0804AA44 {DT_STRTAB}
[05] Address of dynamic symbol table => 0x0804A8E4 {DT_SYMTAB}
[06] Size of string table => 00000127 bytes {DT_STRSZ}
[07] Size of symbol table entry => 00000016 bytes {DT_SYMENT}
[08] Debugging entry (unknown) => 0x00000000 {DT_DEBUG}
[09] Processor defined value => 0x0804A894 {DT_PLTGOT}
[10] Size in bytes for .rel.plt => 0000072 bytes {DT_PLTRELSZ}
[11] Type of reloc in PLT => 00000017 {DT_PLTREL}
[12] Address of .rel.plt => 0x0804AB34 {DT_JMPREL}
[13] Address of .rel.got section => 0x0804AB24 {DT_REL}
[14] Total size of .rel section => 00000016 bytes {DT_RELSZ}
[15] Size of a REL entry => 00000008 bytes {DT_RELENT}
[16] SUN needed version table => 0x080482E0 {DT_VERNEED}
[17] SUN needed version number => 0001 {DT_VERNEEDNUM}
[18] GNU version VERSYM => 0x080482C8 {DT_VERSYM}
=============== END EXAMPLE 16 ================
As you can see, various sections has been copied and extended,
and their entries in .dynamic changed. That holds for .got
(DT_PLTGOT), .rel.plt (DT_JMPREL), .dynsym (DT_SYMTAB), and
.dynstr (DT_STRTAB). Changing those entries allow for the
new ALTPLT technique without any line of assembly. The specific
ALTPLT technique allow still for function redirection with the
capability of calling back the original function without erasing
the hook as you call it. The output of that binary was already
pasted and we wont copy it again here.
---[ C. ALTGOT technique : the RISC complement
On ALPHA and MIPS architecture, calls to PLT entries are
done differently. Indeed, instead of a direct call instruction on
the entry, an indirect jump is used for using the GOT entry linked
to the desired function. If such entry is filled, then the
function is called directly. By default, the GOT entries contains
the pointer on the PLT entries. When the dynamic linker is called
the GOT entries are filled and then the indirect jump instruction
on MIPS and ALPHA do not use the PLT entry anymore. What do we
learn from this ? Simply that we cannot rely on a classical PLT
hijacking because the PLT entry code wont be called if the GOT
entry is already filled.
Let's see how to developed the ALTGOT technique on the ALPHA
architecture:
========= BEGIN EXAMPLE 17 =========
elfsh@alpha$ cat host.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
char *str;
str = malloc(10);
if (str == NULL)
goto err;
strcpy(str, "test");
printf("First_printf %s\n", str);
fflush(stdout);
puts("First_puts");
printf("Second_printf %u\n", 42);
puts("Second_puts");
fflush(stdout);
return (0);
err:
printf("Malloc problem %u\n", 42);
return (-1);
}
elfsh@alpha$ gcc host.c -o a.out
elfsh@alpha$ file ./a.out
a.out: ELF 64-bit LSB executable, Alpha (unofficial), for NetBSD 2.0G,
dynamically linked, not stripped
========= END EXAMPLE 17 =========
The original binary executes:
========= BEGIN EXAMPLE 18 =========
elfsh@alpha$ ./a.out
First_printf test
First_puts
Second_printf 42
Second_puts
========= END EXAMPLE 18 ==========
Let's look again the relocatable object we are injecting:
========= BEGIN EXAMPLE 19 =========
elfsh@alpha$ cat rel.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glvar_testreloc = 42;
int glvar_testreloc_bss;
char glvar_testreloc_bss2;
short glvar_testreloc_bss3;
int puts_troj(char *str)
{
int local = 1;
char *str2;
str2 = malloc(10);
*str2 = 'Z';
*(str2 + 1) = 0x00;
glvar_testreloc_bss = 43;
glvar_testreloc_bss2 = 44;
glvar_testreloc_bss3 = 45;
printf("Trojan injected ET_REL takes control now "
"[%s:%s:%u:%u:%hhu:%hu:%u] \n",
str2, str,
glvar_testreloc,
glvar_testreloc_bss,
glvar_testreloc_bss2,
glvar_testreloc_bss3,
local);
old_puts(str);
fflush(stdout);
return (0);
}
int func2()
{
return (42);
}
========= END EXAMPLE 19 =========
Now we inject the stuff:
========= BEGIN EXAMPLE 20 =========
elfsh@alpha$ ./etrel_injecter
[*] ET_REL injected
elfsh@alpha$ ./fake_aout
First_printf test
Trojan injected ET_REL takes control now [Z:First_puts:42:43:44:45:1]
First_puts
Second_printf 42
Trojan injected ET_REL takes control now [Z:Second_puts:42:43:44:45:1]
Second_puts
========= END EXAMPLE 20 ==========
========= BEGIN EXAMPLE 21 =========
elfsh@alpha$ elfsh -f fake_aout -s -p
[*] Object fake_aout has been loaded (O_RDONLY)
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object fake_aout]
[000] 0x000000000 ------- foff:00000 sz:00000
[001] 0x120000190 a------ .interp foff:00400 sz:00023
[002] 0x1200001A8 a------ .note.netbsd.ident foff:00424 sz:00024
[003] 0x1200001C0 a------ .hash foff:00448 sz:00544
[004] 0x1200003E0 a------ .dynsym foff:00992 sz:00552
[005] 0x120000608 a------ .dynstr foff:01544 sz:00251
[006] 0x120000708 a------ .rela.dyn foff:01800 sz:00096
[007] 0x120000768 a------ .rela.plt foff:01896 sz:00168
[008] 0x120000820 a-x---- .init foff:02080 sz:00128
[009] 0x1200008A0 a-x---- .text foff:02208 sz:01312
[010] 0x120000DC0 a-x---- .fini foff:03520 sz:00104
[011] 0x120000E28 a------ .rodata foff:03624 sz:00162
[012] 0x120010ED0 aw----- .data foff:03792 sz:00000
[013] 0x120010ED0 a------ .eh_frame foff:03792 sz:00004
[014] 0x120010ED8 aw----- .dynamic foff:03800 sz:00352
[015] 0x120011038 aw----- .ctors foff:04152 sz:00016
[016] 0x120011048 aw----- .dtors foff:04168 sz:00016
[017] 0x120011058 aw----- .jcr foff:04184 sz:00008
[018] 0x120011060 awx---- .plt foff:04192 sz:00116
[019] 0x1200110D8 aw----- .got foff:04312 sz:00240
[020] 0x1200111C8 aw----- .sdata foff:04552 sz:00024
[021] 0x1200111E0 aw----- .sbss foff:04576 sz:00024
[022] 0x1200111F8 aw----- .bss foff:04600 sz:00056
[023] 0x120011230 a-x---- rel.o.text foff:04656 sz:00320
[024] 0x120011370 aw----- rel.o.sdata foff:04976 sz:00008
[025] 0x120011378 a--ms-- rel.o.rodata.str1.1 foff:04984 sz:00072
[026] 0x1200113C0 a-x---- .alt.plt.prolog foff:05056 sz:00048
[027] 0x1200113F0 a-x---- .alt.plt foff:05104 sz:00120
[028] 0x120011468 a------ .alt.got foff:05224 sz:00072
[029] 0x1200114B0 aw----- rel.o.got foff:05296 sz:00080
[030] 0x000000000 ------- .comment foff:05376 sz:00240
[031] 0x000000000 ------- .debug_aranges foff:05616 sz:00048
[032] 0x000000000 ------- .debug_pubnames foff:05664 sz:00027
[033] 0x000000000 ------- .debug_info foff:05691 sz:02994
[034] 0x000000000 ------- .debug_abbrev foff:08685 sz:00337
[035] 0x000000000 ------- .debug_line foff:09022 sz:00373
[036] 0x000000000 ------- .debug_frame foff:09400 sz:00048
[037] 0x000000000 ---ms-- .debug_str foff:09448 sz:01940
[038] 0x000000000 ------- .debug_macinfo foff:11388 sz:12937
[039] 0x000000000 ------- .ident foff:24325 sz:00054
[040] 0x000000000 ------- .shstrtab foff:24379 sz:00393
[041] 0x000000000 ------- .symtab foff:27527 sz:02400
[042] 0x000000000 ------- .strtab foff:29927 sz:00948
[Program header table .::. PHT]
[Object fake_aout]
[00] 0x120000040 -> 0x120000190 r-x => Program header table
[01] 0x120000190 -> 0x1200001A7 r-- => Program interpreter
[02] 0x120000000 -> 0x120000ECA r-x => Loadable segment
[03] 0x120010ED0 -> 0x120011510 rwx => Loadable segment
[04] 0x120010ED8 -> 0x120011038 rw- => Dynamic linking info
[05] 0x1200001A8 -> 0x1200001C0 r-- => Auxiliary information
[Program header table .::. SHT correlation]
[Object fake_aout]
[*] SHT is not stripped
[00] PT_PHDR
[01] PT_INTERP .interp
[02] PT_LOAD .interp .note.netbsd.ident .hash .dynsym .dynstr
.rela.dyn .rela.plt .init .text .fini .rodata
[03] PT_LOAD .data .eh_frame .dynamic .ctors .dtors .jcr .plt
.got .sdata .sbss .bss rel.o.text rel.o.sdata
rel.o.rodata.str1.1 .alt.plt.prolog .alt.plt
.alt.got rel.o.got
[04] PT_DYNAMIC .dynamic
[05] PT_NOTE .note.netbsd.ident
[*] Object fake_aout unloaded
========= END EXAMPLE 21 =========
Segments are extended the good way. Everything works well.
---[ D. EXTPLT technique : unknown function postlinking
This technique is one of the major one of the new ELFsh
version. It works on ET_EXEC and ET_DYN files, including
when the injection is done directly in memory. EXTPLT
consists in adding a new section (.elfsh.extplt) so that
we can add entries for new functions. When coupled to
.got, .dynsym, and .dynstr mirroring extension, it allows
for placing relocation entries that match the needs of
the new ALTPLT/ALTGOT couple. Let's look at the additional
relocation information using the elfsh -r command :
First the original binary:
========= BEGIN EXAMPLE 22 =========
[*] Object ./a.out has been loaded (O_RDONLY)
[RELOCATION TABLES]
[Object ./a.out]
{Section .rel.dyn}
[000] R_386_GLOB_DAT 0x08049850 sym[010] : __gmon_start__
[001] R_386_COPY 0x08049888 sym[004] : stdout
{Section .rel.plt}
[000] R_386_JMP_SLOT 0x08049860 sym[001] : fflush
[001] R_386_JMP_SLOT 0x08049864 sym[002] : puts
[002] R_386_JMP_SLOT 0x08049868 sym[003] : malloc
[003] R_386_JMP_SLOT 0x0804986C sym[005] : __libc_start_main
[004] R_386_JMP_SLOT 0x08049870 sym[006] : printf
[005] R_386_JMP_SLOT 0x08049874 sym[007] : free
[006] R_386_JMP_SLOT 0x08049878 sym[009] : read
[*] Object ./testsuite/etrel_inject/etrel_original/a.out unloaded
========= END EXAMPLE 22 =========
Let's now see the new relocation tables:
========= BEGIN EXAMPLE 23 =========
[*] Object fake_aout has been loaded (O_RDONLY)
[RELOCATION TABLES]
[Object ./fake_aout]
{Section .rel.dyn}
[000] R_386_GLOB_DAT 0x08049850 sym[010] : __gmon_start__
[001] R_386_COPY 0x08049888 sym[004] : stdout
{Section .rel.plt}
[000] R_386_JMP_SLOT 0x0804A8A0 sym[001] : fflush
[001] R_386_JMP_SLOT 0x0804A8A4 sym[002] : puts
[002] R_386_JMP_SLOT 0x0804A8A8 sym[003] : malloc
[003] R_386_JMP_SLOT 0x0804A8AC sym[005] : __libc_start_main
[004] R_386_JMP_SLOT 0x0804A8B0 sym[006] : printf
[005] R_386_JMP_SLOT 0x0804A8B4 sym[007] : free
[006] R_386_JMP_SLOT 0x0804A8B8 sym[009] : read
{Section .elfsh.reldyn}
[000] R_386_GLOB_DAT 0x08049850 sym[010] : __gmon_start__
[001] R_386_COPY 0x08049888 sym[004] : stdout
{Section .elfsh.relplt}
[000] R_386_JMP_SLOT 0x0804A8A0 sym[001] : fflush
[001] R_386_JMP_SLOT 0x0804A8A4 sym[002] : puts
[002] R_386_JMP_SLOT 0x0804A8A8 sym[003] : malloc
[003] R_386_JMP_SLOT 0x0804A8AC sym[005] : __libc_start_main
[004] R_386_JMP_SLOT 0x0804A8B0 sym[006] : printf
[005] R_386_JMP_SLOT 0x0804A8B4 sym[007] : free
[006] R_386_JMP_SLOT 0x0804A8B8 sym[009] : read
[007] R_386_JMP_SLOT 0x0804A8BC sym[011] : _IO_putc
[008] R_386_JMP_SLOT 0x0804A8C0 sym[012] : write
[*] Object fake_aout unloaded
========= END EXAMPLE 23 =========
As you see, _IO_putc and write functions has been used
in the injected object. We had to insert them inside
the host binary so that the output binary can work.
The .elfsh.relplt section is copied from the .rel.plt
section but with a doubled size so that we have room
for additional entries.
---[ E. IA32, SPARC32/64, ALPHA64, MIPS32 compliant algorithms
We did not have time to write the pseudo-algorithms for all
these techniques. Since it is something we care about
particulary, the article will be updated in the week with all
pseudo-algorithms information as soon as we return from WTH.
Stay tuned.
-------[ V. Constrained Debugging
In nowadays environment, hardened binaries are usually
of type ET_DYN. We had to support this kind of injection
since it allows for library files modification as much
powerful as the the executable files modification. Moreover
some distribution comes with a default binary set compiled
in ET_DYN, such has hardened gentoo.
Another improvement that we wanted to be done is the ET_REL
relocation in memory. The algorithm for it is the same than
the ondisk injection, but this time the disk is not changed
so it reduces forensics evidences like in [12]. It is believed
that this kind of injection can be used in exploits and direct
process backdooring without touching the hard disk. Evil eh ?
We are aware of another implementation of the ET_REL injection
into memory [10]. Ours supports a wider range of architecture and
couples with with the EXTPLT technique directly in memory.
A last technique that we wanted to develop was about extending
and debugging static executables. We developed a new technique
that we called EXTSTATIC algorithm. It allows for static
injections by taking parts of libc.a when functions or code is
missing. The same ET_REL injection algorithm is used except
that more than one relocatable file taken from libc.a is injected
at a time using a recursive dependency algorithm.
---[ A. ET_REL relocation in memory
Because we want to be able to provide a handler for
breakpoints (wether or not they are done using the
0xCC ia32 opcode or the more portable redirection
function), we allow for direct mapping of an ET_REL
object into memory. We use extra mmap zone for this,
always taking care that it does not break PaX : we
do not map any zone beeing both executable and writable.
Let's look at some simple binary that does just use
printf and puts.
========= BEGIN EXAMPLE 24 =========
elfsh@WTH $ ./a.out
[host] main argc 1
[host] argv[0] is : ./a.out
First_printf test
First_puts
Second_printf test
Second_puts
LEGIT FUNC
legit func (test) !
========= END EXAMPLE 24 =========
We use a small elfsh script as e2dbg so that it creates
another file with the debugger injected inside it, using
regular elfsh techniques. Let's look at it :
========= BEGIN EXAMPLE 25 =========
elfsh@WTH $ cat inject_e2dbg.esh
#!../../vm/elfsh
load a.out
set 1.dynamic[08].val 0x2
set 1.dynamic[08].tag DT_NEEDED
redir main e2dbg_run
save a.out_e2dbg
========= END EXAMPLE 25 =========
We then execute the modified binary.
========= BEGIN EXAMPLE 26 =========
elfsh@WTH $ ./aout_e2dbg
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 16:24:00 2005 - New object ./a.out_e2dbg loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 16:24:00 2005 - New object ./ibc.so.6 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/ld-linux.so.2 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libelfsh/libelfsh.so loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libreadline.so.5 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libtermcap.so.2 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libdl.so.2 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libncurses.so.5 loaded
(e2dbg-0.65) quit
[..: Embedded ELF Debugger returns to the grave :...]
[e2dbg_run] returning to 0x08045139
[host] main argc 1
[host] argv[0] is : ./a.out_e2dbg
First_printf test
First_puts
Second_printf test
Second_puts
LEGIT FUNC
legit func (test) !
elfsh@WTH $
========= END EXAMPLE 26 =========
Okay, that was easy. What if we want to do something more
interresting like ET_REL object injection into memory. We
will make use of the profile command so that we can see
the autoprofiling feature of e2dbg. This command is always
useful to learn more about the internals of the debugger,
and for debugging problems inherent to the debugger.
========= BEGIN EXAMPLE 27 =========
elfsh@WTH $ ./a.out_e2dbg
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 16:12:48 2005 - New object ./a.out_e2dbg loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 16:12:48 2005 - New object ./ibc.so.6 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/ld-linux.so.2 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libelfsh.so loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libreadline.so.5 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libtermcap.so.2 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libdl.so.2 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libncurses.so.5 loaded
(e2dbg-0.65) linkmap
.::. Linkmap entries .::.
[01] addr : 0x00000000 dyn : 0x080497D4 -
[02] addr : 0x00000000 dyn : 0xFFFFE590 -
[03] addr : 0xB7E73000 dyn : 0xB7F9AD3C - /lib/tls/libc.so.6
[04] addr : 0xB7E26000 dyn : 0xB7E6F01C - ./ibc.so.6
[05] addr : 0xB7FB9000 dyn : 0xB7FCFF14 - /lib/ld-linux.so.2
[06] addr : 0xB7DF3000 dyn : 0xB7E24018 - /lib/libelfsh.so
[07] addr : 0xB7DC6000 dyn : 0xB7DEE46C - /lib/libreadline.so.5
[08] addr : 0xB7DC2000 dyn : 0xB7DC5BB4 - /lib/libtermcap.so.2
[09] addr : 0xB7DBE000 dyn : 0xB7DC0EEC - /lib/libdl.so.2
[10] addr : 0xB7D7C000 dyn : 0xB7DBB1C0 - /lib/libncurses.so.5
(e2dbg-0.65) list
.::. Working files .::.
[001] Sun Jul 31 16:24:00 2005 D ID: 9 /lib/libncurses.so.5
[002] Sun Jul 31 16:24:00 2005 D ID: 8 /lib/libdl.so.2
[003] Sun Jul 31 16:24:00 2005 D ID: 7 /lib/libtermcap.so.2
[004] Sun Jul 31 16:24:00 2005 D ID: 6 /lib/libreadline.so.5
[005] Sun Jul 31 16:24:00 2005 D ID: 5 /lib/libelfsh.so
[006] Sun Jul 31 16:24:00 2005 D ID: 4 /lib/ld-linux.so.2
[007] Sun Jul 31 16:24:00 2005 D ID: 3 ./ibc.so.6
[008] Sun Jul 31 16:24:00 2005 D ID: 2 /lib/tls/libc.so.6
[009] Sun Jul 31 16:24:00 2005 *D ID: 1 ./a.out_e2dbg
.::. ELFsh modules .::.
[*] No loaded module
(e2dbg-0.65) source ./etrelmem.esh
~load myputs.o
[*] Sun Jul 31 16:13:32 2005 - New object myputs.o loaded
[!!] Loaded file is not the linkmap, switching to STATIC mode
~switch 1
[*] Switched on object 1 (./a.out_e2dbg)
~mode dynamic
[*] e2dbg is now in DYNAMIC mode
~reladd 1 10
[*] ET_REL myputs.o injected succesfully in ET_EXEC ./a.out_e2dbg
~profile
.:: Profiling enable
+ <vm_print_actual@loop.c:38>
~redir puts myputs
+ <vm_implicit@implicit.c:91>
+ <cmd_hijack@fcthijack.c:19>
+ <elfsh_get_metasym_by_name@sym_common.c:283>
+ <elfsh_get_dynsymbol_by_name@dynsym.c:255>
+ <elfsh_get_dynsymtab@dynsym.c:87>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_dynsymbol_name@dynsym.c:17>
[W] <elfsh_get_dynsymbol_by_name@dynsym.c:274> Symbol not found
[P] --[ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_dynsymbol_name@dynsym.c:17>
[P] --- Last 2 function(s) recalled 12 time(s) ---
+ <elfsh_get_symbol_by_name@symbol.c:236>
+ <elfsh_get_symtab@symbol.c:110>
+ <elfsh_get_symbol_name@symbol.c:20>
[P] --[ <elfsh_get_symbol_name@symbol.c:20>
[P] --- Last 1 function(s) recalled 114 time(s) ---
+ <elfsh_hijack_function_by_name@hijack.c:25>
+ <elfsh_setup_hooks@hooks.c:199>
+ <elfsh_get_pagesize@hooks.c:783>
+ <elfsh_get_archtype@hooks.c:624>
+ <elfsh_get_arch@elf.c:179>
+ <elfsh_copy_plt@altplt.c:525>
+ <elfsh_static_file@elf.c:491>
+ <elfsh_get_segment_by_type@pht.c:215>
+ <elfsh_get_pht@pht.c:364>
+ <elfsh_get_segment_type@pht.c:174>
[P] --[ <elfsh_get_segment_type@pht.c:174>
[P] --- Last 1 function(s) recalled 4 time(s) ---
+ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_relink_plt@altplt.c:121>
+ <elfsh_get_archtype@hooks.c:624>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_relink_plt@altplt.c:121>
[P] --[ <elfsh_get_archtype@hooks.c:624>
[P] --- Last 3 function(s) recalled 1 time(s) ---
+ <elfsh_get_elftype@hooks.c:662>
+ <elfsh_get_objtype@elf.c:204>
+ <elfsh_get_ostype@hooks.c:709>
+ <elfsh_get_real_ostype@hooks.c:679>
+ <elfsh_get_interp@interp.c:41>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_section_by_name@section.c:168>
+ <elfsh_get_section_name@sht.c:474>
[P] --[ <elfsh_get_section_name@sht.c:474>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_symbol_by_name@symbol.c:236>
+ <elfsh_get_symtab@symbol.c:110>
+ <elfsh_get_symbol_name@symbol.c:20>
[W] <elfsh_get_symbol_by_name@symbol.c:253> Symbol not found
[P] --[ <elfsh_get_symbol_name@symbol.c:20>
[P] --- Last 1 function(s) recalled 114 time(s) ---
+ <elfsh_is_pltentry@plt.c:73>
[W] <elfsh_is_pltentry@plt.c:77> Invalid NULL parameter
+ <elfsh_get_dynsymbol_by_name@dynsym.c:255>
+ <elfsh_get_dynsymtab@dynsym.c:87>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_dynsymbol_name@dynsym.c:17>
[P] --[ <elfsh_is_pltentry@plt.c:73>
[P] --[ <elfsh_get_dynsymbol_by_name@dynsym.c:255>
[P] --[ <elfsh_get_dynsymtab@dynsym.c:87>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_dynsymbol_name@dynsym.c:17>
[P] --- Last 5 function(s) recalled 1 time(s) ---
+ <elfsh_get_plt@plt.c:16>
+ <elfsh_is_plt@plt.c:49>
+ <elfsh_get_section_name@sht.c:474>
+ <elfsh_is_altplt@plt.c:62>
[P] --[ <elfsh_is_plt@plt.c:49>
[P] --[ <elfsh_get_section_name@sht.c:474>
[P] --[ <elfsh_is_altplt@plt.c:62>
[P] --- Last 3 function(s) recalled 3 time(s) ---
+ <elfsh_get_anonymous_section@section.c:334>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_is_plt@plt.c:49>
[P] --[ <elfsh_get_section_name@sht.c:474>
[P] --[ <elfsh_is_altplt@plt.c:62>
[P] --[ <elfsh_get_anonymous_section@section.c:334>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 5 function(s) recalled 44 time(s) ---
+ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_hijack_plt_ia32@ia32.c:258>
+ <elfsh_get_foffset_from_vaddr@raw.c:85>
+ <elfsh_get_pltentsz@plt.c:94>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_hijack_plt_ia32@ia32.c:258>
[P] --[ <elfsh_get_foffset_from_vaddr@raw.c:85>
[P] --[ <elfsh_get_pltentsz@plt.c:94>
[P] --- Last 4 function(s) recalled 1 time(s) ---
+ <elfsh_munprotect@runtime.c:97>
+ <elfsh_get_parent_section@section.c:380>
+ <elfsh_get_parent_segment@pht.c:304>
+ <elfsh_segment_is_readable@pht.c:14>
+ <elfsh_segment_is_writable@pht.c:21>
+ <elfsh_segment_is_executable@pht.c:28>
+ <elfsh_raw_write@raw.c:22>
+ <elfsh_get_parent_section_by_foffset@section.c:416>
+ <elfsh_get_sht@sht.c:159>
+ <elfsh_get_section_type@sht.c:887>
+ <elfsh_get_anonymous_section@section.c:334>
+ <elfsh_get_raw@section.c:691>
+ <elfsh_raw_write@raw.c:22>
+ <elfsh_get_parent_section_by_foffset@section.c:416>
+ <elfsh_get_sht@sht.c:159>
+ <elfsh_get_section_type@sht.c:887>
+ <elfsh_get_anonymous_section@section.c:334>
+ <elfsh_get_raw@section.c:691>
+ <elfsh_get_pltentsz@plt.c:94>
+ <elfsh_get_arch@elf.c:179>
+ <elfsh_mprotect@runtime.c:135>
[*] Function puts redirected to addr 0xB7FB6000 <myputs>
+ <vm_print_actual@loop.c:38>
~profile
+ <vm_implicit@implicit.c:91>
.:: Profiling disable
[*] ./etrelmem.esh sourcing -OK-
(e2dbg-0.65) continue
[..: Embedded ELF Debugger returns to the grave :...]
[e2dbg_run] returning to 0x08045139
[host] main argc 1
[host] argv[0] is : ./a.out_e2dbg
First_printf test
Hijacked puts !!! arg = First_puts
First_puts
Second_printf test
Hijacked puts !!! arg = Second_puts
Second_puts
Hijacked puts !!! arg = LEGIT FUNC
LEGIT FUNC
legit func (test) !
elfsh@WTH $
========= END EXAMPLE 27 =========
Really cool. We hijacked 2 functions (puts and legit_func) using
the 2 different (ALTPLT and CFLOW) techniques.
---[ B. ET_REL relocation into ET_DYN
We ported the ET_REL injection and the EXTPLT technique to ET_DYN
files. The biggest difference is that ET_DYN files have a
relative address space ondisk. Of course, stripped binaries
have no effect on our algorithms and we dont need any
non-mandatory information such as debug sections or anything.
Let's see what happens on this ET_DYN files:
========= BEGIN EXAMPLE 28 =========
elfsh@WTH $ file main
main: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV),
stripped
elfsh@WTH $ ./main
0x800008c8 main(argc=0xbfa238d0, argv=0xbfa2387c, envp=0xbfa23878,
auxv=0xbfa23874) __guard=0xb7ef4148
ssp-all (Stack) Triggering an overflow by copying [20] of data into [10] \
of space
main: stack smashing attack in function main()
Aborted
elfsh@WTH $ ./main AAAAA
0x800008c8 main(argc=0xbf898e40, argv=0xbf898dec, envp=0xbf898de8,
auxv=0xbf898de4) __guard=0xb7f6a148
ssp-all (Stack) Copying [5] of data into [10] of space
elfsh@WTH $ ./main AAAAAAAAAAAAAAAAAAAAAAAAAAA
0x800008c8 main(argc=0xbfd3c8e0, argv=0xbfd3c88c, envp=0xbfd3c888,
auxv=0xbfd3c884) __guard=0xb7f0b148
ssp-all (Stack) Copying [27] of data into [10] of space
main: stack smashing attack in function main()
Aborted
========= END EXAMPLE 28 =========
For the sake of fun, we decided to study in priority the
hardened gentoo binaries [11] . Those comes with PIE (Position
Independant Executable) and SSP (Stack Smashing Protection)
built in. It does not change a line of our algorithm. Here
are some tests done on a stack smashing protected binary
with an overflow in the first parameter, triggering the
stack smashing handler. We will redirect that handler
to show that it is a normal function that use classical
PLT mechanisms.
========= BEGIN EXAMPLE 29 =========
elfsh@WTH $ cat simple.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int fake_main(int argc, char **argv)
{
old_printf("I am the main function, I have %d argc and my "
"argv is %08X yupeelala \n",
argc, argv);
write(1, "fake_main is calling write ! \n", 30);
old_main(argc, argv);
return (0);
}
char* fake_strcpy(char *dst, char *src)
{
printf("The fucker wants to copy %s at address %08X \n", src, dst);
return ((char *) old_strcpy(dst, src));
}
void fake_stack_smash_handler(char func[], int damaged)
{
static int i = 0;
printf("calling printf from stack smashing handler %u\n", i++);
if (i>3)
old___stack_smash_handler(func, damaged);
else
printf("Same player play again [damaged = %08X] \n", damaged);
printf("A second (%d) printf from the handler \n", 2);
}
int fake_libc_start_main(void *one, void *two, void *three, void *four,
void *five, void *six, void *seven)
{
static int i = 0;
old_printf("fake_libc_start_main \n");
printf("start_main has been run %u \n", i++);
return (old___libc_start_main(one, two, three, four, five, six, seven));
}
========= END EXAMPLE 29 =========
The elfsh script that allow for the modification is :
========= BEGIN EXAMPLE 30 =========
elfsh@WTH $ cat relinject.esh
#!../../../vm/elfsh
load main
load simple.o
reladd 1 2
redir main fake_main
redir __stack_smash_handler fake_stack_smash_handler
redir __libc_start_main fake_libc_start_main
redir strcpy fake_strcpy
save fake_main
quit
========= END EXAMPLE 30 =========
Now let's see this in action !
========= BEGIN EXAMPLE 31 =========
elfsh@WTH $ ./relinject.esh
The ELF shell 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
~load main
[*] Sun Jul 31 17:24:20 2005 - New object main loaded
~load simple.o
[*] Sun Jul 31 17:24:20 2005 - New object simple.o loaded
~reladd 1 2
[*] ET_REL simple.o injected succesfully in ET_DYN main
~redir main fake_main
[*] Function main redirected to addr 0x00005154 <fake_main>
~redir __stack_smash_handler fake_stack_smash_handler
[*] Function __stack_smash_handler redirected to addr
0x00005203 <fake_stack_smash_handler>
~redir __libc_start_main fake_libc_start_main
[*] Function __libc_start_main redirected to addr
0x00005281 <fake_libc_start_main>
~redir strcpy fake_strcpy
[*] Function strcpy redirected to addr 0x000051BD <fake_strcpy>
~save fake_main
[*] Object fake_main saved successfully
~quit
[*] Unloading object 1 (simple.o)
[*] Unloading object 2 (main) *
.:: Bye -:: The ELF shell 0.65
========= END EXAMPLE 31 =========
What about the result ?
========= BEGIN EXAMPLE 32 =========
elfsh@WTH $ ./fake_main
fake_libc_start_main
start_main has been run 0
I am the main function, I have 1 argc and my argv is BF9A6F54 yupeelala
fake_main is calling write !
0x800068c8 main(argc=0xbf9a6e80, argv=0xbf9a6e2c, envp=0xbf9a6e28,
auxv=0xbf9a6e24) __guard=0xb7f78148
ssp-all (Stack) Triggering an overflow by copying [20] of data into [10] of space
The fucker wants to copy 01234567890123456789 at address BF9A6E50
calling printf from stack smashing handler 0
Same player play again [damaged = 39383736]
A second (2) printf from the handler
elfsh@WTH $ ./fake_main AAAA
fake_libc_start_main
start_main has been run 0
I am the main function, I have 2 argc and my argv is BF83A164 yupeelala
fake_main is calling write !
0x800068c8 main(argc=0xbf83a090, argv=0xbf83a03c, envp=0xbf83a038,
auxv=0xbf83a034) __guard=0xb7f09148
ssp-all (Stack) Copying [4] of data into [10] of space
The fucker wants to copy AAAA at address BF83A060
elfsh@WTH $ ./fake_main AAAAAAAAAAAAAAA
fake_libc_start_main
start_main has been run 0
I am the main function, I have 2 argc and my argv is BF8C7F24 yupeelala
fake_main is calling write !
0x800068c8 main(argc=0xbf8c7e50, argv=0xbf8c7dfc, envp=0xbf8c7df8,
auxv=0xbf8c7df4) __guard=0xb7f97148
ssp-all (Stack) Copying [15] of data into [10] of space
The fucker wants to copy AAAAAAAAAAAAAAA at address BF8C7E20
========= END EXAMPLE 32 =========
No problem there : strcpy, main, libc_start_main and
__stack_smash_handler are redirected on our own routines
as the output shows.
---[ C. Extending static executables
Now we would like to be able to debug static binary the same way
we do for dynamics. But we can't inject e2dbg using DT_NEEDED
dependances. So the idea is to inject e2dbg as an ET_REL as ET_REL
into ET_EXEC is possible on static binaries. E2dbg as many more
dependancy than a simple host.c program. So the idea is to inject
the missing part of static library when it is necessary.
So we have to resolve dependancy on-the-fly while ET_REL injection
is performed.
To be able to find the suitable ET_REL to inject, ELFsh load all
the ET_REL from static library (.a) then the resolution is done
using this pool of binary.
Circular dependancy are solved by using second stage relocation
when the required symbol is in a file that is being injected.
A problem is remaining, as for now we had one PT_LOAD by injected
section, we quickly reach more than 500 PT_LOAD. This seems to be
a bit too much for a regular ELF static file.
This technique provide the same features as EXTPLT but for static
binaries : we can inject what we want (regardless of what the host
binary contains.
So here is an smaller (working) example:
========= BEGIN EXAMPLE 33 =========
elfsh@WTH $ cat host.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int legit_func(char *str)
{
puts("legit func !");
return (0);
}
int main()
{
char *str;
char buff[BUFSIZ];
read(0, buff, BUFSIZ-1);
puts("First_puts");
puts("Second_puts");
fflush(stdout);
legit_func("test");
return (0);
}
elfsh@WTH $ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, statically linked,
not stripped
elfsh@WTH $ ./a.out
First_puts
Second_puts
legit func !
========= END EXAMPLE 33 =========
The injected file source code is as follow :
========= BEGIN EXAMPLE 34 =========
elfsh@WTH $ cat rel2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
int glvar_testreloc = 42;
int glvar_testreloc_bss;
char glvar_testreloc_bss2;
short glvar_testreloc_bss3;
int hook_func(char *str)
{
int sd;
printf("hook func %s !\n", str);
return (old_legit_func(str));
}
int puts_troj(char *str)
{
int local = 1;
char *str2;
int fd;
char name[16];
void *a;
str2 = malloc(10);
*str2 = 'Z';
*(str2 + 1) = 0x00;
glvar_testreloc_bss = 43;
glvar_testreloc_bss2 = 44;
glvar_testreloc_bss3 = 45;
memset(name, 0, 16);
printf("Trojan injected ET_REL takes control now "
"[%s:%s:%u:%u:%hhu:%hu:%u] \n",
str2, str,
glvar_testreloc,
glvar_testreloc_bss,
glvar_testreloc_bss2,
glvar_testreloc_bss3,
local);
free(str2);
gethostname(name, 15);
printf("hostname : %s\n", name);
printf("printf called from puts_troj [%s] \n", str);
fd = open("/etc/services", 0, O_RDONLY);
if (fd)
{
if ((a = mmap(0, 100, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *) -1)
{
perror("mmap");
close(fd);
printf("mmap failed : fd: %d\n", fd);
return (-1);
}
printf("-=-=-=-=-=- BEGIN /etc/services %d -=-=-=-=-=\n", fd);
printf("host : %.60s\n", (char *) a);
printf("-=-=-=-=-=- END /etc/services %d -=-=-=-=-=\n", fd);
printf("mmap succeed fd : %d\n", fd);
close(fd);
}
old_puts(str);
fflush(stdout);
return (0);
}
========= END EXAMPLE 34 =========
The load_lib.esh generated script looks like this :
========= BEGIN EXAMPLE 34 =========
elfsh@WTH $ head -n 10 load_lib.esh
#!../../../vm/elfsh
load libc/init-first.o
load libc/libc-start.o
load libc/sysdep.o
load libc/version.o
load libc/check_fds.o
load libc/libc-tls.o
load libc/elf-init.o
load libc/dso_handle.o
load libc/errno.o
========= END EXAMPLE 34 =========
Here is the injection ELFsh script:
========= BEGIN EXAMPLE 35 =========
elfsh@WTH $ cat relinject.esh
#!../../../vm/elfsh
exec gcc -g3 -static host.c
exec gcc -g3 -static rel2.c -c
load a.out
load rel2.o
source ./load_lib.esh
reladd 1 2
redir puts puts_troj
redir legit_func hook_func
save fake_aout
quit
========= END EXAMPLE 35 =========
Stripped output of the injection :
========= BEGIN EXAMPLE 36 =========
elfsh@WTH $ ./relinject.esh
The ELF shell 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
~exec gcc -g3 -static host.c
[*] Command executed successfully
~exec gcc -g3 -static rel2.c -c
[*] Command executed successfully
~load a.out
[*] Sun Jul 31 16:37:32 2005 - New object a.out loaded
~load rel2.o
[*] Sun Jul 31 16:37:32 2005 - New object rel2.o loaded
~source ./load_lib.esh
~load libc/init-first.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/init-first.o loaded
~load libc/libc-start.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/libc-start.o loaded
~load libc/sysdep.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/sysdep.o loaded
~load libc/version.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/version.o loaded
[[... 1414 files later ...]]
[*] ./load_lib.esh sourcing -OK-
~reladd 1 2
[*] ET_REL rel2.o injected succesfully in ET_EXEC a.out
~redir puts puts_troj
[*] Function puts redirected to addr 0x080B7026 <puts_troj>
~redir legit_func hook_func
[*] Function legit_func redirected to addr 0x080B7000 <hook_func>
~save fake_aout
[*] Object fake_aout saved successfully
~quit
[*] Unloading object 1 (libpthreadnonshared/pthread_atfork.oS)
[*] Unloading object 2 (libpthread/ptcleanup.o)
[*] Unloading object 3 (libpthread/pthread_atfork.o)
[*] Unloading object 4 (libpthread/old_pthread_atfork.o)
[[... 1416 files later ...]]
.:: Bye -:: The ELF shell 0.65
========= END EXAMPLE 36 =========
Does it works ?
========= BEGIN EXAMPLE 37 =========
elfsh@WTH $ ./fake_aout
Trojan injected ET_REL takes control now [Z:First_puts:42:43:44:45:1]
hostname : WTH
printf called from puts_troj [First_puts]
-=-=-=-=-=- BEGIN /etc/services 3 -=-=-=-=-=
host : # /etc/services
#
# Network services, Internet style
#
# Not
-=-=-=-=-=- END /etc/services 3 -=-=-=-=-=
mmap succeed fd : 3
First_puts
Trojan injected ET_REL takes control now [Z:Second_puts:42:43:44:45:1]
hostname : WTH
printf called from puts_troj [Second_puts]
-=-=-=-=-=- BEGIN /etc/services 3 -=-=-=-=-=
host : # /etc/services
#
# Network services, Internet style
#
# Not
-=-=-=-=-=- END /etc/services 3 -=-=-=-=-=
mmap succeed fd : 3
Second_puts
hook func test !
Trojan injected ET_REL takes control now [Z:legit func !:42:43:44:45:1]
hostname : WTH
printf called from puts_troj [legit func !]
-=-=-=-=-=- BEGIN /etc/services 3 -=-=-=-=-=
host : # /etc/services
#
# Network services, Internet style
#
# Not
-=-=-=-=-=- END /etc/services 3 -=-=-=-=-=
mmap succeed fd : 3
legit func !
========= END EXAMPLE 37 =========
Yes, It's working. Now have a look at the fake_aout file:
========= BEGIN EXAMPLE 38 =========
elfsh@WTH $ ../../../vm/elfsh -f ./fake_aout -s
[*] Object ./fake_aout has been loaded (O_RDONLY)
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object ./fake_aout]
[000] 0x00000000 ------- foff:000000 sz:00000
[001] 0x080480D4 a------ .note.ABI-tag foff:069844 sz:00032
[002] 0x08048100 a-x---- .init foff:069888 sz:00023
[003] 0x08048120 a-x---- .text foff:69920 sz:347364
[004] 0x0809CE10 a-x---- __libc_freeres_fn foff:417296 sz:02222
[005] 0x0809D6C0 a-x---- .fini foff:419520 sz:00029
[006] 0x0809D6E0 a------ .rodata foff:419552 sz:88238
[007] 0x080B2F90 a------ __libc_atexit foff:507792 sz:00004
[008] 0x080B2F94 a------ __libc_subfreeres foff:507796 sz:00036
[009] 0x080B2FB8 a------ .eh_frame foff:507832 sz:03556
[010] 0x080B4000 aw----- .ctors foff:512000 sz:00012
[011] 0x080B400C aw----- .dtors foff:512012 sz:00012
[012] 0x080B4018 aw----- .jcr foff:512024 sz:00004
[013] 0x080B401C aw----- .data.rel.ro foff:512028 sz:00044
[014] 0x080B4048 aw----- .got foff:512072 sz:00004
[015] 0x080B404C aw----- .got.plt foff:512076 sz:00012
[016] 0x080B4060 aw----- .data foff:512096 sz:03284
[017] 0x080B4D40 aw----- .bss foff:515380 sz:04736
[018] 0x080B5FC0 aw----- __libc_freeres_ptrs foff:520116 sz:00024
[019] 0x080B6000 aw----- rel2.o.bss foff:520192 sz:04096
[020] 0x080B7000 a-x---- rel2.o.text foff:524288 sz:04096
[021] 0x080B8000 aw----- rel2.o.data foff:528384 sz:00004
[022] 0x080B9000 a------ rel2.o.rodata foff:532480 sz:04096
[023] 0x080BA000 a-x---- .elfsh.hooks foff:536576 sz:00032
[024] 0x080BB000 aw----- libc/printf.o.bss foff:540672 sz:04096
[025] 0x080BC000 a-x---- libc/printf.o.text foff:544768 sz:04096
[026] 0x080BD000 aw----- libc/gethostname.o.bss foff:548864 sz:04096
[027] 0x080BE000 a-x---- libc/gethostname.o.text foff:552960 sz:04096
[028] 0x080BF000 aw----- libc/perror.o.bss foff:557056 sz:04096
[029] 0x080C0000 a-x---- libc/perror.o.text foff:561152 sz:04096
[030] 0x080C1000 a--ms-- libc/perror.o.rodata.str1.1 foff:565248 sz:04096
[031] 0x080C2000 a--ms-- libc/perror.o.rodata.str4.4 foff:569344 sz:04096
[032] 0x080C3000 aw----- libc/dup.o.bss foff:573440 sz:04096
[033] 0x080C4000 a-x---- libc/dup.o.text foff:577536 sz:04096
[034] 0x080C5000 aw----- libc/iofdopen.o.bss foff:581632 sz:04096
[035] 0x00000000 ------- .comment foff:585680 sz:20400
[036] 0x080C6000 a-x---- libc/iofdopen.o.text foff:585728 sz:04096
[037] 0x00000000 ------- .debug_aranges foff:606084 sz:00136
[038] 0x00000000 ------- .debug_pubnames foff:606220 sz:00042
[039] 0x00000000 ------- .debug_info foff:606262 sz:01600
[040] 0x00000000 ------- .debug_abbrev foff:607862 sz:00298
[041] 0x00000000 ------- .debug_line foff:608160 sz:00965
[042] 0x00000000 ------- .debug_frame foff:609128 sz:00068
[043] 0x00000000 ------- .debug_str foff:609196 sz:00022
[044] 0x00000000 ------- .debug_macinfo foff:609218 sz:28414
[045] 0x00000000 ------- .shstrtab foff:637632 sz:00632
[046] 0x00000000 ------- .symtab foff:640187 sz:30192
[047] 0x00000000 ------- .strtab foff:670379 sz:25442
[*] Object ./fake_aout unloaded
elfsh@WTH $ ../../../vm/elfsh -f ./fake_aout -p
[*] Object ./fake_aout has been loaded (O_RDONLY)
[Program Header Table .::. PHT]
[Object ./fake_aout]
[00] 0x8037000 -> 0x80B3D9C r-x memsz(511388) foff(000000) => Loadable seg
[01] 0x80B4000 -> 0x80B7258 rw- memsz(012888) foff(512000) => Loadable seg
[02] 0x80480D4 -> 0x80480F4 r-- memsz(000032) foff(069844) => Aux. info.
[03] 0x0000000 -> 0x0000000 rw- memsz(000000) foff(000000) => Stackflags
[04] 0x0000000 -> 0x0000000 --- memsz(000000) foff(000000) => New PaXflags
[05] 0x80B6000 -> 0x80B7000 rwx memsz(004096) foff(520192) => Loadable seg
[06] 0x80B7000 -> 0x80B8000 rwx memsz(004096) foff(524288) => Loadable seg
[07] 0x80B8000 -> 0x80B8004 rwx memsz(000004) foff(528384) => Loadable seg
[08] 0x80B9000 -> 0x80BA000 rwx memsz(004096) foff(532480) => Loadable seg
[09] 0x80BA000 -> 0x80BB000 rwx memsz(004096) foff(536576) => Loadable seg
[10] 0x80BB000 -> 0x80BC000 rwx memsz(004096) foff(540672) => Loadable seg
[11] 0x80BC000 -> 0x80BD000 rwx memsz(004096) foff(544768) => Loadable seg
[12] 0x80BD000 -> 0x80BE000 rwx memsz(004096) foff(548864) => Loadable seg
[13] 0x80BE000 -> 0x80BF000 rwx memsz(004096) foff(552960) => Loadable seg
[14] 0x80BF000 -> 0x80C0000 rwx memsz(004096) foff(557056) => Loadable seg
[15] 0x80C0000 -> 0x80C1000 rwx memsz(004096) foff(561152) => Loadable seg
[16] 0x80C1000 -> 0x80C2000 rwx memsz(004096) foff(565248) => Loadable seg
[17] 0x80C2000 -> 0x80C3000 rwx memsz(004096) foff(569344) => Loadable seg
[18] 0x80C3000 -> 0x80C4000 rwx memsz(004096) foff(573440) => Loadable seg
[19] 0x80C4000 -> 0x80C5000 rwx memsz(004096) foff(577536) => Loadable seg
[20] 0x80C5000 -> 0x80C6000 rwx memsz(004096) foff(581632) => Loadable seg
[21] 0x80C6000 -> 0x80C7000 rwx memsz(004096) foff(585728) => Loadable seg
[SHT correlation]
[Object ./fake_aout]
[*] SHT is not stripped
[00] PT_LOAD .note.ABI-tag .init .text __libc_freeres_fn .fini
.rodata __libc_atexit __libc_subfreeres .eh_frame
[01] PT_LOAD .ctors .dtors .jcr .data.rel.ro .got .got.plt .data
.bss __libc_freeres_ptrs
[02] PT_NOTE .note.ABI-tag
[03] PT_GNU_STACK
[04] PT_PAX_FLAGS
[05] PT_LOAD rel2.o.bss
[06] PT_LOAD rel2.o.text
[07] PT_LOAD rel2.o.data
[08] PT_LOAD rel2.o.rodata
[09] PT_LOAD .elfsh.hooks
[10] PT_LOAD libc/printf.o.bss
[11] PT_LOAD libc/printf.o.text
[12] PT_LOAD libc/gethostname.o.bss
[13] PT_LOAD libc/gethostname.o.text
[14] PT_LOAD libc/perror.o.bss
[15] PT_LOAD libc/perror.o.text
[16] PT_LOAD libc/perror.o.rodata.str1.1
[17] PT_LOAD libc/perror.o.rodata.str4.4
[18] PT_LOAD libc/dup.o.bss
[19] PT_LOAD libc/dup.o.text
[20] PT_LOAD libc/iofdopen.o.bss |.comment
[21] PT_LOAD libc/iofdopen.o.text
[*] Object ./fake_aout unloaded
========= END EXAMPLE 38 =========
We can notice the ET_REL really injected : printf.o@libc,
dup.o@libc, gethostname.o@libc, perror.o@libc and
iofdopen.o@libc.
Each injected file create several PT_LOAD segments.
Here it's ok but for injecting E2dbg that's really too much.
This technique will be improved as soon as possible by reusing
PT_LOAD entry when this is possible.
-------[ VI. Past and present
In the past we have shown that ET_REL injection into
non-relocatable ET_EXEC object is possible. This paper presented
multiple extensions and ports to this residency technique
(ET_DYN and static executables target). Coupled to the EXTPLT
technique that allow for a complete post-linking of the host file,
we can add function definitions and use unknown functions in the
software extension. All those static injection techniques worse
when all PaX options are enabled on the modified binary. Of course
, the position independant and stack smashing protection features
of hardened Gentoo does not protect anything when it comes to
binary manipulation.
We have also shown that it is possible to debug without using the
ptrace system call, which open the door for new reverse
engineering and embedded debugging methodology that bypass known
anti-debugging techniques. The embedded debugger is not completely
PaX proof and it is still necessary to disable the mprotect flag.
Even if it does not sound like a problem, we are investigating on
how to put breakpoints (e.g. redirections) without disabling it.
Our ground techniques are portable to many architectures (x86,
alpha, mips, sparc) on both 32bits and 64bits files. However our
proof of concept debugger was done for x86 only. We believe that
our techniques are portable enough to be able to port the debugger
without much troubles.
We wrote that article at the WTH conference in Netherland. Due to
Devhell Labs. machine crash during the meeting, we will publish
the e2dbg source code tomorrow evening (August 1st 2005) when we
get back at home and reboot the webs server machine. You can find
it on : http://elfsh.devhell.org as well as on the official mirror
http://elfsh.segfault.net.
Share and enjoy.
-------[ VII. Greetings
We thank all the peoples at the WhatTheHack party 2005 in
Netherland. We add much fun with you guys and again we will come
in the future. Special thanks go to andrewg for teaching us the
sigaction technique, dvorak for his early interest in the
improvement and optimization of the ALTPLT technique on the SPARC
architecture, sk from Devhell Labs. for libasm, solar from the
Hardened Gentoo project for providing us the ET_DYN pie/ssp
testsuite, the PaX team for the awesome entertaining kernel
protection patch, the phrack staff for delaying the Phrack #63
electronic release for us. Final shoutouts to s/ash from RTC for
driving us to WTH and the Coconut crew for everything and the
rest, you know who you are.
-------[ VIII. References
[1] The GNU debugger
http://www.gnu.org/software/gdb/
[2] PaX
http://pax.grsecurity.net/
[3] Silvio: binary reconstruction from a core image
http://vx.netlux.org/lib/vsc03.html
[4] Ripe & Pluf : Antiforensic evolution: Self
http://www.phrack.org (Phrack #63)
[5] Zeljko Vbra : Next-Gen. Runtime binary encryption using
on-demand function extraction
http://www.phrack.org (Phrack #63)
[6] lcamtuf : fenris
http://lcamtuf.coredump.cx/fenris/
[7] Ltrace team : ltrace
http://freshmeat.net/projects/ltrace/
[8] mammon : The dude (replacement to ptrace)
http://www.eccentrix.com/members/mammon/Text/dude_paper.txt
[9] andrewg : Binary protection schemes
http://felinemenace.org/papers/Binary_protection_schemes-1.00\
-prerelease.tar.gz
[10] jp : ET_REL injection in memory
Secret URL
[11] Hardened Gentoo project
http://www.gentoo.org/proj/en/hardened/
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH