此程序在我的计算机上以root权限运行,我需要对以下代码执行堆栈溢出攻击并获得root权限:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>
void sha256(char *string, char outputBuffer[65])
{
unsigned char hash[SHA256_DIGEST_LENGTH];
int i = 0;
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, string, strlen(string));
SHA256_Final(hash, &sha256);
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
}
outputBuffer[64] = 0;
}
int password_check(char *userpass)
{
char text[20] = "thisisasalt";
unsigned int password_match = 0;
char output[65] = { 0, };
// >>> hashlib.sha256("Hello, world!").hexdigest()
char pass[] = "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3";
text[0] = 'a';
text[1] = 't';
text[2] = 'j';
text[3] = '5';
text[4] = '3';
text[5] = 'k';
text[6] = '$';
text[7] = 'g';
text[8] = 'f';
text[9] = '[';
text[10] = ']';
text[11] = '\0';
strcat(text, userpass);
sha256(text, output);
if (strcmp(output, pass) == 0)
{
password_match = 1;
}
return (password_match == 1);
}
int main(int argc, char **argv)
{
if (argc < 3)
{
printf("Usage: %s <pass> <command>\n", argv[0]);
exit(1);
}
if (strlen((const char *) argv[1]) > 10)
{
printf("Error: pasword too long\n");
exit(1);
}
if (password_check(argv[1]))
{
printf("Running command as root: %s\n", argv[2]);
setuid(0);
setgid(0);
system(argv[2]);
}
else
{
printf("Authentication failed! This activity will be logged!\n");
}
return 0;
}
所以我尝试用IDA分析程序,我看到文本段从较低的地址到较高的地址,高于我看到的数据,然后是bss,最后是外部命令。
据我所知,堆栈应该高于它,但我不确定如何查看它,我究竟应该如何查看堆栈才能知道我在写什么? (我甚至需要它还是我完全无能为力?)
第二个问题是考虑输入的长度,我如何在代码中解决这个问题:
if (strlen((const char *) argv[1]) > 10)
{
printf("Error: pasword too long\n");
exit(1);
}
我可以通过引用以某种方式将字符串提供给程序吗?如果是这样我该怎么办? (再一次,希望我不是完全无能为力)
答案 0 :(得分:3)
据我所知,堆栈应该高于它,但我不确定如何查看它,我究竟应该如何查看堆栈才能知道我在写什么? (我甚至需要它还是我完全无能为力?)
堆栈位置始终不变 - 您需要查看ESP
/ RSP
寄存器的值,其值是堆栈顶部的当前地址。通常,变量寻址将基于EBP
而不是ESP
,但它们都将指向相同的一般内存区域。
在分析过程中,IDA为每个函数设置一个堆栈框架,其行为与struct
非常相似 - 您可以在其中定义具有类型和名称的变量。该框架总结在函数的顶部:
双击它或函数体中的任何局部变量将打开一个更详细的窗口。如果没有在调试器中实际运行程序,那就更好了。
您可以看到text
就在password_match
旁边,从地址判断,为0x14
分配了text
个字节,正如人们所期望的那样。但是,这不能保证,编译器可以自由地改变周围的变量,填充它们或将它们优化到寄存器中。
第二个问题是考虑输入的长度,我如何在代码中解决这个问题:
if (strlen((const char *) argv[1]) > 10) { printf("Error: pasword too long\n"); exit(1); }
你不需要绕过这个检查,它已经足够破碎了。有一个一个一个错误。
如果你想弄清楚溢出,请不要在这里阅读。
text
的有效索引范围从text[0]
到text[19]
。在代码中,用户输入被写入从text[11]
开始的存储区。strlen
检查允许的最大输入长度是10个符号+ NULL终止符。不幸的是,这意味着text[19]
包含第9个用户输入的符号,第10个符号+终结符溢出到相邻的内存空间。在某些情况下,允许您使用任意值覆盖password_match
的最低有效字节,使用0
覆盖第二个最低有效字节。
如果{{{}您的函数接受密码password_match
1}}等于1
,这意味着您的密码中的第10个字符必须为'\x01'
(请注意,这与'1'
不是同一个字符。)
以下是IDA作为调试器运行的两个屏幕截图。 text
以黄色突出显示,password_match
为绿色。
我输入的密码是123456789\x01
。
用户输入密码之前的堆栈strcat
进入text
。
strcat
之后的堆叠。请注意password_match
已更改。