domenica 20 febbraio 2022

Alan c2 Framework v6.0: Alan + JavaScript = ♡


Twitter: @s4tan
Download: https://github.com/enkomio/AlanFramework/releases/latest
Documentation: https://github.com/enkomio/AlanFramework/tree/main/doc

Alan v6.0 was release with a new cool feature: JavaScript execution. The scripts are executed in memory and do not depend on any third party program. The scripts source code can be downloaded from the GitHub Alan repository.

Being able to extend the framework is a mandatory feature in today red-team tools. Each team has its own methodology to perform a red-team activity and being able to customize or extend the tool capabilities is mandatory. One of the main goal with Alan was to provide a framework that can be easily adapted to vairous modus-operandi. Alan v6.0 adds a new feature to support an easy extension, it allows the operator to execute JavaScript file directly in memory. This feature is implemented inside an Alan core module and does not depend on any third party program.

In other tools, this kind of feature requires the operator to compile C code by following a specific process. This might be overhelming and unecessary complex. Javascript is an easy language and even novices can become proficient in a short time.

However, being able to execute JavaScript code is not enough, since in most cases the operator needs to interact with native Windows function to perform a given action. Alan provides an interface to call native Windows functions by using the handy JavaScript syntax. This blog post explores the details of this feature and how to use it to extend the Alan capabilities.

Gettin Started

Executing a JavaScript file in Alan is extremyl easy, just use the run command and specify a file with .js extension. In order to call a Windwos function, Alan implements the Win32 module that exposes two methods: GetProcAddress and LoadLibrary. These are the basic methods to call virtually any Windows functions. Let's try to write a simple file that prints the process ID.

import * as win32 from 'Win32';

var kernel32 = win32.LoadLibrary("kernel32.dll");
var GetCurrentProcessId = win32.GetProcAddress(kernel32, "GetCurrentProcessId");
var IsWow64Process = win32.GetProcAddress(kernel32, "IsWow64Process");
var GetCurrentProcess = win32.GetProcAddress(kernel32, "GetCurrentProcess");


var my_pid = GetCurrentProcessId();
var is_wow64 = new Array(4);
IsWow64Process(GetCurrentProcess(), is_wow64);

var msg = "Hello world from Javascript executed in process: " + my_pid;
if (is_wow64[0] == 1)
	msg += " - I'm running under Wow64 :)";
print(msg);
The script opens the Win32 module in order to load the Kernel32 DLL by calling the LoadLibrary function. Using the obtained handle, the GetCurrentProcessId function address is resolved by using the GetProcAddress function. The other functions are resolved in the same way. You can now use the resolved functions by calling them as standard JavaScript functions. As final step, the script prints a string showing a message containing information extracted from the Windows APIs.

A fundamental step of the entire process is being able to easily test the script during the development stage. In this new Alan version, a new folder named tools was added to the Alan package. It contains the files cqjsx86.exe and cqjsx64.exe. These files are JavaScript interpreters in x86 and x64 version. Let's try to run our script with both files to see what result is produced (the --file option is used to specify the file path).

C:\Alan.v6.0.511.24\tools>cqjsx64.exe --file test.js
Hello world from Javascript executed in process: 15532

