# Microsoft Exchange servers backdoored with OwlProxy

## Introduction

In early 2022, Telsy observed malicious cyber activity in the Microsoft Exchange infrastructure. The analysis conducted revealed the presence of a malicious DLL, called “fuscom.dll“, and its persistence by creating the Windows service named “FastUserSwitchingCompatibility“. The service invokes and runs the 'fuscom' DLL each time the operating system is rebooted. The malicious DLL creates a WEB proxy channel through which commands can be executed locally on servers with output returned as a WEB response.

It is assumed, based on the creation date of the DLL on the filesystem, that the implant took place immediately after the start of last year’s massive wave of ProxyLogon attacks.

The analysis revealed that the malicious DLL, 'fuscom.dll', is attributable to the 'OwlProxy' malware family and is primarily used by Chinese threat actors.

OwlProxy is attributed by the security firm 'CyCraft' to the threat actor 'Chimera', and at the same time has a code overlap with the malware called 'Gelsevirine' attributed by the security firm 'Eset' to the Chinese group 'Gelsemium'. Furthermore, the specific OwlProxy variant of the samples we recovered was documented as part of the report called 'The SessionManager IIS backdoor' on the 'Securelist' blog published by 'Kaspersky'.

It is important to emphasise that the use of the same malware by two threat actors, albeit of the same nationality, could even indicate widespread code sharing on forums or code-sharing platforms.

Based on the evidence found and the ability to maintain long-term persistent access within the compromised infrastructure, it is possible to say that this activity was certainly conducted by a criminal group with sophisticated cyber capabilities.

## Analysis

Owlproxy is one of the primary malware with backdoor functionality to tunnel in and out of the network used from some threat actor to bridge the internet and intranet. This backdoor functionality enables threat actors to launch any commands directly into the target system.

The variant of the recovered OwlProxy sample is called 'fuscom.dll'. Its persistence is guaranteed by the creation of an autostart configured Windows service.

'fuscom.dll' is an HTTP requests handler, i.e. it is loaded in the IIS process and installs, in the ServiceMain() routine, some HTTP handler on specific URLs.

On each specific GET or POST request on the URL on which the handler has been installed a specific action is then executed.

The DLL uses the following API to install the handler and receive the requests:

• HttpReceiveHttpRequest() – read the queue of input http requests on the registered URL

Usually, this APIs are used to install stealthy webshell that uses the IIS's functionalities without for example writing any php or aspx page and giving anyway access to the system.

Basically, on the service main it registers a 'service control handler'.  Then starts two different threads:

1. Thread that registers the URLs
2. Thread that implements a busy loop

Most of the http request and response transmits data encrypted using a xor-based custom algorithm.

The malware installs three different requests handler:

1. Command execution handler – used to execute commands via cmd.exe on the system
2. Proxy installer – used to instantiate the 3rd request handler on a new URL
3. Proxy – used to proxying data using the infected system reaching not exposed hosts.

The requests could be GET or POST. The attacker parameters, usually follows the format: Base64(enc(wide(attacker input)))

The http response to every kind of command/URL and whatever return code the command has returns status code 200 and response code OK.

### Encryption/Decryption Algorithm

The encryption algorithm is xor-based, but there is no hardcoded key. Basically, a fixed hardcoded string, i.e., Unicode '20170502160306', is used to generate a lookup table that is used to find the key according to the value and position of every byte of the encrypted string.

The first step is generating two tables:

256 bytes table from the string generator:

for i=0 to 256

table_from_generator[i] = generator_string[i%len(generator_string)] # generator string 20170502160306

An index table is generated too

for i=0 to 256

generated_table[i] = i

Both the tables are used to generate a final table that then is used for the decryption.

processed_i = 0

for i=0 to 256

processed_i =  processed_i + table_from_generator[i] + generated_table[i]

swap(generated_table[i], generated_table[processed_i % 256 ])

processed_i++

Finally, the decryption is achieved doing xor between the i-th byte of the encrypted string and the xor key that comes from the generated table.

Decripted_string[i] = encrypted_string[i] ^ byte_key

The i-th key's byte depends on the index of the byte to decrypt and the related value in the generated table.

J=0, val=0

for i to encrypted_string_len

j++

val = val + generated_table[j]

tmp = generated_table[j]

swap(generated_table[j], generated_table[val])

decrypted_string[i] = encrypted_string[i] ^ generated_table[generated_table[j] + tmp]

### HTTP Handler 1 – Command Execution

The first registered URL is: hxxps://+:443/HelpTheme

Once, the url is registered the handler waits for new incoming HTTP requests on the registered url. The requests are managed synchronously, i.e., when a new request arrive it is managed before that another one could be received. Indeed, this URL provide to the attacker an interactive command execution functionality.

The handler only considers requests that satisfy the following constraints:

• The URL, of course, must match the registered one, i.e., hxxps://+:443/HelpTheme
• The method used must be GET
• The URL should contain a page and a parameter after the fixed registered URL moreover it must have the page 's?pa='

Finally, the complete URL that is processed is:

hxxps://+:443/HelpTheme/s?pa=<attacker input>

