【问题标题】:writing shellcode: why my shellcode won't work?编写 shellcode:为什么我的 shellcode 不起作用?
【发布时间】:2013-11-01 03:36:21
【问题描述】:

我目前正在编写一个利用使用puts 函数的目标程序的shellcode。程序如下所示:

#include <stdio.h>
main() {
    char buf[123];
    puts(gets(buf));
}

我想要做的是溢出这个缓冲区并使用一些参数调用execve。我有一个用 c/inline 程序集编写的测试程序,它可以使用一些参数调用 execve,然后我使用 gdb 从这个程序中获取 shellcode。据我了解,堆栈布局如下所示:

|-------buffer(+padding)---------|---------sfp---------|---- ---ret-------------|

通过查看gcc生成的目标程序的部分汇编代码:

.cfi_startproc                  
pushq   %rbp                    
.cfi_def_cfa_offset 16          
.cfi_offset 6, -16              
movq    %rsp, %rbp              
.cfi_def_cfa_register 6         
addq    $-128, %rsp             
leaq    -128(%rbp), %rax        
movq    %rax, %rdi              
call    gets                    
movq    %rax, %rdi              
call    puts                    
leave                           
.cfi_def_cfa 7, 8               
ret                             
.cfi_endproc       

我认为缓冲区和填充占用 128 个字节,而 sfp 和返回地址各占用 8 个字节,所以总共是 144 个字节.基于此,我的 nop sled、payload 和新的返回地址(等于缓冲区的地址)组合起来(即我的 shellcode)也应该是 144 字节。例如,如果我的有效载荷是 36 个字节,由于返回地址占用 8 个字节,我的 nop sled 将是 100 个字节。但是当我这样做时,它不起作用。所以我想也许我理解堆栈布局的方式是错误的。错了吗?

请注意,在我的情况下,缓冲区地址是已知的,并且使用 execstack 将堆栈设置为可执行文件,并使用 setarch 关闭了 ASLR。因此,如果返回地址被缓冲区地址覆盖,写入该缓冲区的代码就会运行。

我正在使用 x86 64 位机器。

如果我对堆栈布局的理解是正确的,我将提供有关我的 shellcode 的更多信息。

【问题讨论】:

  • 如果您知道要跳转到的确切地址,我认为您真的不需要 nop 雪橇。不过不应该受到伤害,而且您需要某种填充来到达并覆盖返回地址。也可能是nops。
  • @user2357112 是的。无论哪种方式,它都应该工作。
  • 这听起来更像是一个“发现大脑放屁”的错误,而不是“我的理解有缺陷,我需要学习一些东西”的错误。我认为您对堆栈布局的理解没有任何明显错误。
  • @user2357112 那是实际代码。它可以用 gcc 编译。我认为这个程序应该是坏的......作为一个例子
  • 运行你的shellcode会产生段错误?您使用的是哪个版本的 Linux 内核?在后来的内核(和 GCC)中塞进了一堆相当漂亮的安全特性,这些特性阻碍了大多数简单的 shellcode 尝试。

标签: assembly x86-64 shellcode


【解决方案1】:

1) 你没有利用易受攻击的代码,因为它有puts() 函数,你正在利用它,因为它使用了gets() 函数,这里容易受到堆栈溢出的影响。

2) 当你有一个字符buf[123] 时,如果你输入 122 个字符,然后输入一个空终止符,堆栈就可以了。但是,当您输入更多内容时,会发生这种情况:

假设它是 buf[4],当你执行 gets() 时

input AAAA
EBP - 4 => will be AAAA

input AAAAAAAA (8 bytes)
EBP -4 => AAAA
EBP also => AAAA

if you enter 12x A
function return address will be 0x41414141

现在您还将覆盖函数返回地址,所以它也将是 AAAA 0x41414141! 从那里你需要将返回地址指向你的 shellcode 地址才能执行 shellcode。

所以当调用函数和溢出时,布局是:

Buffer for temporary storage
local variables
The saved EBP
Function return address
Function's arguments
Stack frame

所以它是从下到上。 实际上对于大变量最好使用 metasploit pattern_offset.rb,它会生成大字符串,当您找到 EIP 值时,您可以使用 patter_offset.rb 的输出来检测覆盖 EIP 所需的精确填充以执行 shellcode。

所以实际上要覆盖函数返回地址,主要是你需要[变量大小] + 8。但这取决于局部变量,它们的大小,它们的顺序等。还取决于编译器,架构等。主要是通过尝试和 pattern_offset.rb 等来完成。

【讨论】:

    【解决方案2】:

    当例程开始时,它的返回地址在堆栈上。我将假设 rsp = 0x428 的值,以便我们可以查看真实(但任意选择的)数字:

    0x428 return addr
    

    然后我们推送rbp

    0x428 return addr (8 bytes)
    0x420 original rbp (8 bytes)
    

    然后我们分配缓冲区,即 123 字节 + padding = 0x80

    0x428 return addr (8 bytes)
    0x420 original rbp (8 bytes)
    0x418 last eight bytes of buffer+padding 
      ...
    0x3a0 buffer (128 bytes)
    

    因此,如果您希望在 0x428 处粉碎返回地址,那么您对 ​​gets() 的响应总共必须是 144 个字节长,最后 8 个字节由您的 shellcode 修改以指向其他地方(通常指向缓冲区本身)。

    假设堆栈图中的 sfp 表示“已保存的帧指针”(也称为 rbp),那么 是的,您的理解是正确的。 如果它不起作用对你来说,那么可能是你的 shellcode 有问题。 (这可能是一个单独的问题。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-25
      相关资源
      最近更新 更多