C:\Alan.v6.0.511.24\tools>
If we use the wqjsx86.exe program, we obtain the following result (I'm running my test in a x64 OS).
C:\Alan.v6.0.511.24\tools>cqjsx86.exe --file test.js
Hello world from Javascript executed in process: 30844 - I'm running under Wow64 :)

C:\Alan.v6.0.511.24\tools>
As can be noticed, the result is different according to the used version.
Once that the script works as expected, we can run it in the Alan agent by simply using the run command and specifying the full path of the script.

Windows API Data Structure Interoperation

The GetProcAddress and the LoadLibrary should provide the basic functionality to call every Wind32 APIs. However, interacting with a native API might requires further information. A typical example are parameters that are used as buffer (both in input and output). When this is the case, the following rules apply:
  • Each JavaScript Array is considered as an array of bytes when passed to a Win32 function. Each byte is casted to uint8_t (this causes a data truncation and a potential data corruption). If the array contains other complex data types (such as a String) its value is converted to NULL.
  • Boolean values are converted to 1 if true and 0 if false.
  • Each number is converted to a 32-bit interger on x86 process, and to 64-bit integer on x64 process.
  • Each JavaScript String is converted to an ascii string when passed to a Win32 function.
  • You can not call functions with more than 20 parameters.


The rules above imply that:
  • Each parameter passed by address to a Win32 function needs to be converted to an array (eg. to pass a LPDWORD you have to create an Array(4) parater if running in 32-bit or an Array(8) if running in 64-bit).
  • If a Win32 function accept a structure, it needs to be converted to an Array too. For example, a PROCESSENTRY32 structure must be represented as an Array and then parsed by refercing the fields by their offset (an example using this structure is presented later with some helper function to simplify the job).


All these rules might be quite annoying during the development of a not trivial script. In the next section I'll show how to easier the development task by implementing an lsass process memory dumper.

Implementing a simple lsass.exe process memory dumper

This is a perfect case to explore more in-depth this new feature. Being able to dump the process memory of the lsass process is very import to further compromise an host. There are various techniques to achieve this goal, but for the sake of simplicity I'll go for the simpler one, by using the MiniDumpWriteDump function. I'll put the script on GitHub so you can have a look at its full source code.

Let's suppose that our Agent is running as Administrator, then the following points have to be considered to write the dumper:
  • Enable SE_DEBUG_NAME privilege.
  • Scan all processes to identify the lsass.exe process.
  • Create a mini dump of the lsass.exe process.


As first step we have to load all the needed functions. This is a trivial task and already demonstrated in the previous example. Enabling SE_DEBUG_NAME is the next step. To perform this action we have to use a TOKEN_PRIVILEGES structure. This structure is quite simple, so for this task we will just create an array of 0x10 bytes and reference the sTP.Privileges[0].Luid, the sTP.PrivilegeCount and the sTP.Privileges[0].Attributes by their array offset. After calling the AdjustTokenPrivileges function we are now reayd to proceed with the next and probably most complex step.

We have to identify the lsass.exe process. To achieve this goal we use the CreateToolhelp32Snapshot function to obtain a snapshot and loop through all processes untile we find a process whose name is lsass.exe. This implies the usage of a PROCESSENTRY32 structure which is not that simple. To easies the task I created various JavaScript functions helper that serialize an object to a JavaScript array. The serialization function inspects the prefix of each field name and according to its value a specific serialization action is performed. For example, field names that start dw_ are serializated as DWORD. Field names that start with p_ are serializated to a four bytes array or eigth bytes array according to the value of a global variable that I defined at the start of the script (this step can be more dynamic by using the IsWow64Process function). Thanks to these functions, working with structures is now a lot easier (see the script source code for full details).

The final step is to create a file and call the MiniDumpWriteDump function to create a file dump that you can now download to your machine for post-processing.

Demo

Now that we have create our script to dump the lsass.exe process memory, let's use it. The video below provides a demonstration about how to dump the lsass.exe process memory by running our JavaScript script in the agent.

giovedì 20 gennaio 2022

Analyzing an IDA Pro anti-decompilation code


Twitter: @s4tan
GitHub: https://github.com/enkomio/

In this post I'll analyze a piece of code that induces IDA Pro to decompile the assembly in a wrong way. I'll propose a fix, but I'm open to more elegant solutions :)

The function that we want to decompile has the following assembly code (I'm using IDA Pro v7.6):

.text:1001BC95 56                  push    esi
.text:1001BC96 FF 74 24 10         push    [esp+4+arg_8]     
.text:1001BC9A 8B 74 24 10         mov     esi, [esp+8+arg_4] 
.text:1001BC9E 56                  push    esi
.text:1001BC9F FF 74 24 10         push    [esp+0Ch+arg_0]
.text:1001BCA3 52                  push    edx
.text:1001BCA4 51                  push    ecx
.text:1001BCA5 E8 57 20 FF FF      call    nullsub_1
.text:1001BCAA 8B 0A               mov     ecx, [edx]      
.text:1001BCAC 83 C4 14            add     esp, 14h
.text:1001BCAF 89 4E 0C            mov     [esi+0Ch], ecx
.text:1001BCB2 8B 42 04            mov     eax, [edx+4]
.text:1001BCB5 03 C1               add     eax, ecx
.text:1001BCB7 89 46 04            mov     [esi+4], eax
.text:1001BCBA 5E                  pop     esi
.text:1001BCBB C3                  retn


The function uses two arguments with an unconventional calling convention. If we decompile the code, we obtain:

int __cdecl sub_1001BC95(int a1, int a2)
{
  int *v2; // edx
  int v3; // ecx
  int result; // eax

  nullsub_1();
  v3 = *v2;
  *(a2 + 12) = *v2;
  result = v3 + v2[1];
  *(a2 + 4) = result;
  return result;
}
In IDA Pro the v2 variable (corrisponding to the line at address 0x1001BCAA) is colored in red, since its value might be undefined.

Custom calling convention might cause some problems to the decompilation process (see this), but, in general, there exist an easy fix to it: it is enough to inform IDA Pro that the function uses a custom calling convention. By modifying the function, we can set the new type with the following definition:

int __usercall sub_1001BC95@<eax>(PUCHAR arg0@<edx>, int garbage, PUCHAR arg1)
with this new definition, the decompiled code now looks like the following:
int __usercall sub_1001BC95@<eax>(PUCHAR arg0@<edx>, int garbage, PUCHAR arg1)
{
  int *v1; // edx
  int v2; // ecx
  int result; // eax
  int v4; // [esp+Ch] [ebp+8h]

  nullsub_1();
  v2 = *v1;
  *(v4 + 12) = *v1;
  result = v2 + v1[1];
  *(v4 + 4) = result;
  return result;
}
We haven't done any progress at all. The only place where we haven't checked is the nullsub_1 function, the problem must be in its call. If we analyze this function, we notice that it has an empty body, as shown below.

.text:1000DD01 C3                  retn
Why is this function causing problems? The answer is in the software convention used by the compiler. During the compilation, the compiler considers some registers as volatile. This means that the value of these registers, after a function call, should not be considered preserved ([1]). Among the volatile registers, there is EDX, which is exactly one of the registers used to pass a function parameter in the custom calling convention.

This code causes problem to the decompilation process that considers (correctly) the EDX register to have an undefined value after the function call.

I'm not aware of any particular IDA Pro command to inform the decompiler to not consider EDX as volatile, so the simpler solution that I found is to just remove the call instruction (I patched the bytes E8 57 20 FF FF with 90 90 90 90 90). The result is a much cleaner decompiled code, as shown below.

int __usercall sub_1001BC95@<eax>(PUCHAR arg0@<edx>, int garbage, PUCHAR arg1)
{
  PUCHAR v3; // ecx
  int result; // eax
  
  v3 = *arg0;
  *(arg1 + 3) = *arg0;
  result = &arg0[1][v3];
  *(arg1 + 1) = result;
  return result;
}
Now we can proceed to further improve the decompilation code (we can clearly see the usage of a struct in the code) now that the decompiled code represents the real intent of the assembly code.

Update:

I received a message on twitter and reddit that suggests to have a look at the __spoils keyword mentioned in this Igor’s tip of the week post [2] (shame on me for not having found it).

Its meaning is exactly what we need to solve the problem in a more elegant and generic way. It is enough to change the nullsub_1 function definition by adding the __spoils keyword, as show below:

void __spoils<> nullsub_1(void)
The decompilation result of the function sub_1001BC95 is the same as before with the exception that the call to the nullsub_1 function is still there (it is not necessary to patch the bytes anymore).

Links:

[1] Register volatility and preservation
[2] Igor’s tip of the week #51: Custom calling conventions

sabato 18 dicembre 2021

Alan c2 Framework v5.0 - All you can in-memory edition


Twitter: @s4tan
Download: https://github.com/enkomio/AlanFramework
Documentation: https://github.com/enkomio/AlanFramework/tree/main/doc

I just released version 5.0 of my C2 post-exploitation framework Alan. You can download the binaries and read the release notes at: https://github.com/enkomio/AlanFramework/releases/latest

My goal with the Alan project is to provide a post-exploitation framework that can help red-team operators to further compromise their targets. Tipically, each team has its preferred tools to exploit the target, an example is the pletora of tools that can perform the memory dump of the lsass process. Alan does not enforce any particular tool, instead it provides the ground to run whatever tools the operator like. All tools are executed in memory in the address space of a pre-configured host process, or injected into another process.

This feature is achieved by the introduction of the new command run. This command accepts a file path on the operator machine and executes it on the compromised host without touching the disk. It is possible to specify command-line arguments that are passed to the executed program (this feature is not so common in the other C2 framework ;)). For this reason I decided to name this version "All you can in-memory" :)

