Tags: pwn 

Rating:

*Apprentice's Return* was the first pwn challenge of SwampCTF 2018. A single
binary, `return`, is provided, an x86 32-bit unstripped ELF.

Opening the file in a dissasembler, we see three functions we're interested in:

* `main()`
* `doBattle()`
* `slayTheBeast()`

The first, `main()`, simply calls `doBattle()`.

The function `slayTheBeast()` outputs the flag, so this is our target function.

`doBattle()` prints out some story, then performs a `read()` of 50 bytes from
stdin to a buffer. Looking at the stack, we see the input buffer resides at
`ebp-0x26`, meaning we can overflow it by 12 bytes. The return address resides
8 bytes away from the end of the buffer, so it can be overwritten, as well as
another 4 bytes after.

Before the function returns, however, the return address is checked. If the
value is greater than or equal to 0x8048595, the program exits, otherwise it
outputs a line and returns, giving the attacker control over `eip`.

The first choice here would be to overwrite `eip` with the address of the
`slayTheBeast()` function, however that does not work as the address 0x80485db
is larger than the permitted value. If we try the largest permitted value, we
see it lands at the `nop` instruction at the very end of the `doBattle()`
function.

This is where the additional extra four bytes we can overflow come into play.
We have exactly enough room for another address. If we choose the maximum value
for our first address, it would appear that you could loop back and return to
the second address provided. This is the basis of return-oriented programming
(ROP) attacks. The challenge and binary name are clues to this. However, the
`leave` instruction before the `ret` is a hinderance, as it messes up the stack
frame, meaning our second address is not used.

Instead, we look for a different gadget. Using
[ROPgadget](https://github.com/JonathanSalwan/ROPgadget), we can even specify a
range of addresses, so that we don't get any gadgets with an address greater
than the allowed maximum:

```sh
ROPgadget --binary return --range 0-0x8048594
```

This gives us 59 unique gadgets. Of these, the simple `ret` gadget will work.
Our first return will jump from 0x8048596 to this address, 0x0804835a, popping
the value from the stack. The next `ret` from the gadget will be executed,
popping our second value from the stack. This has no restrictions, and so we
can use the address of the `slayTheBeast()` function. This function is returned
to, and we get our flag.

```sh
#!/bin/sh

# arg1 = first return addr - ret gadget (0x0804835A)
# arg2 = target addr - slayTheBeast() (0x80485db)

echo -n $(perl -e 'print "A" x 42, "\x5a\x83\x04\x08", "\xdb\x85\x04\x08"') \
| nc chal1.swampctf.com 1802 | grep flag
```

This gives us the flag: `flag{f34r_n0t_th3_4nc13n7_R0pn1qu3}`

Original writeup (http://tilde.town/~kirby/ctf/swampctf2018/apprentices-return.html).