This is a walk-through of CTF challenge Brainpan: 1. I downloaded the target VM image from vulhub and booted it in VMware. My source machine is a Kali Linux image, also running on VMware.

All my engagements start with netdiscover, which is why I scripted a smarter version I call netDiscoverVM. This shows the IP address of our target machine (192.168.226.128).

netdiscover.PNG

Then I use Sparta to run nmap and nikto scans on the target.

sparta.PNG

I connect to the target on port 9999 and it results in a “brainpan” banner and a password input screen.

nc1.PNG

I run dirb (directory buster) on port 10000 and see a /bin directory.

dirb.PNG

The /bin directory contains one EXE.

ip slash bin.PNG

I download the EXE and copy it to another folder.

cp bpan.PNG

I run strings on the EXE. It looks like the same banner from port 9999 is in this file.

strings.PNG

I run the EXE locally with OllyDbg.

olly1.PNG

Using Python, I use connect() for a socket connection and send “\x41”*1000, also known as 1000 A’s.

overflow41.PNG

Taking a look at the Registers in OllyDbg after sending the A’s, we notice a few things.

  1. EIP is overwritten by 41414141. This is crucial because the EIP holds the “extended instruction pointer” for the stack. In other words, it tells the computer where to go next to execute the next command and controls the flow of a program.
  2. EBP is overwritten by 41414141.
  3. The stack is overwritten with A’s starting at address 0043F7E0.

Register41paint.PNG

It’s great that I was able to overwrite Registers and the stack with A’s, however we need to pinpoint WHERE in my input string these important locations are overwritten. This is where the PWN Tools library for python comes in handy. Cyclic() will generate a string pattern of specified length. In my example, I create a 1000 character pattern, and also output a 20 character pattern to demonstrate the function.

cyclic.PNG

After sending the 1000 char pattern to the EXE, the same resources are overwritten with more meaningful values.

RegisterCyclic.PNG

I use cyclic_find() to determine how many characters into the pattern EIP is overwritten. It is overwritten 524 characters into the input. The stack begins to be overwritten 528 bytes into the input string, which is directly after EIP.

cyclicfind.PNG

To prove the point, I send 524 NOP’s, followed by “AAAA” + “BBBB”. We expect EIP to contain “AAAA” and the stack to contain “BBBB”. Both hold true.

lasttest.PNG RegisterVerification.PNG

Now I need to find the memory location of a Jmp or Call command in the assembly of our EXE. ROPgadget is the tool for the job.

ROPgadgetJmpCall.PNG

The memory location for jmp esp is 0x311712f3. I chose jmp esp because we can overwrite the data on the stack at ESP’s location. I use pack() to format the address little-endian.

packJmpESP.PNG

I use msfvenom to generate reverese shell code. I blacklist “\x00”, “\x0A”, “\x0F”, and “\xFF”. Set the return address to my IP and port 443.

msfvenom.PNG

The following is our shellcode in Python syntax.

catShell.PNG

Now we craft our full buffer overflow: 524 NOPs, followed by the address of Jmp ESP (which will end up in EIP). Then the shellcode.

overFlowDone.PNG

I open up a netcat listener on port 443. I send my overflow to port 9999 of our target machine. The result is a limited shell with uid = puck.

firstShell.PNG

I spawn a nicer shell. Then run sudo -l to see what “puck” can run as root. Puck can run /home/anansi/bin/anansi_util as root. I mess around with anansi_util but it doens’t get anywhere.

spawn bash.PNG

We also find a binary called “validate” which we can run as Anansi.

validate.PNG

Validate looks to take some string input and “validate” it.

testvalidate.PNG

The plan is to get validate down to my host machine. I output the base64 of validate.

base64validate.PNG

On my host machine, I re-create validate using its base64 and store it into validate locally.

validateonhost.PNG

Validate now works on my local host.

validatehosttest.PNG

I use gdb (GNU Debugger) to debug the Validate program with various inputs. First, I send a 2000 char patterned input string. EIP is overwritten by ‘eaab’. Using cyclic_find() we see that ‘eaab’ is 116 bytes into the input string. We send 116 NOPs followed by ‘BBBB’ to verify.

gdb1.PNG

ROPgadget shows a call eax at 0x080484af. This is the only call or jmp, therefore we have to use it.

rop2.PNG

I use google to find a 28 byte shellcode. There are 116 bytes to work with before EIP. The shellcode is 28 bytes, therefore I will have 88 bytes of nops. I divide the nops somewhat evenly around the shellcode.

overflow2.PNG

Sending the overflow to the program in gdb spawns a shell. We see this in the line “process 1566 is executing new program: /bin/dash”.

overflowLocal2.PNG

I head back to the target machine and send the same buffer overflow string to the real Validate program. I’m given an effective uid (euid) of anansi. Now that I’m Anansi I can modify anansi_util, which was the program Puck could run as root.

I modify anansi_util to grant a shell to anyone who executes it. Sudo anansi_util grants us a shell with root privileges!

root.PNG

Comments