Other commands were also implemented that allow the operator to execute a program on the compromised host. In particular the command exec was added to execute a new process and the shell command was modified to accept an argument that is the command to execute (if no argument is specified, a command shell is presented to the operator).

Find below the video that shows the following features:

  • Creation of a x64 powershell agent.
  • In-memory execution of the nanodump utility by using the configured host program (raserver.exe in this case) and passing a command-line argument. The Process Hacker windows will display the execution of the raserver.exe process.
  • Execution of the program notepad.exe in background.
  • In-memory execution of the dumper utility by injecting the binary in the just created notepad process. In this case the raserver.exe is not executed.


domenica 26 settembre 2021

Alan post-exploitation framework v4.0 released


Twitter: @s4tan
Download: GitHub
Documentation: https://github.com/enkomio/AlanFramework/tree/main/doc

I just released version 4.0 of my post-exploitation framework Alan. You can download the binaries and read the release notes at: https://github.com/enkomio/AlanFramework/releases/latest


I also made a video that shows the following features:

  • Creation of two agents, a x86 and a x64 version
  • Migration of agent x86 to a process with a different integrity level
  • Execution of a command-shell on the compromised host and the execution of the x64 agent directly from the command-shell
  • Migration of the x64 agent to another x64 process
  • Restart of the Alan server to show that the agents reconnect to the server after the restart (the agent session is not lost)


