Phân tích CVE-2021-30128 Apache OFBIZ
Bài đăng này đã không được cập nhật trong 3 năm
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