使用缓冲区溢出执行Shell代码

8

最近我一直在学习计算机安全,遇到了一些问题,特别是这个问题让我有些困扰。

我需要溢出一个带有固定缓冲区的函数,以便在文件shellcode中执行shellcode。这个函数非常简单:

void vuln(char *str) {
    char buf[64];
    strcpy(buf, str);
    //function provided to display stack on command prompt
    dump_stack((void **) buf, 21, (void **) &str);
}

我最初的想法是修改函数的返回地址eip,以便定位并执行shellcode文件中的内容,但我意识到我没有可以用十六进制值表示文件的地址。我非常确定需要操纵返回地址,因此目前我正在调用:

//the string is passed as a command line arg
./buffer_overflow_shellcode $(python -c "print 'A'*72 + '\x41\xd6\xff\xff' ")

我的输出结果是:
Stack dump:
0xffffd600: 0xffffd7fd (first argument)
0xffffd5fc: 0x08048653 (saved eip)
0xffffd5f8: 0xffffd641 (saved ebp)
0xffffd5f4: 0x41414141
0xffffd5f0: 0x41414141
0xffffd5ec: 0x41414141
0xffffd5e8: 0x41414141
0xffffd5e4: 0x41414141
0xffffd5e0: 0x41414141
0xffffd5dc: 0x41414141
0xffffd5d8: 0x41414141
0xffffd5d4: 0x41414141
0xffffd5d0: 0x41414141
0xffffd5cc: 0x41414141
0xffffd5c8: 0x41414141
0xffffd5c4: 0x41414141
0xffffd5c0: 0x41414141
0xffffd5bc: 0x41414141
0xffffd5b8: 0x41414141
0xffffd5b4: 0x41414141
0xffffd5b0: 0x41414141 (beginning of buffer)
Segmentation fault

这个Python脚本只是简单地打印出72个字母A,以使缓冲区溢出到edpeip的位置。当我用另一个地址替换edp的地址并到达返回地址时,就可以准备操纵它了。非常感谢任何帮助,谢谢!


6
你是否使用“-really-no-protection”和“-make-me-vulnerable-to-everything”这两个标志来编写代码?现在这是针对该漏洞必须采取的步骤。 - Daniel Fischer
@Daniel Fischer 是的,我之前已经正确编译了计算机安全程序,并且能够成功地进行溢出攻击。我知道我的程序将按预期运行。 - Syntactic Fructose
好的,只是检查显而易见的嫌疑人。 - Daniel Fischer
你禁用了地址空间布局随机化吗?(echo 0 > /proc/sys/kernel/randomize_va_space) - PurpleAlien
4个回答

19

我认为这可能类似于《计算机系统:程序员的视角》中的缓冲区溢出实验。首先使用objdump获取静态地址。其次,使用gdb运行它以查找栈的地址。然后,填充缓冲区的字符串超过返回到缓冲区的地址(这样您就可以放置利用代码,或者调用程序中的其他代码)。

查看这个pdf,它作为该实验的指南。它可以为您提供一些见解。

正如指出的那样,需要许多编译时标志才能实现此目的。(我会检查并很快回来)。或者,帖子提供了有关如何编译这种示例的指南。


2
我最初的想法是修改函数的返回地址(eip),以便定位并执行shellcode文件中的内容,但我意识到我没有可以用十六进制值表示的文件地址。
您想修改RET地址,以便在函数结束时不返回给调用者,而是返回到您的shellcode的开头。
(简要概述shellcode是什么,它是一组汇编指令(因此严重依赖于执行易受攻击的进程的平台),它执行一个shell(通常是root shell),从而将您放在一个可以利用的良好环境中。)
现在回来,您想将RET指向shellcode中第一个汇编指令。奇怪的是,您将其放在一个单独的文件中。这是必需的吗?
通常的做法是这样的:
char shellcode[] = "\x90\x90\x90...";


int main()
{
        /* 
         * huge string (like your 72 A's) that appends the address of the 
         * shellcode at the right address (in your case I think it's 64 + 4) 
         */
        char evilstring[100]; 

        /* Fill the buf and the EBP with A's */
        for (int i = 0; i < 64 + 4; i++) {
                evilstring[i] = 'A';
        }
        /* And the RET with the address of your shellcode */
        sprintf(&evilstring[68], "%p", &shellcode[0]);
        vuln(evilstring);
        /* you should have a shell now */

        /* NOTREACHED */
        return 0;
}

所以,现在当您的函数返回时,它返回到shellcode[]字符串的地址,并从那里继续执行指令。这正是您想要的。因为那些指令可以让您获得根shell(或者您的Shellcode要做的任何事情)。

请注意,上面的代码只是示例代码,甚至没有编译测试。

如果我未能理解您的问题或者我没有解释清楚,请随时提问。


1
char buff[20];
unsigned int pass = 0;

当“buff”溢出时,额外的输入将把“pass”变成大于0的值,使其成为“true”值。

如果栈向下增长,那么过多的“buff”输入将覆盖任何已推入的寄存器和/或返回地址,而不是“传递”。 - drhr

0

如果你知道该去哪里寻找,这并不难,就像之前说的那样,用gdb打开应用程序。运行它。然后使用(i)nfo r(egisters)查看为什么它崩溃了。反汇编也非常有用。

另外,(我假设你已经知道这个):

void vuln(char *str) {
    char buf[64];
    strcpy(buf, str);
    //function provided to display stack on command prompt
    dump_stack((void **) buf, 21, (void **) &str);
}

其实是

void vuln(char *str) {
    void *return;
    char buf[64];

    /* Set Return value and store stack */
    strcpy(buf, str);
    //function provided to display stack on command prompt
    dump_stack((void **) buf, 21, (void **) &str);
    /* restore stack and jmp to return value. */
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接