sabato 15 maggio 2021

Alan - A post exploitation framework


Twitter: @s4tan
Download: GitHub (use this repo to report issues)
Documentation: https://github.com/enkomio/AlanFramework/tree/main/doc

I decided to dedicate a bit of my free time to develop a new project: Alan, a post exploitation framework. Doing red-team activities is not my main job, but I like this field and, as a malware analyst, I analyze a lot of programs that have a very similar intent.

The Alan concept is simple: the operator creates an agent file that is executed on the compromised host, and receives commands from a server under to control of the operator. The goal of the project is to provide a framework that has as primary target red-team activities. I implemented it by using C/Assembly for the agent and F# as backend (with .NET core, this ensure the excution on various OS).

Alan is implemented by considering weaknesses and missing features that I found in some of the currently available red-teaming tools. For example, a lot of tool claims that the traffic with the server is encrypted but they embed the key inside the request, or, in other cases, the key can be retrieved if the binary is available for reversing (too often I found a key generation algorithm based on a seed that can be easily computed).

A post-exploitation tool

Alan supports a good amount of features allowing the operator to further comprimise the target after the initial exploitation. Alan agent can be deployed in various formats, such as: Executable, DLL, Powershell and Shellcode. Below you can find a video that shows how to create an agent and interact with it by launching a command-shell on the remote host.



The agent can be easily customized and flexibility is a key feature for Alan. The agent profile can be updated at runtime, this means that you can change server address or even the communication protocol! The video below shows how to change the agent profile at runtime, by specifying a different server port and moving from HTTP to HTTPS.



