Assembly x86_64 - Spawning a Shell

Assembly Code

We're going to use vim to write our code


[ 192.168.0.18/24 ] [ /dev/pts/88 ] [~/binexp/asm]
→ vim 7.asm


section .text
        global _start

_start:
        xor esi, esi
        xor edx, edx

        push 0x3b
        pop rax
        mov rbx, 0x68732f2f6e69622f
        push rsi
        push rbx
        mov rdi, rsp
        syscall

Now let's check out what is new in the above code:


_start:
        xor esi, esi
        xor edx, edx


using xor on the same register has the property of being equivalent to mov esi, 0 but being shorter (only 2 bytes) the processor recognizes the special case and treats it as a mov esi, 0 so the execution time is the same. so we clear out the esi and edx registers,


push 0x3b                       ;push the value of the syscall id onto the stack (0x3b is 59)
pop rax                         ;take the out the top of the stack to put it into rax

Next we push the value 0x3b (59) onto the stack, and then pop the value out into rax, The equivalent is mov rax, 59 However this results in a shorter shellcode as we're going to see later on. Now since we have our execve() syscall, we want to give it an arguement, we want it to spawn /bin/sh and we want it to be 8 bytes so we get the following: /bin//sh:


[ 192.168.0.18/24 ] [ /dev/pts/3 ] [~/binexp/asm]
→ echo '/bin//sh' | xxd
00000000: 2f62 696e 2f2f 7368 0a                   /bin//sh.

[ 192.168.0.18/24 ] [ /dev/pts/3 ] [~/binexp/asm]
→ echo 'hs//nib/' | xxd
00000000: 6873 2f2f 6e69 622f 0a                   hs//nib/.

So we get our following mov instruction:


mov rbx, 0x68732f2f6e69622f     ; put the little endian hex val of '/bin//sh'  into rbx

Compiling

Here we're going to use nasm to compile our assembly code and then ld to create the binary file:


[ 192.168.0.18/24 ] [ /dev/pts/3 ] [~/binexp/asm]
→ nasm -f elf64 7.asm

[ 192.168.0.18/24 ] [ /dev/pts/3 ] [~/binexp/asm]
→ ld 7.o -o 7

[ 192.168.0.18/24 ] [ /dev/pts/3 ] [~/binexp/asm]
→ ./7

[ 192.168.100.1/24 ] [ /dev/pts/3 ] [/home/nothing/binexp/asm]
→ echo $0 ; exit
bash
exit

[ 192.168.0.18/24 ] [ /dev/pts/3 ] [~/binexp/asm]
→ echo $0
/bin/zsh

And that's it! But if we wanted to create shellcode for binary exploitation, we would adjust the assembly code as follows:


[bits 64]

xor esi, esi                    ; xor out esi and edx
xor edx, edx

push 0x3b                       ;push the value of the syscall id onto the stack (0x3b is 59)
pop rax                         ;take the out the top of the stack to put it into rax

mov rbx, 0x68732f2f6e69622f     ; put the little endian hex val of '/bin//sh'  into rbx

push rsi                        ; push the value of rsi
push rbx                        ; push the value of rbx
mov rdi, rsp                    ; move the value of rsp ( ) into rdi (first arguement)
syscall

And then we would compile it not with the elf64 flag, but this time we don't need a binary file, we want what's called shellcode to use in conjunction with python pwntools:


[ 192.168.0.18/24 ] [ /dev/pts/8 ] [~/binexp/asm]
→ nasm -f bin 7.asm

[ 192.168.0.18/24 ] [ /dev/pts/8 ] [~/binexp/asm]
→ cat 7
11j;XH/bin//shVSH% 

Now let's view the hexdump of our shellcode inside of python pwntools:


[ 192.168.0.18/24 ] [ /dev/pts/7 ] [~/binexp/asm]
→ vim hexdump.py


from pwn import *

#read the shellcode file we compiled
with open('7', 'rb') as f:
  shellcode = f.read()

print(shellcode)


Here basically we take our shellcode file (named 7) and we store its contents into the shellcode variable. Then we print it:


[ 192.168.0.18/24 ] [ /dev/pts/18 ] [~/binexp/asm]
→ python3 hexdump.py
b'1\xf61\xd2j;XH\xbb/bin//shVSH\x89\xe7\x0f\x05'

However this isn't all that accurate for us. Here you can see the non-ascii characters being represented as \x00 \x01 \x02 and such. So to get more information on the shellcode characters we should use the hexdump function that's built-in to pwntools:


from pwn import *

#read the shellcode file we compiled
with open('7', 'rb') as f:
  shellcode = f.read()

print(hexdump(shellcode))

And we get the following result:


[ 192.168.0.18/24 ] [ /dev/pts/18 ] [~/binexp/asm]
→ python3 hexdump.py
00000000  31 f6 31 d2  6a 3b 58 48  bb 2f 62 69  6e 2f 2f 73  │1·1·│j;XH│·/bi│n//s│
00000010  68 56 53 48  89 e7 0f 05                            │hVSH│····│
00000018

And that's it! we have some payload ready to be used for binary exploitation purposes.