基于堆栈的缓冲区溢出 - 在输入有限的情况下使用 scanf 在 C 中的挑战
Stack-based buffer overflow - challenge in C using scanf with limited input
作为安全 CS 课程的一部分,我的 class 的任务是利用漏洞利用 stack/buffer 溢出来绕过密码检查。存在漏洞的代码如下:
#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;
}
我理解classic "stack-smashing"原理(我认为),这里有一个明显的溢出漏洞,可以覆盖correct_hash
数组的前14个字节, 提示时输入超过 15 个字符的密码。但是,我不明白如何利用它来让 memcmp
检查通过,从而完成挑战。我有的一些东西discovered/attempted:
将 password
设置为等同于 correct_hash
不起作用,因为 password
使用 MD5()
进行哈希处理(将两者设置为无论如何,相等是不可能的,因为 scanf
会将一个唯一的 ASCII NUL
字符恰好插入到它可用的 30 个空间中,这意味着这两个数组永远不会相等。 NUL
字符另外(到据我所知)不能插入 scanf
字符串的中间)。
用 scanf
覆盖最大字节数(将始终附加一个 NUL
字符)意味着 correct_hash
的最后 3 个字节将始终是 0x00 0x91 0xeb
。尝试随机生成一个 16 个字符的密码,然后将其散列为最后 3 个 bytes/characters 的内容(考虑到使用 MD5,在计算上相当容易)不起作用,但是,由于使用了 strlen(password)
(这将给出 29 的值,而不是像 16 这样方便的值,因为只有在点击 NUL
字符时才完成长度计数)在对 [=16= 的调用中].这意味着对 MD5()
的调用将散列来自 password
的 16 个字符,然后散列 correct_hash
的 13 个字符,而不是散列 16 个字符的密码以产生预期的输出,从而产生不同的最终结果哈希值。
- 为了解决这个问题,我相信必须找到一个 29 个字符的字符串(称之为 S),其中 S 的前 16 个字符散列为由 S 的最后 13 个字符组成的字符串 R,然后是通过
0x00 0x91 0xeb
。我不确定通过随机 MD5 散列计算找到它的可行性如何,但它并不看好我的机会。
一些注意事项(在上面的解释中提到):
scanf
限制为 29 个字符的字符串,但会附加一个 ASCII NUL
字符,总共允许 30 个字符(password
数组中的 16 个, correct_hash
数组中的 14) 将被覆盖。
ASCII NUL
字符无法通过 scanf
输入,因此调用 MD5()
中的 strlen(password)
(如果使用最大密码长度) 将是 29.
所以这里的问题是,我还能怎么做呢?我错过了一些非常明显的东西吗?随机生成是可行的解决方案,还是唯一的解决方案?
解决方案必须使用缓冲区溢出(否则我想我可以做一些事情,比如预加载一个 memcmp
总是 returns 0),并且必须可以作为 shell 脚本(如果有任何相关性的话)。
只是为了将评论合并到这里的答案中:
问题的症结在于,scanf
将乐于接受零字节作为字符串的一部分,并且不会将其视为空白(因此,不会停止将更多字节读入字符串) .
重定向文件或使用 echo -ne "\x00" | program
等。重定向输入文件可能是这里的首选方式。
我使用的最后一个命令是:echo -ne "\x49\x5a\x4e\x52\x48\x49\x41\x56\x5a\x43\x54\x52\x51\x4c\x43\x00\x81\xae\xf3\xdf\xa2\x45\xb1\x57\x19\xb3\xa9\xb8\x7d\x00\x91\xeb" | ./vulnerable
其中第一组 16 个字节(直到并包括第一个 \x00
)是一个随机生成的字符串,在散列时生成第二个一组 16 个字节,以所需的 \x00\x91\xeb
结尾。最后 3 个字节无论如何都没有被复制,但我把它们留在里面以显示字符串和散列。
作为安全 CS 课程的一部分,我的 class 的任务是利用漏洞利用 stack/buffer 溢出来绕过密码检查。存在漏洞的代码如下:
#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;
}
我理解classic "stack-smashing"原理(我认为),这里有一个明显的溢出漏洞,可以覆盖correct_hash
数组的前14个字节, 提示时输入超过 15 个字符的密码。但是,我不明白如何利用它来让 memcmp
检查通过,从而完成挑战。我有的一些东西discovered/attempted:
将
password
设置为等同于correct_hash
不起作用,因为password
使用MD5()
进行哈希处理(将两者设置为无论如何,相等是不可能的,因为scanf
会将一个唯一的 ASCIINUL
字符恰好插入到它可用的 30 个空间中,这意味着这两个数组永远不会相等。NUL
字符另外(到据我所知)不能插入scanf
字符串的中间)。用
scanf
覆盖最大字节数(将始终附加一个NUL
字符)意味着correct_hash
的最后 3 个字节将始终是0x00 0x91 0xeb
。尝试随机生成一个 16 个字符的密码,然后将其散列为最后 3 个 bytes/characters 的内容(考虑到使用 MD5,在计算上相当容易)不起作用,但是,由于使用了strlen(password)
(这将给出 29 的值,而不是像 16 这样方便的值,因为只有在点击NUL
字符时才完成长度计数)在对 [=16= 的调用中].这意味着对MD5()
的调用将散列来自password
的 16 个字符,然后散列correct_hash
的 13 个字符,而不是散列 16 个字符的密码以产生预期的输出,从而产生不同的最终结果哈希值。- 为了解决这个问题,我相信必须找到一个 29 个字符的字符串(称之为 S),其中 S 的前 16 个字符散列为由 S 的最后 13 个字符组成的字符串 R,然后是通过
0x00 0x91 0xeb
。我不确定通过随机 MD5 散列计算找到它的可行性如何,但它并不看好我的机会。
- 为了解决这个问题,我相信必须找到一个 29 个字符的字符串(称之为 S),其中 S 的前 16 个字符散列为由 S 的最后 13 个字符组成的字符串 R,然后是通过
一些注意事项(在上面的解释中提到):
scanf
限制为 29 个字符的字符串,但会附加一个 ASCIINUL
字符,总共允许 30 个字符(password
数组中的 16 个,correct_hash
数组中的 14) 将被覆盖。ASCII
NUL
字符无法通过scanf
输入,因此调用MD5()
中的strlen(password)
(如果使用最大密码长度) 将是 29.
所以这里的问题是,我还能怎么做呢?我错过了一些非常明显的东西吗?随机生成是可行的解决方案,还是唯一的解决方案?
解决方案必须使用缓冲区溢出(否则我想我可以做一些事情,比如预加载一个 memcmp
总是 returns 0),并且必须可以作为 shell 脚本(如果有任何相关性的话)。
只是为了将评论合并到这里的答案中:
问题的症结在于,scanf
将乐于接受零字节作为字符串的一部分,并且不会将其视为空白(因此,不会停止将更多字节读入字符串) .
重定向文件或使用 echo -ne "\x00" | program
等。重定向输入文件可能是这里的首选方式。
我使用的最后一个命令是:echo -ne "\x49\x5a\x4e\x52\x48\x49\x41\x56\x5a\x43\x54\x52\x51\x4c\x43\x00\x81\xae\xf3\xdf\xa2\x45\xb1\x57\x19\xb3\xa9\xb8\x7d\x00\x91\xeb" | ./vulnerable
其中第一组 16 个字节(直到并包括第一个 \x00
)是一个随机生成的字符串,在散列时生成第二个一组 16 个字节,以所需的 \x00\x91\xeb
结尾。最后 3 个字节无论如何都没有被复制,但我把它们留在里面以显示字符串和散列。