-
-
[HTB]Dream Diary: Chapter 1
-
发表于: 2024-10-25 10:13 2777
-
每日一题计划:督促自己练习,每日分享一题的练习!想一起刷题咱们可以一起练练练,以及,相互监督!
今天是第1天,希望能坚持下去
Hint: Xenial Xerus
这是ubuntu16.04lts的代号,意味着适用2.23的libc
菜单题:
运行起来是这样的菜单:
选项1:Allocate
申请内存,指定大小,填充数据,无溢出
选项2:edit:
编辑操作使用的长度来自strlen的结果,假定数据是满的,strlen会计算到next chunk size字段,导致1字节溢出
选项3:del:
这里会清空指针,没有UAF和Double-free了
第一步:指针数组位于0x0000000006020c0,上面就是got表,存在fake fastbin chunk 的条件,申请走fake fastbin chunk,从而控制指针数组
fake chunk:
打hook需要完成libc地址泄露,got可修改这个条件就可以用上了,能控制指针数组,就能任意地址写,那么就暂时不需要free了
第二步,打got,将free改成puts先用用
指针数组是可控的,那这随便就泄露出libc地址了,接下来就是打hook环节
第三步:打hook,drop shell
默认最大的fastbin chunk是0x68申请出来的,0x78申请出来的会进入unsortedbin
这里申请4个chunk,其中chunk0溢出1字节,将chunk1的大小修改为能刚好覆盖chunk2,chunk3用于分隔top chunk
此时的堆:
得到一个被unsortedbin chunk覆盖的fastbin chunk
接下来,修改fd指向got那里创造出来的fake chunk
申请一个0xe8把unsortedbin 完整申请走,向fastbin chunk.next写入fake chunk address
申请走之后,覆盖ptr指针数组
这里前8字节写入杂乱数据,是为了保留这个区域可以再次被修改,因为edit选项是基于对目标地址strlen获取长度来写入数据的
然后要保留一份方便再次修改的该数组
修改elf.got.free为elf.plt.puts,拿到libc地址泄露
最后drop shell的方法有很多,最简单的方法就是直接通过elf.got来劫持函数执行
有了libc地址泄露,可以劫持atoi为system,然后在输入选项的时候,输入bash字符即可(因为调用atoi之前的read函数接受6个字符的输入)
或者使用one_gadget也行,或者打io也行不过就比较麻烦了
本练习相关的知识:
off by one 创造重叠块
Fastbin Attack
Hijack Elf GOT
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
void
__fastcall __noreturn main(
int
a1,
char
**a2,
char
**a3)
{
int
choose;
// eax
__int64
buf[2];
// [rsp+0h] [rbp-10h] BYREF
buf[1] = __readfsqword(0x28u);
buf[0] = 0LL;
setvbuf
(stdout, 0LL, 2, 0LL);
setvbuf
(stdin, 0LL, 2, 0LL);
setvbuf
(stderr, 0LL, 2, 0LL);
while
( 1 )
{
while
( 1 )
{
menu();
read(0, buf, 4uLL);
choose =
atoi
((
const
char
*)buf);
if
( choose != 2 )
break
;
edit(buf, buf);
// 2
}
if
( choose > 2 )
{
if
( choose == 3 )
{
del(buf, buf);
// 3
}
else
{
if
( choose == 4 )
exit
(0);
LABEL_13:
puts
(
"Invalid choice!"
);
}
}
else
{
if
( choose != 1 )
goto
LABEL_13;
add(buf, buf);
// 1
}
}
}
void
__fastcall __noreturn main(
int
a1,
char
**a2,
char
**a3)
{
int
choose;
// eax
__int64
buf[2];
// [rsp+0h] [rbp-10h] BYREF
buf[1] = __readfsqword(0x28u);
buf[0] = 0LL;
setvbuf
(stdout, 0LL, 2, 0LL);
setvbuf
(stdin, 0LL, 2, 0LL);
setvbuf
(stderr, 0LL, 2, 0LL);
while
( 1 )
{
while
( 1 )
{
menu();
read(0, buf, 4uLL);
choose =
atoi
((
const
char
*)buf);
if
( choose != 2 )
break
;
edit(buf, buf);
// 2
}
if
( choose > 2 )
{
if
( choose == 3 )
{
del(buf, buf);
// 3
}
else
{
if
( choose == 4 )
exit
(0);
LABEL_13:
puts
(
"Invalid choice!"
);
}
}
else
{
if
( choose != 1 )
goto
LABEL_13;
add(buf, buf);
// 1
}
}
}
+------------------------------+
| Dream Diary |
+------------------------------+
| [1] Allocate |
| [2] Edit |
| [3] Delete |
| [4] Exit |
+------------------------------+
>>
+------------------------------+
| Dream Diary |
+------------------------------+
| [1] Allocate |
| [2] Edit |
| [3] Delete |
| [4] Exit |
+------------------------------+
>>
unsigned
__int64
sub_4009A8()
{
int
i;
// [rsp+4h] [rbp-1Ch]
size_t
size;
// [rsp+8h] [rbp-18h]
char
nptr[8];
// [rsp+10h] [rbp-10h] BYREF
unsigned
__int64
v4;
// [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
*(_QWORD *)nptr = 0LL;
for
( i = 0; ; ++i )
{
if
( i > 15 )
// 最多15个
{
puts
(
"Too many notes!"
);
return
__readfsqword(0x28u) ^ v4;
}
if
( !(&ptr)[i] )
break
;
}
printf
(
"\nSize: "
);
read_(nptr, 6LL);
size =
atoi
(nptr);
(&ptr)[i] = (
char
*)
malloc
(size);
// 申请内存
if
( !(&ptr)[i] )
{
puts
(
"Malloc error!"
);
exit
(-1);
}
printf
(
"Data: "
);
read_((&ptr)[i], size);
// 写入数据,无溢出
puts
(
"Success!"
);
return
__readfsqword(0x28u) ^ v4;
}
unsigned
__int64
sub_4009A8()
{
int
i;
// [rsp+4h] [rbp-1Ch]
size_t
size;
// [rsp+8h] [rbp-18h]
char
nptr[8];
// [rsp+10h] [rbp-10h] BYREF
unsigned
__int64
v4;
// [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
*(_QWORD *)nptr = 0LL;
for
( i = 0; ; ++i )
{
if
( i > 15 )
// 最多15个
{
puts
(
"Too many notes!"
);
return
__readfsqword(0x28u) ^ v4;
}
if
( !(&ptr)[i] )
break
;
}
printf
(
"\nSize: "
);
read_(nptr, 6LL);
size =
atoi
(nptr);
(&ptr)[i] = (
char
*)
malloc
(size);
// 申请内存
if
( !(&ptr)[i] )
{
puts
(
"Malloc error!"
);
exit
(-1);
}
printf
(
"Data: "
);
read_((&ptr)[i], size);
// 写入数据,无溢出
puts
(
"Success!"
);
return
__readfsqword(0x28u) ^ v4;
}
unsigned
__int64
edit()
{
unsigned
int
v1;
// [rsp+4h] [rbp-1Ch]
size_t
v2;
// [rsp+8h] [rbp-18h]
__int64
buf;
// [rsp+10h] [rbp-10h] BYREF
unsigned
__int64
v4;
// [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
buf = 0LL;
printf
(
"Index: "
);
read(0, &buf, 4uLL);
v1 =
atoi
((
const
char
*)&buf);
if
( v1 < 0x10 )
{
if
( (&ptr)[v1] )
{
v2 =
strlen
((&ptr)[v1]);
// 计算原本的长度
printf
(
"Data: "
);
read_((&ptr)[v1], v2);
// 写入数据,长度上限是原本的长度
puts
(
"Done!"
);
}
else
{
puts
(
"No UAF for you!"
);
}
}
else
{
puts
(
"Out of bounds!"
);
}
return
__readfsqword(0x28u) ^ v4;
}
unsigned
__int64
edit()
{
unsigned
int
v1;
// [rsp+4h] [rbp-1Ch]
size_t
v2;
// [rsp+8h] [rbp-18h]
__int64
buf;
// [rsp+10h] [rbp-10h] BYREF
unsigned
__int64
v4;
// [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
buf = 0LL;
printf
(
"Index: "
);
read(0, &buf, 4uLL);
v1 =
atoi
((
const
char
*)&buf);
if
( v1 < 0x10 )
{
if
( (&ptr)[v1] )
{
v2 =
strlen
((&ptr)[v1]);
// 计算原本的长度
printf
(
"Data: "
);
read_((&ptr)[v1], v2);
// 写入数据,长度上限是原本的长度
puts
(
"Done!"
);
}
else
{
puts
(
"No UAF for you!"
);
}
}
else
{
puts
(
"Out of bounds!"
);
}
return
__readfsqword(0x28u) ^ v4;
}
unsigned
__int64
del()
{
unsigned
int
v1;
// [rsp+Ch] [rbp-14h]
__int64
buf;
// [rsp+10h] [rbp-10h] BYREF
unsigned
__int64
v3;
// [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
buf = 0LL;
printf
(
"Index: "
);
read(0, &buf, 4uLL);
v1 =
atoi
((
const
char
*)&buf);
if
( v1 < 0x10 )
{
if
( (&ptr)[v1] )
{
free
((&ptr)[v1]);
// 释放内存
(&ptr)[v1] = 0LL;
// 指针清空,不存在UAF和double-free
puts
(
"Done!"
);
}
else
{
puts
(
"No double-free for you!"
);
}
}
else
{
puts
(
"Out of bounds!"
);
}
return
__readfsqword(0x28u) ^ v3;
}
unsigned
__int64
del()
{
unsigned
int
v1;
// [rsp+Ch] [rbp-14h]
__int64
buf;
// [rsp+10h] [rbp-10h] BYREF
unsigned
__int64
v3;
// [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
buf = 0LL;
printf
(
"Index: "
);
read(0, &buf, 4uLL);
v1 =
atoi
((
const
char
*)&buf);
if
( v1 < 0x10 )
{
if
( (&ptr)[v1] )
{
free
((&ptr)[v1]);
// 释放内存
(&ptr)[v1] = 0LL;
// 指针清空,不存在UAF和double-free
puts
(
"Done!"
);
}
else
{
puts
(
"No double-free for you!"
);
}
}
else
{
puts
(
"Out of bounds!"
);
}
return
__readfsqword(0x28u) ^ v3;
}
pwndbg> dq 0x602010 30
0000000000602010 00007ff9258de150 00007ff9257aa31a
0000000000602020 00007ff92579926f 00007ff9257af1e0
0000000000602030 0000000000400706 00007ff925780914
0000000000602040 00007ff9258084c0 00007ff9257532e1
0000000000602050 00007ff9257a9cd3 00007ff9257999c5
0000000000602060 00007ff925765fcb 0000000000400776
0000000000602070 0000000000000000 0000000000000000
0000000000602080 00007ff9258c3620 0000000000000000
0000000000602090 00007ff9258c28e0 0000000000000000
00000000006020a0 00007ff9258c3540 0000000000000000
00000000006020b0 0000000000000000 0000000000000000
00000000006020c0 0000000002458010 0000000000000000
00000000006020d0 0000000000000000 0000000002458180
00000000006020e0 0000000000000000 0000000000000000
00000000006020f0 0000000000000000 0000000000000000
pwndbg> dq 0x602010 30
0000000000602010 00007ff9258de150 00007ff9257aa31a
0000000000602020 00007ff92579926f 00007ff9257af1e0
0000000000602030 0000000000400706 00007ff925780914
0000000000602040 00007ff9258084c0 00007ff9257532e1
0000000000602050 00007ff9257a9cd3 00007ff9257999c5
0000000000602060 00007ff925765fcb 0000000000400776
0000000000602070 0000000000000000 0000000000000000
0000000000602080 00007ff9258c3620 0000000000000000
0000000000602090 00007ff9258c28e0 0000000000000000
00000000006020a0 00007ff9258c3540 0000000000000000
00000000006020b0 0000000000000000 0000000000000000
00000000006020c0 0000000002458010 0000000000000000
00000000006020d0 0000000000000000 0000000002458180
00000000006020e0 0000000000000000 0000000000000000
00000000006020f0 0000000000000000 0000000000000000
Fake chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x60209d
prev_size: 0xf9258c3540000000
size: 0x78 (with flag bits: 0x7f)
fd: 0x00
bk: 0x00
fd_nextsize: 0x2458010000000
bk_nextsize: 0x00
Fake chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x60209d
prev_size: 0xf9258c3540000000
size: 0x78 (with flag bits: 0x7f)