作为安全 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 哈希计算找到这一点有多可行,但它并不看好我的机会。
- 为了解决这个问题,我相信必须找到一个 29 个字符的字符串(称为 S),其中 S 的前 16 个字符散列到由 S 的最后 13 个字符组成的字符串 R,然后是
一些注意事项(在上面的解释中提到):
scanf
限制为 29 个字符的字符串,但会附加一个 ASCIINUL
字符,从而允许覆盖总共 30 个字符(数组中的 16 个,password
数组中的 14 个)。correct_hash
NUL
无法输入ASCII字符,scanf
因此strlen(password)
在调用中MD5()
(如果使用最大密码长度)将为 29。
所以这里的问题是,我还能怎么做呢?我错过了一些非常明显的东西吗?随机生成是可行的解决方案,还是唯一的解决方案?
解决方案必须使用缓冲区溢出(否则我想我可以做一些像 preload amemcmp
总是返回 0 的事情),并且必须可以作为 shell 脚本执行(如果有任何相关性的话)。