awdp pwn
第十八届CISCN&CCB半决赛
awdp pwn
typo
snprintf() 是一个 C 语言标准库函数,用于格式化输出字符串,并将结果写入到指定的缓冲区,与 sprintf() 不同的是,snprintf() 会限制输出的字符数,避免缓冲区溢出。
C 库函数 int snprintf(char str, size_t size, const char format, ...) 设将可变参数 (...) 按照 format 格式化成字符串,并将字符串复制到 str 中,size** 为要写入的字符的最大数目,超过 size 会被截断,最多写入 size-1 个字符。
与 sprintf() 函数不同的是,snprintf() 函数提供了一个参数 size,可以防止缓冲区溢出。如果格式化后的字符串长度超过了 size-1,则 snprintf() 只会写入 size-1 个字符,并在字符串的末尾添加一个空字符(\0)以表示字符串的结束。
声明:
下面是 snprintf() 函数的声明。
int snprintf ( char * str, size_t size, const char * format, ... );
看到 edit 函数,当时就在分析怎么改这个 snprintf 函数,感觉是参数不对
unsigned __int64 edit()
{
signed int v1; // [rsp+4h] [rbp-11Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-118h]
char s[264]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v4; // [rsp+118h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%d", &v1);
if ( (unsigned int)v1 < 0x20 )
{
if ( *((_QWORD *)&unk_4060 + v1) )
{
v2 = **((_QWORD **)&unk_4060 + v1);
printf("New size of content: ");
memset(s, 0, 0x100uLL);
read(0, s, 0x100uLL);
snprintf(*((char **)&unk_4060 + v1), (size_t)"%lu", s, 8LL);// 比赛的时候就觉得这个有问题,但不知道怎么改
if ( v2 < **((_QWORD **)&unk_4060 + v1) )
{
puts("Too large");
**((_QWORD **)&unk_4060 + v1) = v2;
}
printf("What do you want to say: ");
read(0, (void *)(*((_QWORD *)&unk_4060 + v1) + 8LL), **((_QWORD **)&unk_4060 + v1));
}
else
{
puts("No card here");
}
}
else
{
puts("Invalid index");
}
return __readfsqword(0x28u) ^ v4;
}


Prompt
off by null 给它 nop掉

找到 sub_1A40函数,猜测memcpy存在溢出,把rdx改小
unsigned __int64 __fastcall sub_1A40(__int64 a1)
{
signed int v2; // [rsp+18h] [rbp-18h]
signed int v3; // [rsp+1Ch] [rbp-14h]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
if ( *(_QWORD *)(a1 + 48) )
{
if ( *(_QWORD *)(a1 + 32) )
{
v2 = **(_DWORD **)(a1 + 56);
v3 = **(_DWORD **)(a1 + 40);
if ( (unsigned int)v2 < 0x10 && *((_QWORD *)&unk_5060 + v2) && (unsigned int)v3 <= 0x500 )
{
qword_50E0[v2] = v3;
if ( *(_QWORD *)(a1 + 64) && *(_QWORD *)(a1 + 72) )
memcpy(*((void **)&unk_5060 + v2), *(const void **)(a1 + 72), 0LL);
else
memset(*((void **)&unk_5060 + v2), 0, v3);
}
}
}
return v4 - __readfsqword(0x28u);
}
找到 sub_1A40函数,memcpy存在溢出,改rdx参数为0
unsigned __int64 __fastcall sub_17BB(__int64 a1)
{
int i; // [rsp+18h] [rbp-18h]
signed int v3; // [rsp+1Ch] [rbp-14h]
unsigned __int64 v4; // [rsp+28h] [rbp-8h]
v4 = __readfsqword(0x28u);
if ( *(_QWORD *)(a1 + 32) )
{
v3 = **(_DWORD **)(a1 + 40);
if ( (unsigned int)v3 <= 0x500 )
{
for ( i = 0; i <= 15 && *((_QWORD *)&unk_5060 + i); ++i )
;
if ( i <= 15 )
{
qword_50E0[i] = v3;
*((_QWORD *)&unk_5060 + i) = malloc(v3);
if ( !*((_QWORD *)&unk_5060 + i) )
exit(1);
memset(*((void **)&unk_5060 + i), 0, v3);
if ( *(_QWORD *)(a1 + 64) && *(_QWORD *)(a1 + 72) )
memcpy(*((void **)&unk_5060 + i), *(const void **)(a1 + 72), 0LL);
}
}
}
return v4 - __readfsqword(0x28u);
}
其他方法:
在第一个函数 sub_17BB 里面

把0x500改成0x100,防止large bin attack之类的

patch后

这里有个 off by null ,把这一行nop掉(比赛中有改,我应该是改改这个和上面的 memcpy 一起成功的)


这个函数有 uaf 比赛时没看到,把0x1000改小


成了
post_quantum
输入函数,把0x20改小
int sub_1441()
{
char buf[56]; // [rsp+0h] [rbp-40h] BYREF
unsigned __int64 v2; // [rsp+38h] [rbp-8h]
v2 = __readfsqword(0x28u);
read(0, buf, 0x20uLL);
return atoi(buf);
}

同样 off by null 把free 下面一行 nop 掉


这样就改成了,上传就修复成功了
还可以改这个 0x1F 改小



ciscn&ccb半决
浙公网安备 33010602011771号