Security Operation

Beign caugth by a blue team is something that should be avoided if you don't want to lose access to your target. Unfortunately, network traffic is something that cannot be hidden. Alan encrypts the network traffic in a strong way, but even if encrypted the requests might look suspicious. To avoid to raise any alerts, the operator can increase the delay between two requests or customize the requests and server reponses to look as a normal HTTP traffic from a know application. The video below shows the following features:
  • Create a powershell agent
  • Migrate to notepad.exe process. When the migration is completed in the Fiddler window is possible to see that the process sending requests became notepad.exe
  • A command-shell is executed and the original agent powershell file is deleted. By deleting the file there is no trace of the agent on disk and the execution is performed only in-memory. Then, the operator downloads some files to his system
  • The HTTP network traffic is inspected. The agent network traffic looks like normal traffic to an nginx server with default installation




In the next release I'll implement additional features and strenght a bit the code to try to avoid easily detection by AVs ;)

venerdì 12 giugno 2020

Deobfuscating C++ ADVobfuscator with Sojobo and the B2R2 binary analysis framework


Twitter: @s4tan
GitHub code: https://github.com/enkomio/Sojobo/tree/master/Src/Tools/ADVDeobfuscator

At Black Hat Europe 2014 - Amsterdam was presented a new obfuscation tool named ADVobfuscator. It is based on C++11 metaprogramming. The paper describes in depth how the strings and function calls are obfuscated.

ADVobfuscator demonstates how to use C++11/14 language to generate, at compile time, obfuscated code without using any external tool or a custom compiler.
Compile time obfuscators (like this one) are quite annoying to analyze since it is not easy to write a generic deobfuscator that it is based on code patterns recognition. In fact, the resulting binary code depends on the compiler, the used flags and so on. This will result in a series of corner cases that must be correctly handled to correctly deobfuscated the code. The worst part is that the handling of these corner cases might not be reused for a different sample that was compiled with a different compiler or with different flags. My idea to solve this problem, it is to write a deobfuscator that is based on flags extracted through the execution of generic heuristics. In this way, I can abstract the analysis from the code details.

Another interesting aspect of ADVObfuscator, it is that it was recently used to protect a malware sample that was analyzed in this very interesting blog post. In particular, in section "3. Latest variant of Team9 loader", it is possible to see a reference to the strings deobfuscation process.

In this blog post, I'll focus on the strings obfuscation part, by writing an utility that is able to decode the obfuscated strings. The deobfuscation utility uses the B2BR binary analysis framework to statically analyze the binary, and Sojobo to emulate the code.

The sample that I'll consider has SHA256 hash value: aaa9268b4a80f75eeb58b61cbd745523b1823d5adf54c615ad9ddf6b8fa0e806. It was used in a demo during my talk at HackInBo Safe Edition and can be downloaded from my GitHub repository.

Identify obfuscated strings

This is probably the most annoying part. We can't rely on specific code patterns, since according to the used compiler, the code might change. My idea was to abstract this concept and tries to identify interesting points, by using a series of heuristics. ADVObfuscator uses various methodologies to obfuscate the strings, some of them are reported below:

1400012AA    movdqa  xmm0, cs:xmmword_140023520          ; load obfuscated buffer
1400012B2    movdqu  [rbp+57h+var_90], xmm0
1400012B7    mov     rcx, r14
1400012BA
1400012BA loc_1400012BA:; CODE XREF: sub_1400011F4+D4↓j
1400012BA    mov     al, byte ptr [rbp+57h+var_90]
1400012BD    xor     byte ptr [rbp+rcx+57h+var_90+1], al ; deobfuscation
1400012C1    add     rcx, r15                            ; Increase counter
1400012C4    cmp     rcx, 0Eh                            ; check size
1400012C8    jb      short loc_1400012BA
1400012CA    mov     byte ptr [rbp+57h+var_90+0Fh], r14b ; set null byte
Unfortunately, not all deobfuscation tasks are implemented as in-line code, in some cases a function is invoked, as reported below.

