2012-10-16 85 views
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发送到使用功能badguy鸣叫。在badguy中,我们与前面的问题有一个偏差,这是mainpassword的声明与传递给badguy的参数之间的差异。我们已经指示我们使用此偏移量来查找主要的correctPasswordpassword的地址,并操作passwordcorrectPassword中的值,所以当密码检查发生时,它被认为是合法的。我在计算如何使用此偏移量来查找地址并从那里继续时遇到一些问题。

+0

不,你不应该,投票是匿名的,对问题的评论评论被认为是噪音。今后请不要这样做。 – casperOne

+0

http://en.wikipedia.org/wiki/Call_stack也许检查调用堆栈的结构可能会有所帮助。 :) 这些信息都在调用堆栈中,所以只需找到正确的偏移量即可获得所需的信息。 –

+0

在main中没有获得原始'password'的值只是从'badguy'口令中减去我的偏移量?这似乎并没有提供正确的结果,尽管这对我来说合乎逻辑。 –

回答

2

首先,确保您对编译器行为有良好的控制。那就是:确保你知道他们受到尊重的调用约定(未以任何方式优化或改变)。这通常归结为关闭优化设置,至少在更受控制的条件下进行测试,直到设计出可靠的方法。要特别注意的变量,如expectedPassword,因为它极有可能他们会被优化掉(expectedPassword可能永远不会在堆栈中创建,取代为的correctPassword等价,使你没有栈参考正确的密码完全)。

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

考虑在受控环境下的x86 32位目标,expectedPassword将驻留下面password 8个字节(4 password,4 magic,如果它不是最优化的距离)。从password到参数的偏移量如你所说的那样,从该参数的地址减去偏移量就足够了,然后加上8.结果指针应该是expectedPassword,然后指向包含密码的静态区域。再次检查你的环境。请查看this了解x64中堆栈布局的说明(32位大小写的布局相似)。

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

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

这些练习对于理解普通程序中安全漏洞的发生以及提供关于防御性编程的一些基本概念特别有用。

+0

在什么样的优化设置下编译器不会创建参数的引用?如果它没有存储在堆栈中,它存储在哪里? –

+0

这是编译器相关的。通常,如果变量足够小以适合寄存器,并且有寄存器可供使用,则变量将存储在寄存器中。如果变量只是作为一个常量l值使用,那么很可能直接使用该常量(与宏相同的效果)。在GCC中,据我所知,如果使用“-O0”编译这些优化,则不会发生这些优化。 – alecov