-
-
[原创]【NKCTF】babyHeap-Off by one&Tcache Attack
-
发表于: 2023-3-26 23:14 11907
-
保护全开,CRUD全都提供了,但是没有明显可利用的地方,问题出在edit()
函数输入内容时使用的my_read()
函数,看名字都很可移:),其中根据创建时输入的chunkSize进行循环,由于逻辑错误导致会多读取一个字节,可以进行溢出
已知在向堆输入内容时可进行一个字节的溢出,所以当我们申请0x?8
个字节时便可实际输入0x?9
篡改下一个chunkSize
,于是我们可以进行如下利用
此时chunk#1
被重新至于原位且此时记录大小为0xE8
,真实大小依旧为0x68
,可随意篡改,泄露chunk#2
,于是进行以下利用
此时我们已经实现了 libcBase Leak 和 Chunk Extends ,题目给出了的ibc版本为glibc-2.32
,由于应用了Tcache
,所以决定使用Tcache poisoning
进行任意地址写
Tcache
整体和FastBin
较相似,同采取 单链表 LIFO 进行管理,也既其FreeChunk
仅有fd
字段,区别是在利用时,FastBin
等都将对ChunkHeader
进行检查,而Tcache
的检查极其少,导致安全性低,其中值得注意的一项检查和一项保护措施分别如下
tcache_perthread_struct
结构体中的counts
字段记录了当前分类中存在多少个可分配FreeChunk
在glibc-2.31
后,对FastBin/Tcache
的fd
字段进行了混淆保护,当第一个FreeChunk
进入分类中时,&FreeChunk#0 >> 3
将作为key被保存并写入FreeChunk#0->fd
,而后该分类的每个FreeChunk->fd
在存取时都将与key
进行异或,所以若是我们要篡改fd
字段,则需要泄露key
在 glibc 2.29
之前,TcacheChunk
以 16 bytes
进行对齐,而在这之后,当申请的大小64<=size<=128
,则以16 bytes
进行对齐,其它情况下则以 8 bytes
进行对齐,所以若是申请的&FakeChunk
不符合对齐条件,需要-=8
以绕过检查
当tcache_perthread_struct->counts=0
时,则会直接跳过Tcache
从而去别处分配
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
choice;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v6;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v6
=
__readfsqword(
0x28u
);
init(argc, argv, envp);
while
(
1
)
{
while
(
1
)
{
menu();
read(
0
, buf,
4uLL
);
choice
=
strtol(buf,
0LL
,
10
);
if
( choice <
=
5
&& choice >
0
)
break
;
puts(
"Index error."
);
}
if
( choice
=
=
5
)
break
;
switch ( choice )
{
case
1
:
add();
break
;
case
2
:
delete();
break
;
case
3
:
edit();
break
;
default:
show();
break
;
}
}
puts(
"Goodbye and welcome to use it next time."
);
return
0
;
}
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
choice;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v6;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v6
=
__readfsqword(
0x28u
);
init(argc, argv, envp);
while
(
1
)
{
while
(
1
)
{
menu();
read(
0
, buf,
4uLL
);
choice
=
strtol(buf,
0LL
,
10
);
if
( choice <
=
5
&& choice >
0
)
break
;
puts(
"Index error."
);
}
if
( choice
=
=
5
)
break
;
switch ( choice )
{
case
1
:
add();
break
;
case
2
:
delete();
break
;
case
3
:
edit();
break
;
default:
show();
break
;
}
}
puts(
"Goodbye and welcome to use it next time."
);
return
0
;
}
unsigned __int64 add()
{
signed
int
index;
/
/
[rsp
+
Ch] [rbp
-
14h
]
int
size;
/
/
[rsp
+
10h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
14h
] [rbp
-
Ch] BYREF
unsigned __int64 v4;
/
/
[rsp
+
18h
] [rbp
-
8h
]
v4
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
index
=
strtol(buf,
0LL
,
10
);
if
( (unsigned
int
)index >
0xF
)
{
puts(
"Up to 16 nkctf notes can be created."
);
}
else
if
( note_array[index] )
{
puts(
"Sorry, this nkctf note has already been used."
);
}
else
{
printf(
"Enter the Size: "
);
read(
0
, buf,
4uLL
);
size
=
strtol(buf,
0LL
,
10
);
if
( size <
=
256
)
{
note_size[index]
=
size;
note_array[index]
=
malloc(note_size[index]);
if
( !note_array[index] || !note_size[index] )
my_error(
"malloc()"
,
0xFFFFFFFFLL
);
}
else
{
puts(
"This nkctf note is too big."
);
}
}
return
__readfsqword(
0x28u
) ^ v4;
}
unsigned __int64 add()
{
signed
int
index;
/
/
[rsp
+
Ch] [rbp
-
14h
]
int
size;
/
/
[rsp
+
10h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
14h
] [rbp
-
Ch] BYREF
unsigned __int64 v4;
/
/
[rsp
+
18h
] [rbp
-
8h
]
v4
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
index
=
strtol(buf,
0LL
,
10
);
if
( (unsigned
int
)index >
0xF
)
{
puts(
"Up to 16 nkctf notes can be created."
);
}
else
if
( note_array[index] )
{
puts(
"Sorry, this nkctf note has already been used."
);
}
else
{
printf(
"Enter the Size: "
);
read(
0
, buf,
4uLL
);
size
=
strtol(buf,
0LL
,
10
);
if
( size <
=
256
)
{
note_size[index]
=
size;
note_array[index]
=
malloc(note_size[index]);
if
( !note_array[index] || !note_size[index] )
my_error(
"malloc()"
,
0xFFFFFFFFLL
);
}
else
{
puts(
"This nkctf note is too big."
);
}
}
return
__readfsqword(
0x28u
) ^ v4;
}
unsigned __int64 delete()
{
unsigned
int
v1;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v3;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v3
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
v1
=
strtol(buf,
0LL
,
10
);
if
( v1 >
0xF
)
{
puts(
"Index error."
);
}
else
if
( note_array[v1] )
{
free((void
*
)note_array[v1]);
note_array[v1]
=
0LL
;
note_size[v1]
=
0
;
if
( note_array[v1] || note_size[v1] )
my_error(
"free()"
,
4294967294LL
);
}
else
{
puts(
"The nkctf note to be freed does not exist."
);
}
return
__readfsqword(
0x28u
) ^ v3;
}
unsigned __int64 delete()
{
unsigned
int
v1;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v3;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v3
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
v1
=
strtol(buf,
0LL
,
10
);
if
( v1 >
0xF
)
{
puts(
"Index error."
);
}
else
if
( note_array[v1] )
{
free((void
*
)note_array[v1]);
note_array[v1]
=
0LL
;
note_size[v1]
=
0
;
if
( note_array[v1] || note_size[v1] )
my_error(
"free()"
,
4294967294LL
);
}
else
{
puts(
"The nkctf note to be freed does not exist."
);
}
return
__readfsqword(
0x28u
) ^ v3;
}
unsigned __int64 edit()
{
unsigned
int
v1;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v3;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v3
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
v1
=
strtol(buf,
0LL
,
10
);
if
( v1 >
0xF
)
{
puts(
"Index error."
);
}
else
if
( note_array[v1] )
{
printf(
"Enter the content: "
);
my_read(note_array[v1], (unsigned
int
)note_size[v1]);
}
else
{
puts(
"The nkctf note to be filled was not created."
);
}
return
__readfsqword(
0x28u
) ^ v3;
}
unsigned __int64 edit()
{
unsigned
int
v1;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v3;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v3
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
v1
=
strtol(buf,
0LL
,
10
);
if
( v1 >
0xF
)
{
puts(
"Index error."
);
}
else
if
( note_array[v1] )
{
printf(
"Enter the content: "
);
my_read(note_array[v1], (unsigned
int
)note_size[v1]);
}
else
{
puts(
"The nkctf note to be filled was not created."
);
}
return
__readfsqword(
0x28u
) ^ v3;
}
unsigned __int64 show()
{
unsigned
int
v1;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v3;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v3
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
v1
=
strtol(buf,
0LL
,
10
);
if
( v1 >
0xF
)
{
puts(
"Index error."
);
}
else
if
( note_array[v1] )
{
puts((const char
*
)note_array[v1]);
}
else
{
puts(
"The nkctf note to be printed was not created."
);
}
return
__readfsqword(
0x28u
) ^ v3;
}
unsigned __int64 show()
{
unsigned
int
v1;
/
/
[rsp
+
0h
] [rbp
-
10h
]
char buf[
4
];
/
/
[rsp
+
4h
] [rbp
-
Ch] BYREF
unsigned __int64 v3;
/
/
[rsp
+
8h
] [rbp
-
8h
]
v3
=
__readfsqword(
0x28u
);
printf(
"Enter the index: "
);
read(
0
, buf,
4uLL
);
v1
=
strtol(buf,
0LL
,
10
);
if
( v1 >
0xF
)
{
puts(
"Index error."
);
}
else
if
( note_array[v1] )
{
puts((const char
*
)note_array[v1]);
}
else
{
puts(
"The nkctf note to be printed was not created."
);
}
return
__readfsqword(
0x28u
) ^ v3;
}
__int64 __fastcall my_read(__int64 a1,
int
a2)
{
unsigned
int
v3;
/
/
[rsp
+
10h
] [rbp
-
10h
]
int
i;
/
/
[rsp
+
14h
] [rbp
-
Ch]
for
( i
=
0
; i <
=
a2;
+
+
i )
/
/
逻辑错误导致
1
个字节的溢出
{
v3
=
read(
0
, (void
*
)(i
+
a1),
1uLL
);
if
(
*
(_BYTE
*
)(i
+
a1)
=
=
10
)
break
;
}
return
v3;
}
__int64 __fastcall my_read(__int64 a1,
int
a2)
{
unsigned
int
v3;
/
/
[rsp
+
10h
] [rbp
-
10h
]
int
i;
/
/
[rsp
+
14h
] [rbp
-
Ch]
for
( i
=
0
; i <
=
a2;
+
+
i )
/
/
逻辑错误导致
1
个字节的溢出
{
v3
=
read(
0
, (void
*
)(i
+
a1),
1uLL
);
if
(
*
(_BYTE
*
)(i
+
a1)
=
=
10
)
break
;
}
return
v3;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)