140006161    movdqa  xmm0, cs:xmmword_140023800          ; load obfuscated buffer
140006169    lea     rcx, [rbp+var_30]                   ; pointer to the obfuscated buffer
14000616D    xor     eax, eax
14000616F    mov     [rbp+var_20], 627A6844h
140006176    movdqu  [rbp+var_30], xmm0
14000617B    mov     byte ptr [rbp+var_1C], al
14000617E    call    sub_140003684                       ; call deobfuscation function
140006183    mov     r9d, r15d
..............
140003684 sub_140003684   proc near  ; CODE XREF: sub_140005EB8+2C6↓p
140003684    lea     rax, [rcx+1]                        ; skip first byte, which is used as key
140003688    mov     r9d, 13h                            ; set string size
14000368E    mov     r8, rax                             ; pointer to buffer to decode
140003691
140003691 loc_140003691:; CODE XREF: sub_140003684+19↓j
140003691    mov     dl, [rcx]                           ; read XOR key
140003693    xor     [r8], dl                            ; deobfuscation
140003696    inc     r8                                  ; increment buffer pointer
140003699    sub     r9, 1                               ; decrement counter and check for termination
14000369D    jnz     short loc_140003691
14000369F    mov     [rcx+14h], r9b
1400036A3    retn
1400036A3 sub_140003684   endp
In the later case, it is possible to see that the string size is hardcoded inside the function body and not passed as input parameter. This means that we have a lot of functions like that, that differ only for some minor changes (like the string size).

Heuristics definition

As said, my main idea is to analyze all the functions that the B2R2 framework is able to identify and extract the flags that are based on the heuristics that I created. You can find all the defined heuristics in the associated source code. An excerpt from that list is presented below:
The heurstics above are used for the following tasks:
  • Identify all functions that deobfuscate a string. This task is useful to cover the case of the deobfuscation process defered to another function.
  • Identify the start of the code in charge for the deobfuscation.
  • Identify the address of the buffer that will be deobfuscated. This is done by identifying the deobfuscation operation.
  • Identify the end of the code in charge for the deobfuscation.

Emulation

At this point I have the following information: the functions that run a deobfuscation task and the related chunk of code in charge for this task. The final step is to emulate this code and read the deobfuscated string from memory. Before to run the emulation it is necessary to execute one final step. The heuristics might miss some important information, like the register that is used to increment the counter (in one of the example above we can see that r15 is used to increment the counter).

To cover this problem I used two strategies. In the first strategy, I do a backtrace analysis starting from the identified start address, and verify if the instruction is safe to be emulated. If so, I'll change the start address. The second strategy analyzes the instructions that should be emulated and if it notices that exists an operation that add two registers, I set the value of the source register, inside the emulator, to the value 1.
These strategies seem to be good enough to catch possible registers initialization code.

We can now run the emulator and read the decrypted string from memory.

Result

Below is reported a short video of the execution of the deobfuscation tool on the considered sample. With these information it shouldn't be too difficult to patch the original file and to NOP the deobfuscation operations. I tested it on various samples and it seems to work properly. If you found any errors just send me a message on twitter.


martedì 17 dicembre 2019

Writing a packer in Sacara

Twitter: @s4tan

GitHub project: https://github.com/enkomio/sacara

Release: https://github.com/enkomio/sacara/releases/latest

Sacara packer script: https://gist.github.com/enkomio/35b14084c1422db6740b5ed98cdb2db7

The Sacara project

It is a while that I don't write a blog post and recently I had the opportunity to work again on the Sacara project. I really like this project since it allows me to:
  • write code in x86 Assembly since all the VM code is implemented in x86 Assembly
  • to better understand how to implement a simple programming language (you can find the Sacara grammar file here)
  • how to write an assembler.
My previous post on Sacara is more than 1 year old so it is a good time to see if the project has any issues that can be resolved. After trying to write a new script, I immediately realize which was the biggest defect, writing a program was quite a pain due to the awkward syntax. It is time to improve this aspect with some syntax sugar :).