If the previous constraints are satisfied, then the request is processed.

The GET parameter 'pa' contains the command to execute on the system, it is sent encoded and encrypted. The encoding is in Base64, the encryption algorithm is a custom xor-based one.

The format of the parameter could be resumed in this way: 's?pa=<base64(encrypted(wide(command)))>'.

After that the command has been decrypted it is parsed, since many kinds of commands could be sent by the attacker.

The response to a request for the command execution handler, contains the command output encrypted with the same algorithm previously seen.

### Command execution modes

The received command is interpreted via common string operation, in particular the following strings are searched for:

• 'w;'
• 'rk;'
• 'wf;'

These strings are needed to specify the mode of execution of the command, below is the detail of each of them:

• If the command starts with the string 'w;' then the string that follows the prefix is executed on the system and the output is send back to the attacker in the response body
• If the command starts with the string 'rk;' then the string that follows the prefix is executed as command without sending back the command's output.
• If the command starts with the string 'wf;' then the string that follows the prefix is used to time-stomping a file. This mode, anyway, seems to be not fully implemented.

If the command received has no prefix, then the default behavior follows the same of 'w;'.

The routine that manages the prefixes 'w;' and 'rk;' requires three parameters:

• Buffer to store the command output
• Buffer with the command to execute, i.e., the string following the prefix
• Mode, i.e., the execution mode 'w' or 'rk'

The string following the prefix 'w' and 'rk' is executed via 'cmd.exe'. If the prefix is 'w;' then the process will read the command's output from the pipe.

### Self-uninstall

If the received command is 'self-uninstall'' then the DLL starts to remove itself from the system. The code flow branches according to the web server on which the DLL is executed, i.e., Apache or IIS.

If the malicious DLL is running into Apache than it will remove itself and reset the configuration files. Indeed, it opens for the file 'C:\Windows\conf\httpd.conf' and remove the string:

On the other hand, if the DLL is executed on an IIS webserver, the service aimed at persistence will be deleted in addition to the deletion of the DLL. The deletion function in the IIS context has a hard-coded service name called 'wmipd'. In the forensic analysis conducted by Telsy, the service identified for persistence had another name, so using the 'self-uninstall' command will not perform a complete deletion but only the removal of the malicious DLL.

If the remotion fails via 'DeleteFileW()', then the library in both execution contexts it rename itself in 'original_file_name + _' then force a delete on reboot.

### Time-stomping

The command if prefixed with 'wf;' lead to execute the time-stomping of a file. The routine that executes the time-stomping starts getting timestamps from 'calc.exe'.

The purpose is to place the same timestamp of 'calc.exe' to two hardcoded filenames.

The routine seems incomplete, since the hardcoded filenames to time-stomp are missing in the sample. This, most likely, is a mistake of the programmer.

For this motivation the command 'wf;' is not usable.

### HTTP Handler 2 – Proxy Installation

The second registered URL is 'hxxps://+:443/HelpTheme/pp/'.

When a GET request for the registered url arrives the handler search for the 's?pp=' page parameter, so the complete second URL would be:

'hxxps://+:443/HelpTheme/pp/s?pp=<base64(encrypted(wide(value)))>'.

The parameter value is base64 decoded and decrypted with the same algorithm previously shown.

In the parse_and_decrypt_url() routine, the new URL to handler is created.

Basically, the second handler is required to install a 3rd handler that is then used in the proxy functionalities. The only parameter that leads to create the 3rd handler is 'proxy'.

So, the only useful call to this handler is:

'hxxps://+:443/HelpTheme/pp/s?pp=<base64(encrypted(wide(proxy)))>'.

Since, for every request to the URL installed and managed by the second handler corresponds a new registered URL and a new handler then every URL here generated should be different.

Indeed, the URL created will have the following format: 'hxxps://+:443/s/HelpTheme/pp/<ID>/'.

ID in this case is a global variable that is incremented on every new URL generated, its initial value is: 554780024.

The third URL registered provides the proxy functionality to the attacker, basically the attacker could use this new URL to reach internal host that are not directly exposed.

### HTTP Handler 3 – Proxy Commands

The third URL registered is: 'hxxps://+:443/s/HelpTheme/pp/ID/' where the ID is equal to '554780024 + i' provides the proxy feature to the attacker. In the main routine that handle request to the new registered URL initially the winsocket library is initialized via WSAStartup().

Moreover, it creates an event using CreateEvent() and waits for incoming requests at most for 20 seconds.

The timeout is needed to avoid memory wasting and possible flooding. So, if the event expires or the received request is wrong then the URL will be removed and the thread that handles the requests quits.

The received requests could have the following commands:

• Connect (method GET) – connection to the specified IP
• Disconnect (method GET) – close the connection
• Receive (method GET) – receives, in the response body, data from the IP
• Send (method POST) – send data to the IP

### Proxy Command (GET) – Connect, Disconnect, Receive

When the request is a GET request then it search for the page and parameter 's?pp='.

So, the GET requests have the following format: 'hxxps://+:443/s/HelpTheme/pp/ID/s?pp=<base64(enc(wide(command)))>'

