其他
HITB CTF 2018 gundam分析
看雪论坛作者ID:dolphindiv
1
gundam结构分析
通过逆向分析,可以知道,gundam结构如下:
struct gundam{
uint32_t flag;
char *name;
char type[24];
}gundam;
struct gundam *factory[9]
s = malloc(0x28) #1
buf = malloc(0x100uLL); #2
创建8个gundam,再释放,可以看到前7个进入tcache,最后一个进入unsortedbin:
(二)gundam中的关键函数
__int64 sub_B7D()
{
int v1; // [rsp+0h] [rbp-20h] BYREF
unsigned int i; // [rsp+4h] [rbp-1Ch]
void *s; // [rsp+8h] [rbp-18h]
void *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
s = 0LL;
buf = 0LL;
if ( (unsigned int)dword_20208C <= 8 )
{
s = malloc(0x28uLL);
memset(s, 0, 0x28uLL);
buf = malloc(0x100uLL);
if ( !buf )
{
puts("error !");
exit(-1);
}
printf("The name of gundam :");
read(0, buf, 0x100uLL);
*((_QWORD *)s + 1) = buf;
printf("The type of the gundam :");
__isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > 2 )
{
puts("Invalid.");
exit(0);
}
strcpy((char *)s + 16, &aFreedom[20 * v1]);
*(_DWORD *)s = 1;
for ( i = 0; i <= 8; ++i )
{
if ( !factory[i] )
{
factory[i] = s;
break;
}
}
++dword_20208C;
}
return 0LL;
}
__int64 sub_D32()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
if ( dword_20208C ) //gundam数目
{
printf("Which gundam do you want to Destory:");
__isoc99_scanf("%d", &v1);
if ( v1 > 8 || !factory[v1] )
{
puts("Invalid choice");
return 0LL;
}
*(_DWORD *)factory[v1] = 0;
free(*(void **)(factory[v1] + 8LL));
}
else
{
puts("No gundam");
}
return 0LL;
}
unsigned __int64 sub_E22()
{
unsigned int i; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
for ( i = 0; i <= 8; ++i )
{
if ( qword_2020A0[i]&&!*(_DWORD *)qword_2020A0[i] )
{
free((void *)qword_2020A0[i]);
qword_2020A0[i] = 0LL;
--dword_20208C;
}
}
puts("Done!");
return __readfsqword(0x28u) ^ v2;
}
2
内存泄露
前面分析,函数构造gundam时,对于用户的输入字符串没有进行处理,即末尾增加截断字符“\x00”,而申请的堆空间有0x100字节,没有初始化,导致存在泄露信息的可能。
通过vmmap命令,可以看到heap的开始位置为0x55e22cd98000:
认真观察可以发现,地址0x55e22cd980c8处的指针0x000055e22cd98a10正好就是tcache bin头结点指向的第一个chunk,也就是最后被释放加入tcache的chunk(第7个chunk):
通过连续申请创建8个gundam,第7个gundam的地址加上2个chunk的大小,就是第8个chunk的地址。
使用x/26gx 0x000055e22cd98a10+0x30+0x110-0x10,可以看到第8个chunk的地址为0x55e22cd98b40,其bk和fd指针都指向0x7f566befac78(unsortedbin头结点,是main_arena+88,也是泄露的地址):
知道了泄露地址,我们再查找libc基地址,紧挨着heap的libc-2.26.so对应的地址0x7f566bb4f000就是libc基地址:
3
双重释放漏洞
对于前面我们创建的8个gundam,我们先依次destroy序号为2、1、0的gundam,可以看到有3个空闲的chunk进入tcache:
再次destroy序号为0的gundam,可以看到:
当free一个chunk时,该chunk的fd指针会指向tcache bin中的第一个chunk,然后tcache bin的头结点指向刚free掉的chunk,依次类推,保证tcache bin的头结点指向的chunk永远是最后freed的。
当再次destroy 0号gundam时,对应的chunk(已在tcache中)会再次被free,并修改fd指针,从而使chunk 0的fd指向自身,从而完成double free。
通过前面计算出的泄露地址与libc基地址的偏移,可以在获得泄露地址的前提
再次构造gundam,分别以free_hook_addr、'/bin/sh\x00'字符串和system_addr作为name参数:
1、构造第1个gundam时,参数为free_hook_addr的地址,glibc会从tcache bin 中找到空闲的chunk,此时为chunk0,tcache_get 操作会将tcache->entries[tc_idx]指向的第一个chunk返回,并使entries[tc_idx]指向下一个chunk。
2、构造第2个gundam时,参数为'/bin/sh\x00'字符串,glibc会从tcache bin 中找到空闲的chunk,此时仍然为chunk0,tcache_get 操作会使chunk0返回,使tcache->entries[tc_idx]指向前面chunk0的fd,即free_hook_addr,同时使chunk0的fd改写为'/bin/sh\x00'字符串;
3、构造第3个gundam时,参数为system_addr地址,glibc会从tcache bin 中找到空闲的chunk,因为此时tcache->entries[tc_idx]指向free_hook_addr,就在free_hook_addr处写上system_addr。
通过以上3步操作,成功使system_addr与free_hook绑定,再执行1个destroy操作,即可启动system。
因此,选择destroy含有'/bin/sh\x00'字符串的1号gundam,成功执行shell:
4
总结遇到的各种坑
看雪ID:dolphindiv
https://bbs.pediy.com/user-home-717768.htm
# 往期推荐
5. 新人PWN入坑总结
6. 新人PWN堆Heap总结
球分享
球点赞
球在看
点击“阅读原文”,了解更多!