+4

Phân tích CVE-2021-30128 Apache OFBIZ

Tản mạn

Bài này mình sẽ phân tích CVE mới nhất của apache OFIBZ là CVE-2021-30128. Vì theo như một người anh đã nhắc nhở.

Ban đầu sau bài viết lần trước về CVE-2021-26295 thì mình định phân tích tiếp về CVE-2021-29200. Nhưng CVE-2021-29200 sử dụng một cách bypass để vẫn có thể sử dụng RMI để RCE. Tuy nhiên thì như mình thấy các chain như JRMPClient trong các poc được public trên mạng thì còn phụ thuộc vào phiên bản java nữa mới có thể thành công. Vì theo bài viết https://tradahacking.vn/rmi-study-note-and-some-study-case-72bfd47275d9

Chain này đã bị filter. Nên mình bỏ ngỏ con CVE-2021-29200 kia để bắt đầu với CVE-2021-30128. Ok. Bắt đầu thôi.

Dựng môi trường debug

Trong bài phân tích mình sử dụng jdk1.8.0_281, apache ofbiz 17.12.06 và intelij để debug. Về apache ofbiz 17.12.06 các bạn có thể tải tại đây: https://github.com/apache/ofbiz-framework/releases/tag/release17.12.06.

Về phần build và debug. Mình có làm hơi khác so với bài trước một chút. Đó là mình cho chạy server và debug trực tiếp trên intelij để tiết kiệm thời gian. Đầu tiên các bạn load cả project của ofbiz 17.12.06 vào intelij. Intelij sẽ tự động phát hiện gradle project và load hết tất cả các dependencies cho chúng ta. Sau hi load xong chúng ta làm như sau để build server.

Sau khi build thành công chúng ta làm tiếp như sau để debug.

sau đó

Tiếp theo các bạn run lên thì được như sau:

Các bạn nhớ bấm Attach debugger thì chương trình mới chạy tiếp được nhé.

Khi hiển thị như thế này là chúng ta đã set up thành công

Phân tích CVE-2021-30128

Xác định chain to trigger deserialization + Xác định endpoint để tạo request

Mục này mình đã phân tích rất kĩ ở bài viết trước đây. Bạn nào chưa đọc thì có thể đọc lại tại đây: https://viblo.asia/p/phan-tich-cve-2021-26295-apache-ofbiz-RQqKLGMO57z. Về cơ bản thì CVE-2021-30128 này là một phương pháp để có thể bypass blacklist mà vẫn thỏa mãn whitelist của ofbiz từ đó dẫn tới RCE. Phương pháp này rất hay và đây cũng là lần đầu mình gặp nó.

Xây dựng payload

Để ý thì trong đống lib của project có sử dụng commons-beanutils version 1.9.3. Thỏa mãn để chúng ta sử dụng gadget chain CommonsBeanutils1. Tuy nhiên ofbiz chỉ cho phép các lớp thỏa mãn trong whitelist được đi qua. Nên payload của chúng ta sử dụng gadget chain CommonsBeanutils1 có vẻ không sử dụng được vì payload sử dụng các lớp như: org.apache.commons.collections.comparators.ComparableComparator, com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl, org.apache.commons.beanutils.BeanComparator không thỏa mãn whitelist.

Xem xét kĩ hơn method resolveClass trong lớp SafeObjectInputStream

lớp này có nhiệm vụ là xử lý các classname. Dễ dàng nhận thấy flow của method này, đầu tiên sẽ kiểm tra classname có thỏa mãn whitelist và không nằm trong blacklist hay không. Sau đó sẽ nhảy vào ObjectType.loadClass để xử lý tiếp

Lớp này đã thực hiện rất nhiều hành động tùy chỉnh cho mô tả lớp. Như trong đoạn code được đánh dấu ở trên thì classname sẽ là chuỗi bắt đầu từ đầu cho đến trước kí tự <. Kết hợp với whitelist trước đó thì nếu chúng ta serialize payload và thực hiện sửa đổi một chút về classname của chúng ta sao cho nó có dạng

org.apache.commons.beanutils.BeanComparator<xxxx

thì sau khi được xử lý qua loadClass sẽ trả về cho chúng ta class

org.apache.commons.beanutils.BeanComparator.

Để ý trong whitelist có java..* tức là chúng ta chỉ cần xây dựng payload với mô tả lớp có dạng

org.apache.commons.beanutils.BeanComparator<java.xxx

là sẽ thỏa mãn whitelist và sau khi xử lý qua loadClass sẽ trả về lớp org.apache.commons.beanutils.BeanComparator như chúng ta mong muốn.

Tất nhiên thì trong core của jdk cũng có những hạn chế về việc tên lớp trong chuỗi byte stream sau khi được deserialization có giống với tên lớp thực sự hay không.

classNamesEqual chịu trách nhiệm so sánh tên lớp trong chuỗi byte stream sau khi được deserialization và tên lớp trong library của project. Đi sâu hơn vào method này

Ở đây thì chỉ có phần cuối cùng sau dấu . của tên lớp và mô tả lớp được so sánh với nhau. Kết hợp tất cả các điều kiện trên thì ta có thể xây dựng mô tả lớp trong dữ liệu serialized như sau để vượt qua tất cả các hạn chế như mình đã trình bày.

org.apache.commons.beanutils.BeanComparator<java.BeanComparator

Poc

Ở đây mình sử dụng gadget chain CommonsBeanutils1 được sinh ra từ ysoserial như sau

java -jar ysoserial-master-d367e379d9-1.jar CommonsBeanutils1 "calc.exe" > payload.bin

Sau đó mình dùng SerializationDumper-v1.13.jar để dump các giá trị của file payload.bin kia ra cho dễ đọc. Về tool này các bạn có thể thêm thông tin tại đây. https://github.com/NickstaDB/SerializationDumper

java -jar SerializationDumper-v1.13.jar -r payload.bin > dump.txt

Tiếp theo để xây dựng payload các bạn sửa đổi mô tả lớp classname như chúng ta đang trình bày ở trên. Lưu ý là chúng ta phải sửa đổi cả về Length và Value nhé. Và nhớ là sửa giá trị hex đằng sau nhé.

Về công việc thủ công có thể gây sai sót nên mình dùng poc của ông này https://github.com/LioTree/CVE-2021-30128-EXP

demo xíu cho mọi người xem

Tham khảo

https://mp.weixin.qq.com/s/Dr-jwiRr4NByjErjiX_e1w https://github.com/LioTree/CVE-2021-30128-EXP

Cảm ơn

Cảm ơn anh @minhtuan.nguy đã giúp đỡ em trong quá trình xây dựng môi trường và phân tích lại con cve này.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí