Pwnable.kr loveletter
这道题太可惜了太可惜了,漏洞点很明显,然后没想到怎么用!看了别人题解瞬间就明白了,后悔哇...
题目描述
首先是 main 函数,很好理解。protect 是一个过滤函数,假设我们输入 allen,此时它没有反应。然后最后有连续三个 memcpy,这样会让 loveletter = echo I love allen very much,最后执行依次 system(loveletter),就相当于在 shell 上执行 echo I love allen very much。
int main()
{
char s[256]; // [esp+10h] [ebp-114h]
size_t v5; // [esp+110h] [ebp-14h]
size_t v6; // [esp+114h] [ebp-10h]
size_t v7; // [esp+118h] [ebp-Ch]
unsigned int v8; // [esp+11Ch] [ebp-8h]
v8 = __readgsdword(0x14u);
v6 = strlen(epilog); // epilog --> 'echo I love'
v5 = strlen(prolog); // prolog --> ' very much'
memset(loveletter, 0, 0x100u);
fgets(s, 0x100, stdin);
protect(s);
v7 = strlen(s);
idx = 0;
memcpy((void *)((unsigned int16)idx + loveletter), prolog, v5); idx += v5;
memcpy((void *)((unsigned int16)idx + loveletter), s, v7); idx += v7;
memcpy((void *)((unsigned int16)idx + loveletter), epilog, v6); idx += v6;
return system(loveletter);
}
然后看看这个 protect 这个过滤函数。被我大改了一通,但是本质还是没变。实际上就是看看输入有那些地方是禁止字符。如果是禁止字符,注意使用了 *(_DWORD *)&input[i] = 0xA599E2,所以相当于是 input[i]=0xE2, input[i+1]=0x99, input[i+2]=0xA5, input[i+3]=0。
然后又执行 memcpy((void *)&input[v1], &dest, v2);,所以相当于从 input[i+3] 开始复制原来的 input[i+1] 开头内容。本质上就是将 input 往后移动两个,原来的 input[i~i+2] 三块变成 0xA599E2。
所以如果我们输入 && sh ||,这样做是为了最后相当于执行 echo I love && sh || very much,但是这个函数就会过滤掉我们的输入,最后相当于 echo I love xxx sh xxx very much。
unsigned int protect(const char *input)
{
char ban[25];
char dest[0x100]; // [esp+3Ch] [ebp-10Ch]
unsigned int v8; // [esp+13Ch] [ebp-Ch]
v8 = __readgsdword(0x14u);
strcpy(ban, "#&;`'\"|*?~<>^()[]{}$\\,");
for ( int i = 0; i < strlen(input); ++i ) {
for ( int j = 0; j < 25; ++j ) {
if ( input[i] == ban[j] ) {
strcpy(&dest, &input[i + 1]);
*(_DWORD *)&input[i] = 0xA599E2;
int v1 = strlen(input);
int v2 = strlen(dest);
memcpy((void *)&input[v1], &dest, v2);
}
}
}
return __readgsdword(0x14u) ^ v8;
}
漏洞分析
仔细想,漏洞点很简单,就是 protect 会把一个字节扩展为三个字节,这就导致我们的输入变长了。算一下最长的长度: 0x100*3 = 0x300,而 main 函数的 s 变量最长为 0x100,堆上的 loveletter 变量最长为 0x100,所以可以溢出这两个。
流程分析
溢出 main 函数的 s 变量,它的后面是 v5, v6, v7, v8, v8 是 canary,所以溢出到这就结束了(因为本题没想到办法去泄露 canary),所以可以泄露 v5,v6,v7,这几个是和 memcpy 有关系的。
这里参考别人的做法,把 v5 泄露为 0,这样就相当于没有 memcpy((void *)(loveletter), prolog, v5)。
程序继续执行 memcpy((void *)(loveletter), s, v6),所以 loveletter 开始就是我们的输入,输入 cat flag,这样 system(loveletter) 就是 cat flag。看下面代码。
payload = ('cat flag').ljust(253, ' ') + '&'
总结
我推荐重新做一遍