Slides for a college course at City College San Francisco. Based on "The Shellcoder's Handbook: Discovering and Exploiting Security Holes ", by Chris Anley, John Heasman, Felix Lindner, Gerardo Richarte; ASIN: B004P5O38Q.
Instructor: Sam Bowne
Class website: https://samsclass.info/127/127_S18.shtml
4. Shellcode
• Written in assembler
• Translated into hexadecimal opcodes
• Intended to inject into a system by
exploiting a vulnerability
• Typically spawns a root shell, but may do
something else
5. System Calls (or Syscalls)
• Syscalls directly access the kernel, to:
– Get input
– Produce output
– Exit a process
– Execute a binary file
– And more
• They are the interface between protected
kernel mode and user mode
6. Protection Rings
• Although the x86
provides four
rings, only rings 0
and 3 are used by
Windows or Unix
• Ring 3 is user-
land
• Ring 0 is kernel-
land
• Links Ch 3a-3c
7. Protecting the Kernel
• Protected kernel mode
– Prevents user applications from compromising
the OS
• If a user mode program attempts to
access kernel memory, this generates an
access exception
• Syscalls are the interface between user
mode and kernel mode
8. Libc
• C library wrapper
• C functions that perform syscalls
• Advantages of libc
– Allows programs to continue to function
normally even if a syscall is changed
– Provides useful functions, like malloc
– (malloc allocates space on the heap)
• See link Ch 3d
9. Syscalls use INT 0x80
1. Load syscall number into EAX
2. Put arguments in other registers
3. Execute INT 0x80
4. CPU switches to kernel mode
5. Syscall function executes
10. Syscall Number and Arguments
• Syscall number is an integer in EAX
• Up to six arguments are loaded into
– EBX, ECX, EDX, ESI, EDI, and EBP
• For more than six arguments, the first
argument holds a pointer to a data
structure
11. exit()
• The libc exit function does a lot of
preparation, carefully covering many
possible situations, and then calls SYSCALL
to exit
12. Disassembling exit
• gdb e
– disassemble main
– disassemble exit
– disassemble __run_exit_handlers
• All that stuff is error handling, to prepare
for the syscall, which is at the label _exit
• disassemble _exit
13. Disassembling _exit
• syscall 252 (0xfc), exit_group() (kill all threads)
• syscall 1, exit() (kill calling thread)
– Link Ch 3e
15. Shellcode Size
• Shellcode should be as simple and
compact as possible
• Because vulnerabilities often only allow a
small number of injected bytes
– It therefore lacks error-handling, and will
crash easily
21. C Code to Test Shellcode
• From link Ch 3k
• Textbook version explained at link Ch 3i
22. Compile and Run
• Textbook omits the "-z execstack" option
• It's required now or you get a segfault
• Next, we'll use "strace" to see all system
calls when this program runs
• That shows a lot of complex calls, and
"exit(0)" at the end
25. Getting Rid of Nulls
• We have null bytes, which will terminate
a string and break the exploit
26. Replacing Instructions
• This instruction contains nulls
– mov ebx,0
• This one doesn't
– xor ebx,ebx
• This instruction contains nulls, because it
moves 32 bits
– mov eax,1
• This one doesn't, moving only 8 bits
– mov al, 1
30. Beyond exit()
• The exit() shellcode stops the program, so
it just a DoS attack
• Any illegal instruction can make the
program crash, so that's of no use
• We want shellcode that offers the
attacker a shell, so the attacker can type
in arbitrary commands
31. Five Steps to Shellcode
1. Write high-level code
2. Compile and disassemble
3. Analyze the assembly
4. Clean up assembly, remove nulls
5. Extract commands and create shellcode
32. fork() and execve()
• Two ways to create a new process in Linux
• Replace a running process
– Uses execve()
• Copy a running process to create a new
one
– Uses fork() and execve() together
37. execve() Arguments
1. Pointer to a string containing the name
of the program to execute
– "/bin/sh"
2. Pointer to argument array
– happy
3. Pointer to environment array
– NULL