5

作为安全 CS 课程的一部分,我的班级被赋予了利用漏洞利用堆栈/缓冲区溢出来击败密码检查的任务。存在漏洞的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/md5.h>

int main(int argc, char **argv) {
    char correct_hash[16] = {
        0xd0, 0xf9, 0x19, 0x94, 0x4a, 0xf3, 0x10, 0x92,
        0x32, 0x98, 0x11, 0x8c, 0x33, 0x27, 0x91, 0xeb
    };
    char password[16];

    printf("Insert your password: ");
    scanf("%29s", password);

    MD5(password, strlen(password), password);

    if (memcmp(password, correct_hash, 16) == 0) {
        printf("Correct Password!\n");
    } else {
        printf("Wrong Password, sorry!\n");
    }
    return 0;
}

我理解经典的“堆栈粉碎”原理(我认为),这里有一个明显的溢出漏洞,correct_hash可以通过在提示时输入超过 15 个字符的密码来覆盖数组的前 14 个字节。但是,我不明白如何利用它来让memcmp检查通过,完成挑战。我发现/尝试的一些事情:

  • 设置password为等价的correct_hash不起作用,因为password使用哈希MD5()(无论如何都不可能将两者设置为相等,因为scanf将精确地插入一个唯一的 ASCIINUL字符到可用的 30 个空格中,这意味着这两个数组永远不会等价.NUL字符(据我所知)不能插入scanf字符串中间)。

  • scanf用(总是附加一个字符)覆盖最大字节数NUL意味着最后 3 个字节correct_hash总是0x00 0x91 0xeb. 尝试随机生成一个 16 个字符的密码,然后用最后 3 个字节/字符(考虑到使用 MD5,计算上相当容易)进行哈希处理是行不通的,但是,由于使用strlen(password)(这将给出一个值29而不是像 16 这样方便的东西,这要归功于NUL在对MD5(). 这意味着,不是对 16 个字符的密码进行散列以产生预期的输出,而是对 16 个字符的调用进行MD5()散列,password然后是 13 个字符correct_hash,产生不同的最终散列值。

    • 为了解决这个问题,我相信必须找到一个 29 个字符的字符串(称为 S),其中 S 的前 16 个字符散列到由 S 的最后 13 个字符组成的字符串 R,然后是0x00 0x91 0xeb. 我不确定通过随机 MD5 哈希计算找到这一点有多可行,但它并不看好我的机会。

一些注意事项(在上面的解释中提到):

  • scanf限制为 29 个字符的字符串,但会附加一个 ASCIINUL字符,从而允许覆盖总共 30 个字符(数组中的 16 个,password数组中的 14 个)。correct_hash

  • NUL无法输入ASCII字符,scanf因此strlen(password)在调用中MD5()(如果使用最大密码长度)将为 29。

所以这里的问题是,我还能怎么做呢?我错过了一些非常明显的东西吗?随机生成是可行的解决方案,还是唯一的解决方案?

解决方案必须使用缓冲区溢出(否则我想我可以做一些像 preload amemcmp总是返回 0 的事情),并且必须可以作为 shell 脚本执行(如果有任何相关性的话)。

4

1 回答 1

6

只是为了将评论合并到这里的答案中:

问题的关键在于,它scanf会愉快地接受零字节作为字符串的一部分,并且不会将其视为空格(因此,不会停止将更多字节读入字符串)。

重定向文件或使用echo -ne "\x00" | program等。重定向输入文件可能是这里的首选方式。

我使用的最后一个命令是:echo -ne "\x49\x5a\x4e\x52\x48\x49\x41\x56\x5a\x43\x54\x52\x51\x4c\x4‌​3\x00\x81\xae\xf3\xd‌​f\xa2\x45\xb1\x57\x1‌​9\xb3\xa9\xb8\x7d\x0‌​0\x91\xeb" | ./vulnerable其中第一组 16 个字节(直到并包括第一个\x00)是一个随机生成的字符串,当散列时产生第二组 16 个字节,并具有所需的\x00\x91\xeb结尾。无论如何,最后 3 个字节都没有被复制,但我把它们留在里面以显示字符串和散列。

于 2017-01-11T11:07:23.937 回答