【问题标题】:How can I reprogram my shellcode snippet to avoid null bytes?如何重新编程我的 shellcode 片段以避免空字节?
【发布时间】:2019-09-06 12:22:14
【问题描述】:

我编写了一段 x64 linux 程序集。它所做的只是打印一行“Hello world”,仅此而已。但是我想做的是通过 objdump 从它的目标文件中复制字节,这样我就可以为我的缓冲区溢出攻击制作自己的 shellcode。

我面临的问题是 shellcode 包含大量空字节,这将终止我的 shellcode 的执行。

root@kali:~/C scripts/shellcode/Assembly Based Shellcode# cat print.asm
section .text
 
global _start
 
_start:
 
    mov rax, 1
    mov rdi, 1
    mov rsi, message
    mov rdx, 12
    syscall
 
    mov rax, 60
    xor rdi, rdi
    syscall
 
message:
    db "Hello world", 10
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# nasm -f elf64 print.asm && ld print.o -o print && ./print
Hello world
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# objdump -D print.o
 
print.o:     file format elf64-x86-64
 
 
Disassembly of section .text:
 
0000000000000000 <_start>:
   0:   b8 01 00 00 00          mov    $0x1,%eax
   5:   bf 01 00 00 00          mov    $0x1,%edi
   a:   48 be 00 00 00 00 00    movabs $0x0,%rsi
  11:   00 00 00
  14:   ba 0c 00 00 00          mov    $0xc,%edx
  19:   0f 05                   syscall
  1b:   b8 3c 00 00 00          mov    $0x3c,%eax
  20:   48 31 ff                xor    %rdi,%rdi
  23:   0f 05                   syscall
 
0000000000000025 <message>:
  25:   48                      rex.W
  26:   65 6c                   gs insb (%dx),%es:(%rdi)
  28:   6c                      insb   (%dx),%es:(%rdi)
  29:   6f                      outsl  %ds:(%rsi),(%dx)
  2a:   20 77 6f                and    %dh,0x6f(%rdi)
  2d:   72 6c                   jb     9b <message+0x76>
  2f:   64                      fs
  30:   0a                      .byte 0xa
root@kali:~/C scripts/shellcode/Assembly Based Shellcode#

我希望 shellcode 没有空字节。然而事实并非如此。 有人可以帮我改正我的代码吗?

【问题讨论】:

  • 使用字节加载并寻找其他替代方法来避免零字节。请参阅指令集参考。例如。 mov rax, 1可以写成xor eax, eax; mov al, 1
  • 不幸的是,我找不到那个集合参考。如果你这样做,请也给我。即使这对我也有帮助,谢谢!
  • 请注意,除了修复 NUL 字节之外,您还需要使您的代码位置独立。让我看看我能不能写一个合适的答案。
  • @fuz,是的,请!位置无关是什么意思。

标签: assembly x86-64 nasm shellcode


【解决方案1】:

您似乎对汇编和缓冲区溢出感到困惑。

我像这样重新编程了汇编文件:

section .text

GLOBAL _start

_start:

    xor rax, rax                  ; Clear the RAX register
    push rax                      ; Push the NULL byte [ string terminator ]
    add al, 0x1                   ; RAX = 1, to put the system in sys_write mode
    mov rdi, rax                  ; RDI = 1, to setup the fist parameter for write ( file descriptor to write to ). The integral value for 'stdout' is 1.
    lea rsi, [rel msg+0x41414141] ; Move the relative RIP address of msg to RSI to prepare the string buffer for writing to the stdout. Also add a large 4-byte offset to evade NULL bytes.
    sub rsi, 0x41414141           ; Subtract that large offset to make the RSI point correctly.
    xor rdx, rdx                  ; Empty the 3rd argument for write
    mov dl, 0xc                   ; RDX = 12, 12 ==> string length of msg
    syscall                       ; system call

msg db "Hello world", 0xa

编辑:根据评论部分的讨论,我删除了终止的 NULL 字节

section .text

GLOBAL _start

