我如何重新编程我的 shellcode 片段以避免空字节?

How can I reprogram my shellcode snippet to avoid null bytes?

我编写了一段 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    [=10=]x1,%eax
   5:   bf 01 00 00 00          mov    [=10=]x1,%edi
   a:   48 be 00 00 00 00 00    movabs [=10=]x0,%rsi
  11:   00 00 00
  14:   ba 0c 00 00 00          mov    [=10=]xc,%edx
  19:   0f 05                   syscall
  1b:   b8 3c 00 00 00          mov    [=10=]x3c,%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 没有空字节。然而事实并非如此。 有人可以帮我更正我的代码吗?

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

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

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() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('[=19=]') is stored after the last character in the buffer.

这显然意味着 NULL 字节不应影响您的利用。

希望你的疑惑得到解开!