-
-
[原创]unsortbin attack分析与总结
-
发表于: 2020-10-3 11:06 6479
-
想要弄明白largebin的相关内容,首先要整理清楚unsortbin的分配利用方式。
可以看到,首先插入了0x602000(ptr1)然后在头部插入了0x6020f0(ptr3),看一下fd和bk指针如下:
ptr1的bk指向ptr3,ptr3的bk指向ptr1,并且是个循环链表,ptr1的fd指向unsortbin,ptr3的bk指向unsortbin。
然后我觉得有一句形如fd和bk很清楚:
fd pointer to the next free chunk; bk pointer to the pre free chunk
这里的pre和next是相对unsorbtin里的位置说的,比如说先free 1,先插入1,再free2,再插入2,那么unsortbin中的就是unsortbin->2->1。那么2->fd=&1;2->bk=unsorbtin | 1->fd=unsortbin;1->bk=&2
然后往出取的时候从链尾取(注意:当malloc时程序首先查找fastbin和smallbin。如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。)。总结一张如图:
其实核心就是这里:
可以看到,在fakechunk的fd位置(64位下是fake_chunk+0x10)写入了unsorted_bin的地址(unsorted_chunks (av))
ubattack有很多种用法,比如错位写一个0x7f,给fastbin attack打配合(打free_hook的时候能用到)等等;比如在没有del的情况下踩出一个main_arena的地址等等orz。
注意:unsorted_chunks (av)写入之后这个位置指向的是top chunk:
举个例子:
此时ubattack写入了0x00007ffff7dd1b78
可以看到,依次是:top chunk->last_remainder->fd->bk
glibc2.23,且只有new和edit,没有free和show。
Edit的时候没有检查index合法性,导致可以越界写。
在0x6020C0+0x108储存我们申请chunk的size。由于使用了strdup,所以分配的大小实际是由我们输入决定的,跟size无关,但是edit的时候是根据我们输入的size读入的。所以这里存在溢出
然后限制add的时候输入的index不能大于2,但是没关系我们一直add index=0就行,反正是strdup开chunk
打印依次是:Hello 6 2
https://ama2in9.top/2019/12/12/hhb/
https://l0ne1y.xyz/2020/05/10/Unsorted_Bin_Attack/#more
https://xz.aliyun.com/t/7251#toc-7
ptr1:
0x602010
ptr3:
0x602100
我们先free1再free3:
unsortbin:
0x6020f0
(size :
0xc0
) <
-
-
>
0x602000
(size :
0xc0
)
ptr1:
0x602010
ptr3:
0x602100
我们先free1再free3:
unsortbin:
0x6020f0
(size :
0xc0
) <
-
-
>
0x602000
(size :
0xc0
)
addr prev size status fd bk
0x602000
0x0
0xc0
Freed
0x7ffff7dd1b78
0x6020f0
0x6020f0
0x0
0xc0
Freed
0x602000
0x7ffff7dd1b78
addr prev size status fd bk
0x602000
0x0
0xc0
Freed
0x7ffff7dd1b78
0x6020f0
0x6020f0
0x0
0xc0
Freed
0x602000
0x7ffff7dd1b78
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
/
/
从unsortbin第一个chunk(队尾)开始顺着bk指针向前遍历
{
bck
=
victim
-
>bk;
/
/
bck是倒数第二个,victim是倒数第一个
/
/
size check
if
(__builtin_expect (victim
-
>size <
=
2
*
SIZE_SZ,
0
)
|| __builtin_expect (victim
-
>size > av
-
>system_mem,
0
))
malloc_printerr (check_action,
"malloc(): memory corruption"
,
chunk2mem (victim), av);
size
=
chunksize (victim);
/
/
取出最victim的size
/
*
If a small request,
try
to use last remainder
if
it
is
the
only chunk
in
unsorted
bin
. This helps promote locality
for
runs of consecutive small requests. This
is
the only
exception to best
-
fit,
and
applies only when there
is
no exact fit
for
a small chunk.
*
/
/
/
last remainder first
if
(in_smallbin_range (nb) &&
bck
=
=
unsorted_chunks (av) &&
victim
=
=
av
-
>last_remainder &&
/
/
如果我们申请的size在smallbin的范围,且last_remainder是unsortedbin中的唯一chunk,那么优先使用last_remainder(这个跟我们usortbin切割相关)
(unsigned
long
) (size) > (unsigned
long
) (nb
+
MINSIZE))
{
/
*
split
and
reattach remainder
*
/
remainder_size
=
size
-
nb;
remainder
=
chunk_at_offset (victim, nb);
/
/
cut
and
put the remained part back to unsorted
list
unsorted_chunks (av)
-
>bk
=
unsorted_chunks (av)
-
>fd
=
remainder;
av
-
>last_remainder
=
remainder;
remainder
-
>bk
=
remainder
-
>fd
=
unsorted_chunks (av);
if
(!in_smallbin_range (remainder_size))
{
remainder
-
>fd_nextsize
=
NULL;
remainder
-
>bk_nextsize
=
NULL;
}
set_head (victim, nb | PREV_INUSE |
(av !
=
&main_arena ? NON_MAIN_ARENA :
0
));
set_head (remainder, remainder_size | PREV_INUSE);
set_foot (remainder, remainder_size);
check_malloced_chunk (av, victim, nb);
void
*
p
=
chunk2mem (victim);
alloc_perturb (p, bytes);
/
/
return
to user
return
p;
}
/
*
remove
from
unsorted
list
*
/
/
/
unsorted
bin
attack在这里
/
/
unsorted_chunks (av)这里就是victim即最后一个chunk,当我们可以控制victim的bk指针时(即bck)
/
/
就可以往bck
-
>fd写入unsorted_chunks(av)即
*
(bck
+
0x10
)
=
unsorted(av)
/
/
要求bck是一个可写的地址
unsorted_chunks (av)
-
>bk
=
bck;
bck
-
>fd
=
unsorted_chunks (av);
/
*
Take now instead of binning
if
exact fit
*
/
/
/
如果请求的nb和victim的大小刚好符合,直接返回给用户
if
(size
=
=
nb)
{
set_inuse_bit_at_offset (victim, size);
if
(av !
=
&main_arena)
victim
-
>size |
=
NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);
void
*
p
=
chunk2mem (victim);
alloc_perturb (p, bytes);
return
p;
}
/
*
place chunk
in
bin
*
/
if
(in_smallbin_range (size))
{
victim_index
=
smallbin_index (size);
bck
=
bin_at (av, victim_index);
fwd
=
bck
-
>fd;
}
else
{
victim_index
=
largebin_index (size);
bck
=
bin_at (av, victim_index);
fwd
=
bck
-
>fd;
/
*
maintain large bins
in
sorted
order
*
/
if
(fwd !
=
bck)
{
/
*
Or with inuse bit to speed comparisons
*
/
size |
=
PREV_INUSE;
/
*
if
smaller than smallest, bypass loop below
*
/
assert
((bck
-
>bk
-
>size & NON_MAIN_ARENA)
=
=
0
);
if
((unsigned
long
) (size) < (unsigned
long
) (bck
-
>bk
-
>size))
{
fwd
=
bck;
bck
=
bck
-
>bk;
victim
-
>fd_nextsize
=
fwd
-
>fd;
victim
-
>bk_nextsize
=
fwd
-
>fd
-
>bk_nextsize;
fwd
-
>fd
-
>bk_nextsize
=
victim
-
>bk_nextsize
-
>fd_nextsize
=
victim;
}
else
{
assert
((fwd
-
>size & NON_MAIN_ARENA)
=
=
0
);
while
((unsigned
long
) size < fwd
-
>size)
{
fwd
=
fwd
-
>fd_nextsize;
assert
((fwd
-
>size & NON_MAIN_ARENA)
=
=
0
);
}
if
((unsigned
long
) size
=
=
(unsigned
long
) fwd
-
>size)
/
*
Always insert
in
the second position.
*
/
fwd
=
fwd
-
>fd;
else
{
victim
-
>fd_nextsize
=
fwd;
victim
-
>bk_nextsize
=
fwd
-
>bk_nextsize;
fwd
-
>bk_nextsize
=
victim;
victim
-
>bk_nextsize
-
>fd_nextsize
=
victim;
}
bck
=
fwd
-
>bk;
}
}
else
victim
-
>fd_nextsize
=
victim
-
>bk_nextsize
=
victim;
}
mark_bin (av, victim_index);
victim
-
>bk
=
bck;
victim
-
>fd
=
fwd;
fwd
-
>bk
=
victim;
bck
-
>fd
=
victim;
#define MAX_ITERS 10000
if
(
+
+
iters >
=
MAX_ITERS)
break
;
}
while
((victim
=
unsorted_chunks (av)
-
>bk) !
=
unsorted_chunks (av))
/
/
从unsortbin第一个chunk(队尾)开始顺着bk指针向前遍历
{
bck
=
victim
-
>bk;
/
/
bck是倒数第二个,victim是倒数第一个
/
/
size check
if
(__builtin_expect (victim
-
>size <
=
2
*
SIZE_SZ,
0
)
|| __builtin_expect (victim
-
>size > av
-
>system_mem,
0
))
malloc_printerr (check_action,
"malloc(): memory corruption"
,
chunk2mem (victim), av);
size
=
chunksize (victim);
/
/
取出最victim的size
/
*
If a small request,
try
to use last remainder
if
it
is
the
only chunk
in
unsorted
bin
. This helps promote locality
for
runs of consecutive small requests. This
is
the only
exception to best
-
fit,
and
applies only when there
is
no exact fit
for
a small chunk.
*
/
/
/
last remainder first
if
(in_smallbin_range (nb) &&
bck
=
=
unsorted_chunks (av) &&
victim
=
=
av
-
>last_remainder &&
/
/
如果我们申请的size在smallbin的范围,且last_remainder是unsortedbin中的唯一chunk,那么优先使用last_remainder(这个跟我们usortbin切割相关)
(unsigned
long
) (size) > (unsigned
long
) (nb
+
MINSIZE))
{
/
*
split
and
reattach remainder
*
/
remainder_size
=
size
-
nb;
remainder
=
chunk_at_offset (victim, nb);
/
/
cut
and
put the remained part back to unsorted
list
unsorted_chunks (av)
-
>bk
=
unsorted_chunks (av)
-
>fd
=
remainder;
av
-
>last_remainder
=
remainder;
remainder
-
>bk
=
remainder
-
>fd
=
unsorted_chunks (av);
if
(!in_smallbin_range (remainder_size))
{
remainder
-
>fd_nextsize
=
NULL;
remainder
-
>bk_nextsize
=
NULL;
}
set_head (victim, nb | PREV_INUSE |
(av !
=
&main_arena ? NON_MAIN_ARENA :
0
));
set_head (remainder, remainder_size | PREV_INUSE);
set_foot (remainder, remainder_size);
check_malloced_chunk (av, victim, nb);
void
*
p
=
chunk2mem (victim);
alloc_perturb (p, bytes);
/
/
return
to user
return
p;
}
/
*
remove
from
unsorted
list
*
/
/
/
unsorted
bin
attack在这里
/
/
unsorted_chunks (av)这里就是victim即最后一个chunk,当我们可以控制victim的bk指针时(即bck)
/
/
就可以往bck
-
>fd写入unsorted_chunks(av)即
*
(bck
+
0x10
)
=
unsorted(av)
/
/
要求bck是一个可写的地址
unsorted_chunks (av)
-
>bk
=
bck;
bck
-
>fd
=
unsorted_chunks (av);
/
*
Take now instead of binning
if
exact fit
*
/
/
/
如果请求的nb和victim的大小刚好符合,直接返回给用户
if
(size
=
=
nb)
{
set_inuse_bit_at_offset (victim, size);
if
(av !
=
&main_arena)
victim
-
>size |
=
NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);
void
*
p
=
chunk2mem (victim);
alloc_perturb (p, bytes);
return
p;
}
/
*
place chunk
in
bin
*
/
if
(in_smallbin_range (size))
{
victim_index
=
smallbin_index (size);
bck
=
bin_at (av, victim_index);
fwd
=
bck
-
>fd;
}
else
{
victim_index
=
largebin_index (size);
bck
=
bin_at (av, victim_index);
fwd
=
bck
-
>fd;
/
*
maintain large bins
in
sorted
order
*
/
if
(fwd !
=
bck)
{
/
*
Or with inuse bit to speed comparisons
*
/
size |
=
PREV_INUSE;
/
*
if
smaller than smallest, bypass loop below
*
/
assert
((bck
-
>bk
-
>size & NON_MAIN_ARENA)
=
=
0
);
if
((unsigned
long
) (size) < (unsigned
long
) (bck
-
>bk
-
>size))
{
fwd
=
bck;
bck
=
bck
-
>bk;
victim
-
>fd_nextsize
=
fwd
-
>fd;
victim
-
>bk_nextsize
=
fwd
-
>fd
-
>bk_nextsize;
fwd
-
>fd
-
>bk_nextsize
=
victim
-
>bk_nextsize
-
>fd_nextsize
=
victim;
}
else
{
assert
((fwd
-
>size & NON_MAIN_ARENA)
=
=
0
);
while
((unsigned
long
) size < fwd
-
>size)
{
fwd
=
fwd
-
>fd_nextsize;
assert
((fwd
-
>size & NON_MAIN_ARENA)
=
=
0
);
}
if
((unsigned
long
) size
=
=
(unsigned
long
) fwd
-
>size)
/
*
Always insert
in
the second position.
*
/
fwd
=
fwd
-
>fd;
else
{
victim
-
>fd_nextsize
=
fwd;
victim
-
>bk_nextsize
=
fwd
-
>bk_nextsize;
fwd
-
>bk_nextsize
=
victim;
victim
-
>bk_nextsize
-
>fd_nextsize
=
victim;
}
bck
=
fwd
-
>bk;
}
}
else
victim
-
>fd_nextsize
=
victim
-
>bk_nextsize
=
victim;
}
mark_bin (av, victim_index);
victim
-
>bk
=
bck;
victim
-
>fd
=
fwd;
fwd
-
>bk
=
victim;
bck
-
>fd
=
victim;
#define MAX_ITERS 10000
if
(
+
+
iters >
=
MAX_ITERS)
break
;
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!