Ở bài trước, mình đã viết về điều tra memory shell bằng cách dump memory, upload một file jsp lên website và dựa vào các phương thức để lấy các thông tin liên quan đến Listener, Filter, Servlet, hay cấu hình logging. Tuy nhiên, phương pháp trên chỉ hiệu quả đối với servlet và cũng còn nhiều hạn chế như là việc cấu hình logging cần phải restart dịch vụ.
Và trong lúc trình bày về bài trước, một người anh xã hội đã hỏi mình: “Có cách nào hook thẳng vào memory không em?”
Bắt đầu từ phiên bản JDK 1.5, java đã thêm vào tính năng Instrumentation (Java Agent API) và JVMTI (JVM Tool Interface). Hai tính năng này cho phép chỉnh sửa bytecode của classfile trước khi nó được load, và cũng cho phép liệt kê toàn bộ class được load trong memory. Ở jdk1.5 thì việc chỉnh sửa và liệt kê cần phải diễn ra trước khi chạy chương trình phần nào đó không thực tế.
Rồi như sinh ra để dành cho việc tấn công và phỏng thủ. Từ phiên bản jdk1.6, Java Agent có thể can thiệp trực tiếp vào các tiến trình java đang chạy.(Tính năng ra mắt ở 1.5 là Premain và ở 1.6 là Agentmain ).
Nó là một con dao hai lưỡi khi mà vừa có thể tận dụng để tấn công khi chỉnh sửa, thêm vào class độc hại và cũng vừa có thể dùng để phòng thủ khi hoàn toàn có thể liệt kê các class trong bộ nhớ, dễ dàng kiểm tra nội dung của class mà không cần can thiệp vào chương trình đang chạy hay phải restart lại tiến trình,..
Đầu tiên cùng mình đi tìm hiểu về Premain vì nó là tiền đề cho Agentmain.
Premain
Ở Premain, class premain sẽ được load trước khi hàm main được load. Và để cấu hình cho việc này chúng ta cần phải sửa file MANIFEST.MF như sau:
TIếp theo viết một class Premain:
Class này sẽ được load và chạy trước khi hàm main được chạy:
Nó cung cấp cho chúng ta interface là instrumentation. Với rất nhiều phương thức hữu ích như sau:
Để thay đổi bytecode của class Main trước khi nó được load, ở premain mình sẽ thêm một Transformer.
Và một class để handler bytecode.
Sau đó build project với maven. pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<archive>
<manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Hàm main:
package org.example;
public class Main {
public static void main(String[] args) {
System.out.println(“Attacker!”);
}
}
Khi chạy hàm main cũng cần phải thêm tham số:
-javaagent:PathToAgentFile/JavaAgent-1.0-SNAPSHOT.jar
org.example.Main
Kết quả thu được:
Có thể thấy Premain được load trước khi hàm Main được load. Và dễ dàng có thể chỉnh sửa bytecode của hàm main:
Agentmain
Từ jdk 1.6 trở đi , agentmain ra đời và cho phép chúng ta tương tác trực tiếp với một tiến trình java đang chạy.
Thay đổi class main thành như sau:
Build lại agent bằng cách thay tên đổi premain thành agentmain:
Sau đó sửa lại file Manifest như sau:
Build lại agent, và tiếp theo chúng ta sẽ viết hook. Khởi tạo một project mới. Thường thì lib attach tool đã có sẵn trong jdk1.6 trở đi, nhưng nếu tạo project với intelij mà chưa nhận lib thì add ở JAVA_HOME/libs/tools.jar nhé.
Chạy main và chạy hook:
Dễ dàng liệt kê các class được load trong memory và chỉnh sửa bytecode của class.
Vậy áp dụng điều này vào việc phòng thủ như thế nào?
Cảnh sát JVM với khẩu súng mang tên Agent
Dưới góc độ phòng thủ hoàn toàn có thể sử dụng cơ chế này để attach vào tiến trình java đang chạy dịch vụ web kiểm tra xem các class nào đang được load. Ngoài ra còn có thể dump class, kiểm tra content của file được dump.
Ý tưởng về việc detect như sau:
- Liệt kê tất cả class đang được load.
- Kiểm tra từng class xem có import những lib, có các superclass, annotations, interfaces có thể dẫn đến RCE không? Đánh dấu các class có nguy cơ
- Lặp qua từng class được đánh dấu để xác định chắc chắn nó là webshell
Liệt kê toàn bộ class trong JVM
Ở đây mình thử nghiệm với Tomcat. Minh sử dụng VirtualMachine.list() có thấy tiến trình tomcat sẽ chứa catalina
Có thể chỉ định chỉnh xác package có chứa Catalina hoặc khi chạy công cụ điền vào pid của tiến trình tomcat.
Khi đã attach được tiến trình chạy tomcat, Chúng ta sẽ liệt kê tất cả class được load trong tiến trình đó.
Duyệt từng class trong danh sách class được load sau đó lần lượt kiểm tra các yếu tố sau:
- Các import package nguy hiểm
- Kiểm tra Super class
- Kiểm tra các Annotations
- Kiểm tra tên class
- Kiểm tra các interface
Nếu có bất kì nguy cơ nào từ một trong các yếu tố trên thì sẽ tiến hành dump class
Kiểm tra các package được import
Kiểm tra các Annotations
Kiểm tra các SuperClass
Kiểm tra các Interface
Kiểm tra class name
Từ danh sách những class nghi ngờ ở trên chúng ta sẽ đi kiểm tra chi tiết hơn.
Kiểm tra xem Class đó có tồn tại file .class không?
Với các class của hệ thống sẽ có file .class, còn đối với memshell thì attacker sẽ hạn chế nhất có thể việc tạo ra file, nên việc khi một class được load vào hệ thống nhưng lại không có file .class cũng là một dấu hiệu để nghi ngờ.
Kiểm tra nội dung của các file class
Từ những hình thức kiểm tra trên phần nào giúp chúng ta tìm được memoryshell trong hệ thống một cách dễ hơn. Nó cũng giúp có thể tùy chỉnh những dấu hiệu để nhận diện memoryshell, dễ dàng cập nhật những chain mới. Và cũng đã khắc phục được những điểm yếu của công cụ của phần trước khi nó có thể nhận diện được cả tấn công với Spring, Agent attack và các hình thức inject class khác. Tuy nhiên, vẫn sẽ luôn luôn là cuộc đua giữa blueteam và redteam khi mà sẽ luôn có cách để obfuscate để tránh những kiểm tra ở trên, đòi hỏi người làm phòng thủ cần phải liên tục update “blacklist”.
Cảm ơn mọi người đã đọc phần này, hẹn gặp mọi người ở phần sau với những kỹ thuật khác và điều tra memoryshell trên .NET
Nguồn tham khảo:
- GitHub – LandGrey/copagent: java memory web shell extracting tool
- 查杀Java web filter型内存马 | 回忆飘如雪
- Tomcat 源代码调试笔记 – 看不见的 Shell
- 【原创】利用“进程注入”实现无文件不死webshell – rebeyond – 博客园
- LandGrey’s Blog
- Verification