第十八届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] BYREFunsigned __int64 v2; // [rsp+8h] [rbp-118h]char s[264]; // [rsp+10h] [rbp-110h] BYREFunsigned __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);elsememset(*((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] BYREFunsigned __int64 v2; // [rsp+38h] [rbp-8h]v2 = __readfsqword(0x28u);read(0, buf, 0x20uLL);return atoi(buf);
}
同样 off by null 把free 下面一行 nop 掉
这样就改成了,上传就修复成功了
还可以改这个 0x1F 改小