好的,我们得到以下代码:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "callstack.h"
#include "tweetIt.h"
#include "badguy2.c"
static char *correctPassword = "ceriaslyserious";
char *message = NULL;
int validateSanity(char *password) {
for(int i=0;i<strlen(password);i++)
if(!isalpha(password[i]))
return 0;
unsigned int magic = 0x12345678;
return badguy(password);
}
int validate(char *password) {
printf("--Validating something\n", password);
if (strlen(password) > 128) return 0;
char *passwordCopy = malloc(strlen(password) + 1);
strcpy(passwordCopy, password);
return validateSanity(passwordCopy);
}
int check(char *password, char *expectedPassword) {
return (strcmp(password, expectedPassword) == 0);
}
int main() {
char *password = "wrongpassword";
unsigned int magic = 0xABCDE;
char *expectedPassword = correctPassword;
if (!validate(password)) {
printf("--Invalid password!\n");
return 1;
}
if (check(password, expectedPassword)) {
if (message == NULL) {
printf("--No message!\n");
return 1;
} else {
tweetIt(message, strlen(message));
printf("--Message sent.\n");
}
} else {
printf("--Incorrect password!\n");
}
return 0;
}
我们应该使用函数main
欺骗badguy
发送推文。在badguy中,我们偏离了先前的问题,即password
中main
的声明与传递给badguy
的参数之间的差异。我们已被指示使用此偏移量来查找main中correctPassword
和password
的地址,并将password
中的值操作为correctPassword
,因此在进行密码检查时,它被认为是合法的。我在查找如何使用此偏移量来查找地址并从那里继续时遇到了一些麻烦。
答案 0 :(得分:2)
首先,确保您可以很好地控制编译器行为。那就是:确保你知道他们被尊重的调用约定和(没有以任何方式优化或改变)。这通常归结为关闭优化设置,至少在更受控制的条件下进行测试,直到设计出稳健的方法。要特别注意expectedPassword
之类的变量,因为它们极有可能被优化掉(expectedPassword
可能永远不会在堆栈中创建,被等效的correctPassword
替换,使用 no 堆栈引用正确的密码来呈现您。
其次,请注意"wrongpassword"
比"ceriaslyserious"
短;换句话说,如果我直截了当,试图破解passwordCopy
指向的缓冲区(其大小为"wrongpassword"
的长度加一),以便将"ceriaslyserious"
复制到那里可能会导致分段违规。尽管如此,跟踪调用堆栈中expectedPassword
的地址应该相对简单,如果存在(见上文),特别是如果你已经有{{1}的偏移量堆栈框架。
在受控环境下考虑x86 32位目标,main()
将位于expectedPassword
以下8个字节(password
为4个,password
为4个magic
如果未优化远)。如上所述,从password
到参数的偏移量,应该足以从该参数的地址中减去偏移量,然后加上8.结果指针应为expectedPassword
,然后指向包含密码的静态区域。再次,仔细检查您的环境。检查this以获取有关x64中堆栈布局的说明(32位情况下的布局类似)。
最后,如果调用堆栈中不存在expectedPassword
,那么,由于correctPassword
是一个全局静态,它将驻留在数据段中,使该方法无用。要在这种情况下实现目标,您需要使用更智能的算法仔细扫描数据段。但是,简单地尝试在程序文本中找到check()
的返回值的测试并替换为nop
s(在正确操作页面权限以允许写入之后)可能会更容易。文本段)。
如果您遇到问题,请检查生成的汇编代码。如果您正在使用GCC,gcc -S
将在汇编之前暂停编译(即,生成汇编源代码文件作为输出)。 objdump -d
也可以提供帮助。 gdb
可以在指令之间切换,显示框架的反汇编并显示寄存器内容;查看文档。
这些练习对于了解常见程序中安全漏洞的发生方式特别有用,并提供了一些关于防御性编程的基本概念。