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:
- Multiple null bytes (
00) in every instruction with immediate values - Absolute address (
0x804a000) for the string - not position independent - 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.