环境:ubuntu16.04 glibc版本:2.23
按照惯例用checksec查看下保护
可以看到除了canary保护,其余保护全开 再用IDA进行代码审计
main函数
为了后续的代码审计,我这里给函数名都自定义了一个名字。 前几行代码定义了几个变量,然后关闭了缓冲区。 后面调用各种函数,然后进入while循环。 整个main函数没什么好看的。
welcome函数
打印了一行字,也没啥好看的
author_name函数
主要功能是往off_202018
写入大小不超过32的数据 这里注意off_202018
是在bss段的
my_read(_BYTE *a1, int a2)函数
开始定义了一个变量i 然后进入if语句,判断a2(也可以理解为输入大小)是否合法,不合法即返回0 接着进入循环语句: 第一个if:每次往a1中写入一个字节,读取失败则返回1 第二个if:判断当前是否为换行符,10 == \x0a =='\n' 然后自增a1 最后判断是否读完,然后结束循环 循环结束后,将a1最后一个字节设为0,最后返回0
menu函数
普普通通的一个菜单
create函数
定义了几个变量: v1,存name和description的size *ptr,name所在chunk的指针 v5,description所在chunk的指针 v3,整个book所在chunk的指针 v2,可以理解为book数组的索引 整个函数流程就是:写入name_size -> 写入name -> 写入description_size -> 写入description -> 最后把id name指针 description指针 description大小写入到books结构体中 注意,这里存book数组的指针为off_202010
,与off_202018
(即author_name)刚好相距0x20 所以当author_name大小刚好为0x20时,会溢出到off_202010
,存在off-by-one漏洞,因为最后溢出了一个'\x00'
edit函数
先写入函数id 如果id大于0,循环book数组,找到id对应的书, 如果i == 20,就报错,说明整个book数组最多可以存20个book结构体,否则覆写之前的description
print函数
主要功能就是打印所有book数组中的book结构体的信息
到这里代码审计就先暂停一下,理一下思路 回顾create
函数,其通过malloc来创建name和description和book结构体对应的chunk,因此他们的地址都是有一定关系的,而author_name
又会把book结构体所在地址低两位用'\x00'
给覆盖, 所以我们可以在写完author_name后,创建一个book结构体,然后通过print
函数把结构体在堆中的地址给打印出来,达到泄露堆地址的目的 再接着调用author_name
函数,再次把第一个book结构体的低两位给覆盖 因为name description book结构体之间的地址都是有关系的,因此我们可以控制malloc的大小,让description的后两位变成'\x00'
, 最重要的是这里又给了修改description的功能,所以我们就可以通过修改某一个book结构体的description,伪造一个book结构体,达到任意写地址的目的。
解题思路:通过_free_hook
来获取shell __free_hook
可以在free前或者free后执行一些我们想要执行的函数,比如system("/bin/sh")
既然是利用__free_hook
,那就必须得先获取libc基地址,我们从第一步开始
先往author_name写入0x20个字符,然后创建一个book结构体,再print该结构体信息,达到泄露堆地址的目的
具体的malloc大小得自己测试,不一定是我这个数字,可能环境的原因吧
创建一个由mmap分配空间的堆,因为mmap创建的堆的地址和libc地址的偏移是固定的
mmap阈值,可以在glibc中的malloc.c中找到
修改s1的description,伪造一个结构体,使其name和description指向s2的name,然后打印泄露mmap地址
使用vmmap
命令查看libc基地址,计算mmap与其偏移
偏移 = 0x7f2caba03010 - 0x00007f2cab451000 基地址 = mmap地址 - 偏移
获取system、/bin/sh和__free_hook地址
修改s1的description为"bin/sh"和"__free_hook"的地址,达到间接修改s2的name和description的目的。
修改s2的description为"system"的地址,使得在free之前,执行system("/bin/sh")
__int64 __fastcall main(int a1, char **a2, char **a3)
{
struct _IO_FILE *v3; // rdi
int v5; // [rsp+1Ch] [rbp-4h]
setvbuf(stdout, 0LL, 2, 0LL);
v3 = stdin;
setvbuf(stdin, 0LL, 1, 0LL);
welcome();
author_name();
while ( 1 )
{
v5 = menu();
if ( v5 == 6 )
break;
switch ( v5 )
{
case 1:
create();
break;
case 2:
delete(v3);
break;
case 3:
edit(v3);
break;
case 4:
print();
break;
case 5:
author_name();
break;
default:
v3 = (struct _IO_FILE *)"Wrong option";
puts("Wrong option");
break;
}
}
puts("Thanks to use our library software");
return 0LL;
}
int welcome()
{
return puts("Welcome to ASISCTF book library");
}
__int64 sub_B6D()
{
printf("Enter author name: ");
if ( !(unsigned int)my_read(off_202018, 32LL) )
return 0LL;
printf("fail to read author_name");
return 1LL;
}
__int64 __fastcall my_read(_BYTE *a1, int a2)
{
int i; // [rsp+14h] [rbp-Ch]
if ( a2 <= 0 )
return 0LL;
for ( i = 0; ; ++i )
{
if ( (unsigned int)read(0, a1, 1uLL) != 1 )
return 1LL;
if ( *a1 == 10 )
break;
++a1;
if ( i == a2 )
break;
}
*a1 = 0;
return 0LL;
}
__int64 sub_A89()
{
int v1; // [rsp+Ch] [rbp-4h] BYREF
v1 = -1;
puts("\n1. Create a book");
puts("2. Delete a book");
puts("3. Edit a book");
puts("4. Print book detail");
puts("5. Change current author name");
puts("6. Exit");
printf("> ");
__isoc99_scanf("%d", &v1);
if ( v1 <= 6 && v1 > 0 )
return (unsigned int)v1;
else
return 0xFFFFFFFFLL;
}
[注意]APP应用上架合规检测服务,协助应用顺利上架!
最后于 2024-11-24 10:21
被mick0960编辑
,原因: 添加了代码语言