1

好的,我们得到以下代码:

#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使用函数来欺骗发送推文badguypassword在 badguy 中,我们从之前的问题中得到了一个偏移量,即in的声明main和传递给的参数之间的差异badguy。我们被指示使用此偏移量来查找 main 和 in 的地址correctPasswordpassword操纵 in passwordto的值,correctPassword因此当密码检查发生时,它被认为是合法的。我在弄清楚如何使用此偏移量来查找地址并从那里继续时遇到了一些麻烦。

4

1 回答 1

2

首先,确保您对编译器行为有很好的控制。那就是:确保你知道调用约定并且它们得到尊重(没有被优化掉或以任何方式改变)。这通常归结为关闭优化设置,至少是为了在更受控的条件下进行测试,直到设计出一种稳健的方法。请特别注意诸如 之类的变量expectedPassword,因为它们很可能会被优化掉(expectedPassword可能永远不会在堆栈中创建,被等效的 替换correctPassword,从而使您根本没有对正确密码的堆栈引用)。

其次,注意"wrongpassword""ceriaslyserious";短 换句话说,如果我直截了当,试图破解指向的缓冲区passwordCopy(其大小是"wrongpassword"加一的长度)以便复制"ceriaslyserious"到那里可能会导致分段违规。expectedPassword尽管如此,跟踪调用堆栈中的地址应该相对简单,如果它存在(见上文),特别是如果您已经从main()的堆栈帧偏移。

考虑到在受控情况下的 x86 32 位目标,expectedPassword将驻留在以下 8 个字节password(4 代表password,4 代表magic如果它没有被优化掉)。如您所说,具有参数的偏移password量,从该参数的地址中减去偏移量就足够了,然后加 8。结果指针应该是expectedPassword,然后指向包含密码的静态区域。再次,仔细检查您的环境。检查this以获取有关x64中堆栈布局的说明(32位情况下的布局类似)。

最后,如果expectedPassword调用栈中不存在,那么,由于correctPassword是全局静态的,它将驻留在数据段中,使该方法无用。为了在这种情况下实现目标,您需要使用更智能的算法仔细扫描数据段。但是,简单地尝试check()在程序文本中查找 ' 返回值的测试并替换为nops (在正确操作页面权限以允许写入文本段之后)可能会更容易。

如果您遇到问题,检查生成的汇编代码是可行的方法。如果您使用的是 GCC,gcc -S则在汇编之前暂停编译(即,生成汇编源代码文件作为输出)。objdump -d也可以提供帮助。gdb可以在指令之间跳转,显示帧的反汇编和显示寄存器内容;检查文档。

这些练习对于了解常见程序中如何发生安全漏洞以及提供一些防御性编程的基本概念特别有用。

于 2012-10-16T04:40:09.117 回答