So, the routine extracts the parameter decode from Base64 and decrypt it using the same algorithm previously described and execute the proper action. The output of the command is sent in the response.

The commands sent in the GET request could be:

• Connect – having the following format
• hxxps://+:443/s/HelpTheme/pp/ID/s?pp=<base64(enc(wide(connect;ip:port)))
• Disconnect – having the following format
• hxxps://+:443/s/HelpTheme/pp/ID/s?pp=<base64(enc(wide(disconnect)))
• Recv – having the following format
• hxxps://+:443/s/HelpTheme/pp/ID/s?pp=<base64(enc(wide(recv)))

The decrypted command is traversed to split strings according to ';' character.

### Comando Connect

The 'connect' command with the following format 'base64(enc(connect;ip:port))' is used to create a socket and connect to the specified IP:PORT.

The command's output sent back in the response body could be 'true' o 'false', encrypted with the same algorithm, according to the return code of the connection.

The socket and the connection happen if and only if there is not another established connection.

In this way it's avoided that on the same URL could be opened multiple sockets that are then instantiated and never used since the mapping between URL-socket is 1:1.

### Comando Disconnect

The 'disconnect' command with the following format 'base64(enc(wide(disconnect)))' receive the command’s output 'true' encrypted with the same algorithm.

### Comando Recv

The receive command having the format 'base64(enc(wide(recv)))' and returns the received data encrypted or the output ''false' encrypted.

### Proxy Command (POST) – Send Data

The POST request contains in the body the data to send to the IP:PORT connected with a previous request. The body request is only base64 encoded, so the data are decoded and sent to the socket. The response body will contain the encrypted string 'true' or 'false'.

## Detection

Below our rule to detect statically or in memory the OwlProxy sample:

rule fuscom_OwlProxy_detection {

meta:
date = “06.04.2022”
author = “Telsy-CTI”
hash1 = “3a161dd76f9bc9c34ce0e9ce23011a96c415a79806413bf3fdc881cb6dd0a42c”
hash2 = “C9B8CFACC859E494984AE9DDF864314AF651FD32E60F33C7F42BFC640620E8CA”

strings:
$code_enc_1 = { 41 8b c0 41 ff c0 4d 8d 52 01 99 f7 ff 48 63 c2 0f b6 0c 30 41 88 4a ff 45 3b c3 }$code_enc_2 = { 41 0f b6 04 12 44 0f b6 02 44 03 c8 45 03 c8 41 81 e1 ff 00 00 80 7d ?? 41 ff c9 41 81 c9
00 ff ff ff 41 ff c1 49 63 c9 48 ff c2 41 ff c1 0f b6 04 19 88 42 ff 44 88 04 19 49 ff cb }
$code_enc_3 = { 49 63 d2 42 0f b6 0c 1a 43 88 0c 18 42 88 1c 1a 43 0f b6 14 18 03 d3 81 e2 ff 00 00 80 7d ?? ff ca 81 ca 00 ff ff ff ff c2 48 63 ca 49 ff c1 ff c0 42 0f b6 14 19 41 ff c2 42 32 54 0e ff 41 88 51 ff 48 ff cf }$code_hdl = { 0f 28 02 0f 29 01 0f 28 4a 10 0f 29 49 10 0f 28 42 20 0f 29 41 20 0f 28 4a 30 0f 29 49 30 0f
28 42 40 0f 29 41 40 0f 28 4a 50 0f 29 49 50 0f 28 42 60 0f 29 41 60 48 8d 89 80 00 00 00 0f
28 4a 70 0f 29 49 f0 48 8d 92 80 00 00 00 }
$code_hdl2 = { 48 c7 84 24 88 00 00 00 0f 00 00 00 48 89 9c 24 80 00 00 00 c6 44 24 70 00 48 89 5c 24 20 48 89 5c 24 28 48 89 5c 24 30 48 8d 94 24 c8 00 00 00 48 8d 4c 24 20 }$code_hdl5 = {81 fb e5 03 00 00 0f 85 ?? ?? 00 00 85 c0 0f 85 ?? ?? 00 00 41 8b 47 24 83 f8 06}
$s1 = “wf;” fullword ascii$s2 = “rk;” fullword ascii
$s3 = “w;” fullword ascii$s7 = “proxy” fullword ascii
$s8 = “connect” fullword ascii$s9 = “disconnect” fullword ascii
$s10 = “proxy” fullword ascii$s4 = “%s://+:%d%spp/” fullword wide
$s5 = “%s://+:%d%s” fullword wide$s6 = “s?pp=” fullword ascii
$s11 = “System/calc.exe” wide fullword$s12 = “HttpSendHttpResponse” ascii fullword
$s13 = “HttpSendResponseEntityBody” ascii fullword$s14 = “HttpReceiveHttpRequest” ascii fullword
$s15 = “HttpAddUrl” ascii fullword$s16 = “\\cmd.exe /c ” ascii fullword
condition:
//all of them
2 of ($code_enc*) or 3 of ($code_h*) or 8 of (\$s*)
}