Skip to content

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, v8v8 是 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, ' ') + '&'

总结

我推荐重新做一遍