I released a new version 2.4 which adds some directives and other features that allows to easier the task of writing of a new script, leaving the VM core almost untouched.

Writing a simple packer

As done in my latest post about Sacara, even in this one I'll try to see how the AV industry will behave by analyzing a malicious program. I'll write a simple program that executes a Sacara script which purpose is to decode and run a malicious content.

This is the typical behavior of a packer, in my case I'll just decode and run the embedded content as if it was a shellcode. In general a packer will correctly maps the PE in memory and executes it by locating the Original Entry Point (OEP) but I'll leave this aspect out.

This time I'll create a C project and linking the Sacara static library, in this way we will have just one binary (and not a bunch of files as in my .NET test project created in my previous post).

For my test I wanted to use a real malware so I looked for some good stuff in VT. In the end I decided to use an unpacked Cobalt Strike payload whit SHA-1: 83a490496a7ea9562d6e3eb9a12a224fe50875e7. This is a perfect fit for my case since all Cobalt Strike modules are packed in a way that can be executed as a shellcode starting for the DOS header (the same happens with the Metasploit meterpreter_loader).

The overall design is quite simple, I'll embed the encoded malicious content as a PE resource and will use a Sacara script to decode and execute it. The Sacara script will be embedded as a PE resource too. The task done by the C code is deadly simple, just read the needed resources, allocates a memory region and run the script by providing the input buffer.

Implementation

I'll focus on the Sacara script, you can read the source code of the C code from the repository. The tasks done by the script are:
  1. Decode the password used to encrypt the malicious code
  2. RC4 decrypt the resource content
  3. Run the decrypted code
In order to easier the development and debugging of the script, I'll create various standalone scripts for each task and I'll merge them at the end. All the scripts in this post can be found in the test directory.

Decode the password used to encrypt the malicious code

As first step we want to retrieve the password used to encrypt the content. We don't want that a simple string search will reveals its value, so we will obfuscate it with a simple XOR operation by using a 1-byte key with value 0xA1.

Below you will find the relevant code with a simple test case, I think its comments are self explanatory.

// this routine will be used to stored the 
// script global data, all labels are global
proc global_data
password: 
 // encoded password
 byte 0xe0, 0xe1, 0xe2, 0xe3
endp

proc decode_password(pwd, pwd_len) 
 .mov index, 0
 
decode_pwd_loop:
 // read the byte to decode
 .mov pwd_offset, (pwd + index)
 .read.b pwd_offset 
 pop xored_char
 
 // decode the byte with hardcoded key
 .xor xored_char, 0xA1
 
 // write back the result
 pop xored_char
 .write.b pwd_offset, xored_char
 
 // check if completed
 .cmp index, pwd_len
 .inc index
 push decode_pwd_loop
 jumpifl 
 
 ret
endp

proc main
 // result must be the first variable if I want
 // to retrieve the result with SacaraRun, so set it to 0
 .mov result, 0
 
 // invoke the routine to decode the password
 .decode_password(password, 4)
 
 // read the decoded password as a double word at the specified offset
 .read.dw password
 pop result
 halt
endp
To test if it works, we will try to deobfuscate the buffer 0x41, 0x40, 0x43, 0x42. We have to first obfuscate it, so we will compute the XOR operation between the two integers 0x42434041 (little endian) and 0xA1A1A1A1, which result in 0xE3, 0xE2, 0xE1, 0xE0 (this is the same buffer that you will find at the top of the script).

Firs let's assemble it:
c:\SacaraAsm.exe test_decode_buffer.sacara

          -=[ Sacara SIL Assembler ]=-
Copyright (c) 2018-2019 Antonio Parata - @s4tan

