level1.0
这个题目的程序用的是旧版glibc,没法本地测试了。但是这题比较简单,我们只需要覆盖返回地址。
from pwn import * |
非常轻松 主要想测试一下markdown的语法
随便插入个好玩的图片好了
level1.1
原理和1.0相同。
level2
lseek函数
off_t lseek(int fd, off_t offset, int whence); |
whence参数:
- SEEK_SET offset即为新的位置
- SEEK_CUR 目前位置+offset
- SEEK_END end+offset
因此 两个阶段就是flag的两个部分
STACK |
---|
rbp1 |
ret1 |
ret2 |
attention: 不需要填入rbp 因为每次都是从函数开头开始调用的
from pwn import * |
level3
研究了半天
一开始我是打算跳过每一段函数前面的检测部分,但是这有问题
如果我跳过这些部分,我无法使得rbp放在合适的位置,那么这个函数leave ret的时候,
栈的状态和当前的就大不相同,程序很有可能崩溃。
于是用ROPgadget找了下,找到了一个gadget
有了这个就很轻松了,我们构造栈上数据如下
stack |
---|
rbp |
poprdi |
p64(1) |
fun1 |
(类推)
from pwn import * |
level 4
这题相当于一个一个自由的构造rop链的题
我们几乎拥有所有的pop [register] ret 。
还拥有一个syscall
还拥有栈的地址。
我的想法是运行掉execve(‘/bin/sh\x00’,[‘-p’])
要实现这个 我们需要:
option | addr |
---|---|
pop rdi ; ret | 0x401fd5 |
pop rax ; ret | 0x401fad |
pop rsi ; ret | 0x401fcd |
pop rdx ; ret | 0x401fa5 |
syscall ; ret | 0x401fb5 |
栈上需要构造如下内容。
name | stack |
---|---|
buf: | ‘/bin/sh\x00’ |
[buf+32] | |
[buf+40] | |
p64(0) | |
‘sh\x00’ | |
‘-p\x00’ | |
…. | …………. |
rbp: | trash |
ret: | pop rdi ; ret |
[buf] | |
pop rsi ; ret | |
[buf+8] | |
pop rdx ; ret | |
p64(0) | |
pop rax ; ret | |
syscall ; ret | |
from pwn import * |
这题我有个不太理解的点就是,必须要把指令也sendline出去 不能直接interactive 真是奇怪。
attention: 第二关一部分会被重新覆盖 注意跳过那一部分即可 不过有个更
简单的方法 就是把-p改成占四个字节
payload=b'/bin/sh\x00'+p64(buf+32)+p64(buf+40)+p64(0)+b'sh'.ljust(8,b'\x00')+b'-p'.ljust(4,b'\x00')+\ |
level 5
这一关写完之后 发现上一关为啥不能直接interactive了 因为我用了输入流重定向… 请不要使用这种方式,这甚至会导致你无法使用gdb.debug 和 gdb.attach
这一关没有了栈的地址,但无所谓,我们可以写到bss段里的可写段 ,注意不要把stdin 和stdout覆盖到。(不过事实上是可以覆盖到的,只要没有用到依赖他们的函数)
还有一个要注意的点是argv[0]是文件名。我原来没意识到这点,构造了一个/bin/cat /flag 结果纳闷了半天怎么没输出。
具体就不多说了
from pwn import * |
第二关 bss段的几乎不可用 因为一开始就放了个stdout,后面的puts马上又会用到
那就考虑一下能不能覆盖前面的部分,也就是got.plt和data
特意控制不覆盖到puts 因为后面要用
事实证明这是可行的。
level 6
给出了sendfile 和open函数 并且还在同一个函数里面,但是貌似不能直接用。我的想法是在rop链上填plt表地址 先看看能不能行再说
这里对于open的返回值 个人认为可以直接猜测是3
如果不放心 有一个gadget可用
0x0000000000401249 : push rax ; add dil, dil ; loopne 0x4012b5 ; nop ; ret
这个gadget中的loopne在ecx为零的时候是没有作用的
我决定先试一下猜测3能不能成功
结果证明我的猜测是正确的
from pwn import * |
level 7
发现无法在本地运行并不是libc版本不同 而是没有安装一个库叫capstone
sudo apt install libcapstone-dev |
即可安装
这题其实用one_gadget基本上可以直接过 但是为了练习我决定还是自己构造ROP链
题目没有给我们对应的libc.so.6版本
可以通过ldd指令,找到程序使用的libc的文件地址,然后下载到本地,就可以用了
为了书写方便 尽量少用到libc里面的gadget 原来文件有的多用
addr | instruction |
---|---|
0x4022c3 | pop rdi;ret |
0x4022c1 | pop rsi; pop r15;ret |
0x119431l | pop rdx; pop r12;ret |
0x052290l | system() |
l代表libc中 需要带上base
这题system函数貌似会直接降权,我们需要找其他方案
我决定用之前的open sendfile方案
经过重写之后,新增的gadget如下
addr | instruction |
---|---|
0x10df00l | open |
0x1131C0l | sendfile |
0x10257dl | pop rdx ; pop rcx ; pop rbx ;ret |
from pwn import * |
level 8
got表里面的在函数调用过一次之后会存储实际的地址。
并且我们可以控制rdi。这样就可以获得libc里面的值 再调用level7相同的脚本即可
gadget:
address | insturction |
---|---|
0x401fd3 | pop rdi ;ret |
0x401fd1 | pop rsi ; pop r15 ;ret |
0x10257dl | pop rdx ; pop rcx ; pop rbx ;ret |
0x401140 | read |
0x401110 | puts(in plt) |
0x401D29 | challenge |
0x084420l | puts(in libc) |
0x1131c0l | sendfile |
0x10df00l | open |
puts在got表中地址是0x404028 data地址是0x404078
泄露之后重新运行main再次运行
其他和level7一致
from pwn import * |
level 9
栈迁移,题目允许我们在retaddr开始写入3个64位数
考虑覆盖第一个返回地址为 pop rbp ret(从leave return),随后接上bss段。然后再leave return
事实上 rbp的位置并不是非常重要 从我们的ROP链来看 我们用到的要么是直接call一个函数 要么是 pop ret,这两个其实都不会破坏栈的状态。
因此如果有pop rsp也不是不行 比如 0x00000000004014b4 : pop rsp ; pop r13 ; pop rbp ; ret
但这题给了三个 就不管了
这时候还需要考虑到第二次return challenge的时候栈空间的问题,一个方便的做法就是第二次的时候前三个覆盖和return adress后面相同的内容,其他的就不管,因为第二次我们压根不需要栈迁移。
这一题还有个有意思的事情就是栈对齐
如果栈没有16字节对齐 会导致segmentation fault
利用gdb会发现 在printf里面的代码中 用到了xmm寄存器。
今后构造的时候也是 最好控制栈对齐。
from pwn import * |
level 10
栈迁移 修改了返回地址之后,main函数的栈就会跑到覆盖之后的位置,那么之后main再leave就会被我们利用
因此,题目泄露给我们buf的地址,我们要填入的就是buf-16,这样第一次leave return ,rbp就变成了buf-16 然后再leave return就返回到buf-8指向的函数。
这时候要注意 如果返回到puts 会导致程序segmentation fault 因为返回之后 rsp在rbp下方,这就会导致puts覆盖了我们构造的栈帧。
因此我们还需要跳过puts函数,只要覆盖一个字节即可,因为两者的地址相差很小。
from pwn import * |
第二关非常有意思,如果你不仔细看的话,每次都会出现这个报错
babyrop_level10.1:
再一看代码,会发现覆盖一个字节是不够的
可以看到地址整好卡0x100边界处 所以进位了。
因此用覆盖两个字节的方法即可解决
level 11
区别在哪 😓
level 12
目前没想到什么很好的策略 打算是直接和额前面差不多 爆破出libc中leave return地址
先用gdb打开查一下加载的基地址
找出一个可能的leave ret的地址之后 就一直用这个爆破
我试出来一个0x3fd8c8
其他的事实上和前面的差不多
注意:爆破的时候一定要加上break!!!不然你就会一直纳闷自己哪里错了
level 13
先泄露canary 然后覆盖返回地址重新掉调用一遍challenge,再泄露libc基地址。然后就可以随便构造了
from pwn import * |
level 14
基本思路:
用爆破来确定代码段和canary 只需要检查返回中是否有 b’Goodbye’ 即可
这最坏也只需要16*256次就能解决。
from pwn import * |
** 有很小的概率libc会随机到\x0a和\x00自己 这时候重启一下再运行就行了
level 15
这题爆破libc地址其实不难 我们控制函数重新返回到main然后recv检测是否eof即可
难点在于我们的ropchain很难利用到/flag这个字符串
同时onegadget和ropchain生成的binsh利用方式都被禁止了(没有-p)
那应该怎么办呢,我们可以把/flag软链接到一个libc里面有的字符串的名字 再通过软连接修改/flag的权限
在检测是否eof的时候,会有个问题就是没有eof会被漏掉 然后他的进程又存在 这样整个连接就会卡住 ,因为没有给他输入。
解决方法是手动kill
还有个方法是用pwntools获取pid然后kill 但是我懒得搞
from pwn import * |
有时候一次不成功 就多试几次 2333