Post

17. Spawning a shell

17. Spawning a shell

Now let’s explore how to create shellcode that spawns a shell. This you will mostly use in exploit development.

1. The execve System Call

The most common way to spawn a shell in Linux is using the execve system call:

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

System Call Numbers:

  • x86: execve = 11 (0x0b)
  • x64: execve = 59 (0x3b)

2. Assembly Implementation

Let’s start with the most straightforward approach that stores the string in the .data section:

x86 Version (32-bit)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
; execve32.s
section .data
    shell db '/bin/sh', 0    ; The string with null terminator

section .text
    global _start

_start:
    ; Set up execve syscall
    mov eax, 11         ; execve syscall number
    mov ebx, shell      ; pointer to "/bin/sh" string
    xor ecx, ecx        ; argv = NULL
    xor edx, edx        ; envp = NULL
    int 0x80

Assembling and Linking

1
2
3
nasm -f elf32 execve32.s -o execve32.o
ld -m elf_i386 execve32.o -o execve32
./execve32

x64 Version (64-bit)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; execve64.s
section .data
    shell db '/bin/sh', 0       ; Null-terminated string

section .text
    global _start

_start:
    xor rax, rax                ; clear registers
    mov rdi, shell              ; filename -> first argument
    xor rsi, rsi                ; argv = NULL
    xor rdx, rdx                ; envp = NULL
    mov rax, 59                 ; syscall number for execve in x64
    syscall                     ; invoke system call

Assembling and Linking

1
2
3
nasm -f elf64 execve64.s -o execve64.o
ld execve64.o -o execve64
./execve64

This works! You should get a shell. Now let’s examine the machine code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ objdump -d execve32 -M intel

execve32:     file format elf32-i386


Disassembly of section .text:

08049000 <_start>:
 8049000:	b8 0b 00 00 00       	mov    eax,0xb
 8049005:	bb 00 a0 04 08       	mov    ebx,0x804a000
 804900a:	b9 00 00 00 00       	mov    ecx,0x0
 804900f:	ba 00 00 00 00       	mov    edx,0x0
 8049014:	cd 80                	int    0x80

The Problems:

  1. Multiple null bytes (00) in every instruction with immediate values
  2. Absolute address (0x804a000) for the string - not position independent
  3. Relies on data section - won’t work when injected into another process

Let’s extract the raw bytes to see the full picture:

1
2
3
4
$ ./shellcode_kit.sh  -a x86 --extract execve32
$ cat shellcode_execve32.txt 
\xb8\x0b\x00\x00\x00\xbb\x00\xa0\x04\x08\xb9\x00\x00\x00\x00\xba\x00\x00\x00\x00\xcd\x80

This clearly shows why this won’t work as shellcode - it’s full of null bytes!

In this chapter, we learned how to spawn a shell using the execve syscall. In the next part of this series, we will study how to avoid bad characters in shellcode - essential for bypassing filters and working with constrained environments where certain bytes can break our exploits.

This post is licensed under CC BY 4.0 by the author.