This is the last payload that I’ve debugged so far for the 5th assignment on SecurityTube Linux Assembly Expert certification.
This time I opted by looking at the payload linux/x64/exec with the option to run ‘/bin/sh’.
Here’s how I generated the payload with msfvenom.
$> msfvenom -p linux/x64/exec -f elf -o exec CMD="/bin/sh"
This is the smallest payload so far, with only 47 bytes and 8 of them are for the command that one wants to run.
0x400078 push 0x3b
0x40007a pop rax
0x40007c movabs rbx, 0x68732f6e69622f
0x400086 push rbx
0x400087 mov rdi, rsp ; argument #1 for method sub_4000a0
0x40008a push 0x632d
0x40008f mov rsi, rsp ; argument #2 for method sub_4000a0
0x400092 push rdx
0x400093 call sub_4000a0
0x400098 db 0x2f ; '/'
0x400099 db 0x62 ; 'b'
0x40009a db 0x69 ; 'i'
0x40009b db 0x6e ; 'n'
0x40009c db 0x2f ; '/'
0x40009d db 0x73 ; 's'
0x40009e db 0x68 ; 'h'
0x40009f db 0x00 ; '.'
0x4000a0 push rsi ; XREF=EntryPoint+27
0x4000a1 push rdi
0x4000a2 mov rsi, rsp
I continue having no luck in disassembling shellcode created with msfvenom, neither using hte, gdb nor objdump. So this time I remembered I have a license for Hopper, an impressive debugger with a nice version for Linux (can’t wait for v4 to be released on Linux).
The first two lines load 0x3b into rax, which immediately tells us that we will be running on execve() syscall (0x3b == 59 or __NR_execve as per unistd_64.h).
The way the code is setup actually took me quite a while to understand, but once I got it I thought it is very smart.
If you remember your Operating Systems classes, this is the signature for execve() syscall
int execve(const char *filename, char *const argv, char *const envp);
This means we need to pass in the following arguments in the following registers:
RDI – address of the string containing the path of the application
RSI – address to the array of arguments including the applications as 1st arg
RDX – address to array of environment variables
So, where we left off in the code they are just filling rbx with the string ‘/bin/sh‘, pushing it on the stack and loading it’s address back onto rdi.
One continues by loading ‘-c‘ onto the stack and loading the address onto rsi.
rdx will be 0 (a.k.a NULL) for *envp.
For the last argument, which is the actual command to run, a similar approach to the Jump-Call-Pop is used. The function call jumps over the definition of the null terminated string representing the command to run thus leaving the string’s address as RET address on the stack. Ontop one pushes both rsi and rdi and the stack ends up with
[ address of ‘/bin/sh’ on the stack, address of ‘-c’ on the stack, address of the command string ]
which makes up our the const char* argc argument to execve().
I was not lying, I thought the strategy for a variable length command string to be very smart.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification.
Student ID: SLAE64-1440