[INFO] 2019-12-08 11:52:07 - VM code written to file: test_decode_buffer.sac
We can now test the script by running it, passing the input value 0x42434041 and expecting as result 0x42434041 (or 1111703617 in decimal notation) which is the little-endian hexadecimal notation of our buffer.
c:\SacaraRun.exe -p test_decode_buffer.sac 0x42434041
Execute file: c:\test_decode_bufferdecode_password.sac
Code execution result: 1111703617


RC4 decrypt the resource content

Our second and most complex step is the decryption of the buffer. In my previous post I used a simple XOR algorithm, for this post I decided to implement the RC4 cryptographic algorithm. If you are used to reverse malware you have probably encountered the usage of RC4 to encrypt configuration or code.

As in my previous script I'll use a lot of comments to make the code easy to understand (I also avoided some trivial optimization to avoid over complication). Since this code is quite long you can find the source code of this step in a test script, in this post I'll only show the KSA and PRGA phases.
proc ksa(password, password_length)
 .mov i, 0
 .mov j, 0

ksa_loop:
 // read the i-th byte from S array
 .read.b (S + i)
 pop S_i
 
 // read the i-th byte from password
 .read.b (password + (i % password_length))
 pop pwd_i
 
 // compute loop expression
 .mov j, ((j + S_i + pwd_i) % 256) 
 .swap(i, j)
 
 // check if I have to iterate
 .inc i 
 .cmp i, 256
 push ksa_loop
 jumpifl 
 ret
endp

proc prga(buffer, buffer_length)
 .mov i, 0
 .mov j, 0
 .mov n, 0
 
prga_loop:
 // update index i
 .mov i, ((i + 1) % 256) 
 
 // update index j
 .read.b (S + i) 
 pop S_i
 .mov j, ((j + S_i) % 256)
 
 // swap
 .swap(i, j)
 
 // read indexes
 .read.b (S + i) 
 pop S_i
 
 .read.b (S + j) 
 pop S_j
 
 // compute random
 .read.b (S + ((S_i + S_j) % 256))
 pop rnd
 
 // read n-th buffer value
 .read.b (buffer + n)
 pop buffer_n 
 
 // XOR with buffer and write back the result 
 .xor buffer_n, rnd
 pop encrypted_char
 .write.b (buffer + n), encrypted_char 

 // check if I have to iterate
 .inc n
 .cmp n, buffer_length
 push prga_loop
 jumpifl
 
 ret
endp

The assembling step is the same as before.

Run the decrypted resource

At this step we have the code in a decrypted form, we just have to run it. This can be done by the infrastructure code (the C code) or from Sacara. Since Sacara allows to invoke native code via the ncall instruction we will use this approach.

Build the code

Now we have all the pieces for our packer, once compiled the infrastructure code we can add the needed resources, that are:

  • DATA: contains the buffer that will be decrypted and executed.
  • SECRET: is the RC4 password that will beu sed to decrypt the buffer. It is XOR encoded with the 0x41 value.
  • SACARA: the Sacara code that will decrypt the buffer and execute it.


To add the resources you can use a simple utility that I wrote or any other PE explorer utility. Finally, the full source code of the Sacara packer can be found here.

Evaluation

As said my test will run a Cobalt Strike Payload encrypted with RC4 and the password: sacara_packer_password. I uploaded the file to VT and waited for the analysis, the result can be found here. We went from 52/70 to 24/69.

Not bad but to be honest I was expecting a better result. If you run the code in a real environment you will notice that the decryption of the code is quite CPU intensive and needs several seconds before to be invoked, this means that the identification of malicious content via emulation is improbable due to performance reason.

I decided to do a second test and upload the same program but without resources, this will make the program 100% safe. The result was very interesting, 12/69 AV flagged my sample as malicious. This means that they flagged the Sacara code as malicious and not real payload.

Conclusion and Future Work

This new version of Sacara improved the language in order to easier the development of a script. The next step is to provide an easy access to the Windows API in order to create more meaningful programs. Also, I want to improve the VM code in order to make it more resistant to reverse engineering.

Finally, I want to stress out that a single test case is not a valid reason to decide if an AV is good or not, so please take my result as a first step in the complex process of AV evaluation.