首页 > 解决方案 > 如何重新编程我的 shellcode 片段以避免空字节?

问题描述

我已经编写了一段 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 没有空字节。然而事实并非如此。有人可以帮助我并更正我的代码吗?

标签: assemblyx86-64nasmshellcode

解决方案


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

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

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

[选项]:您可以在执行内存损坏漏洞之前测试您的 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 似乎工作得很好!

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

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

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

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

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

希望你的疑惑能解开!


推荐阅读