_start:

    push 0x1
    pop rax
    mov rdi, rax
    mov rbx, 'AAAAArld'
    shr rbx, 0x28
    push rbx
    mov rbx, 'Hello wo'
    push rbx
    mov rsi, rsp
    push 0xc
    pop rdx
    syscall

然后我像这样编译程序:

root@kali:~/Desktop/assembly# nasm -f elf64 main.asm; ld main.o -o main.elf; ./main.elf 
Hello world
Segmentation fault

段错误真的无关紧要,因为您想将其用作 shellcode 进行缓冲区溢出攻击,所以无论如何都有段错误。

现在,从目标代码中提取字节:

root@kali:~/Desktop/assembly# for i in $(objdump -D main.o | grep "^ " | cut -f2); do echo -n "\x$i"; done; echo
\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a

[ OPTION ] : 您可以在执行内存损坏漏洞利用之前测试您的 shellcode

为此,只需从上述命令中复制字节并创建一个新的 C 文件 [您似乎也对 C 脚本感到困惑]

#include <stdio.h>

int main(void) {

    char shellcode[] = "\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a";
    int (*ret)() = (int (*)())shellcode;
    // The above line will create an integer pointer ret make it point to a function which doesn't require parameter [ indicated by the () ]. Then it will type casting to cast the shellcode to a function pointer of the same type.
    // So this will essentially cast your shellcode array address to a function pointer which you can later use to call it as a function and execute the code.
    ret(); // Execute the shellcode

}

然后编译程序并确保堆栈可执行,否则您最终会在此处遇到段错误,并且 shellcode 将无法执行。

root@kali:~/Desktop/assembly# gcc -z execstack test.c; ./a.out
Hello world
Segmentation fault

从上面的代码看来,shellcode 似乎工作得很好!

我在一个基本的应用程序上试过这个,它可以工作,所以你的问题应该得到解决。

另一点要提的是,只有当您的可执行应用程序 [您将要利用的应用程序] 使用像 strcpy() 这样在 NULL 字节上停止的输入法时,您才必须删除 NULL 字节。

如果您的可执行文件使用gets()fgets() 等输入函数,那么您无需担心NULL 字节[除非您也期待格式字符串漏洞] 这来自 fgets 的手册页:

fgets() 从流中最多读入一个小于 size 的字符并将它们存储到 s 指向的缓冲区中。 EOF 或换行符 后停止读取。如果换行符是 读取,它被存储到缓冲区中。一个终止的空字节 ('\0') 存储在缓冲区中的最后一个字符之后。

这显然意味着 NULL 字节不应该打扰您的利用。

我希望你的疑虑得到解决!

【讨论】:

  • 谢谢!您的回答似乎澄清了我的疑虑,是的,新的程序集文件完美运行!将此标记为正确答案。
  • push rax 似乎毫无意义。 write 系统调用采用显式长度,而不是以 0 结尾的字符串。因此,即使这确实发生在 RSP 指向msg: db ... 结束后的 8 个字节的情况下,也没有必要。还有几个浪费的 REX 前缀。此外,您可以通过向前跳过字符串来避免sub rsi, 0x41...,因此rel32 是负数,使用高0xff 字节而不是零。 (如果你也想避免 0xff 字节,这是一个好技巧。或者我想确保换行符在末尾,对于缓冲区大小错误的 fget。)
  • 另外,push 1 / pop rax 是将寄存器设置为一个小的立即常数的更紧凑的方法。 Tips for golfing in x86/x64 machine code
  • @PeterCordes 你是对的,代码有点长,也有点不必要。但毕竟,从这个问题来看,用户似乎对组装不是很有经验,所以我从其他 cmets 中提取了一些要点,让用户更容易理解。
  • 是的,我赞成其余答案中有用的 cmets / text。但特别是对于没有经验的用户来说,引入问题中不存在的push rax 只会误导和混淆。它仅在您未提及且不需要的非常特定的情况下以 0 终止 msg
猜你喜欢
  • 2020-07-14
  • 1970-01-01
  • 2019-09-10
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
相关资源
最近更新 更多