Chắc
hẳn nhiều người đã biết đến lỗ hổng CVE-2020-5902, mới được công bố tồn tại
trên hệ thống BIG-IP của F5 được cộng đồng bảo mật quan tâm gần đây. Sau thời gian tìm hiểu, tôi
cũng tò mò về root cause của nó và càng thấy thú vị hơn về mitigation
bypass được công bố vài ngày sau đó. Do đó, tôi quyết định tìm hiểu sâu hơn về lỗ
hổng này. Bài viết chia sẻ lại một số câu hỏi mà tôi đã đặt ra và trả lời trong
quá trình hiểu về lỗ hổng.
Bài
viết này được viết sau khi tìm hiểu, trao đổi và hướng dẫn bởi anh QuangNX (@sovietw0rm) và
QuangBX (@buxu). Xin chân thành cảm ơn các chuyên gia!
Câu chuyện
Mã khai thác được lan truyền nhiều trên mạng xã
hội chỉ vài ngày ngắn ngủi sau khi hãng F5 ra KB về lỗ hổng này. Tôi bắt đầu với một số câu hỏi trong đầu:
- Tại sao lỗ hổng lại tồn tại?
- Tại sao với chuỗi ..; lại
có thể bypass auth, hơn nữa lại chỉ có thể áp dụng với
/tmui/login.jsp
mà không phải là các endpoint khác.
Theo mô tả của KB, tôi tải xuống phiên bản BigIP Virtual Edition 15.1.0.0 là một phiên bản có
lỗi. Chúng ta có đoạn cấu hình httpd như sau:
// httpd.conf <Location /tmui> …. <RequireAll> AuthType Basic AuthName “Restricted area” AuthPAM_Enabled on AuthPAM_ExpiredPasswordsSupport on AuthPam_ValidateIP On AuthPAM_IdleTimeout 1200 AuthPAM_DashboardTimeout Off require valid-user Require all granted </RequireAll>
</Location>
|
Cấu hình này bắt buộc các HTTP requests đến
/tmui/*
phải
xác thực thông qua PAM. Nếu thỏa mãn (authenticated), request sẽ được gửi qua
cho ứng dụng servlet xử lý qua AJP. Ở đây tôi không có nhiều kiến thức về Tomcat nên chỉ có thể hiểu sơ qua vậy.// /config/httpd/conf.d/proxy_ajp.conf ProxyPassMatch ^/tmui/(.*\.jsp.*)$ ajp://localhost:8009/tmui/$1 retry=5
ProxyPassMatch ^/tmui/Control/(.*)$ ajp://localhost:8009/tmui/Control/$1 retry=5
ProxyPassMatch ^/tmui/deal/?(.*)$ ajp://localhost:8009/tmui/deal/$1 retry=5
ProxyPassMatch ^/tmui/graph/(.*)$ ajp://localhost:8009/tmui/graph/$1 retry=5
ProxyPassMatch ^/tmui/service/(.*)$ ajp://localhost:8009/tmui/service/$1 retry=5
ProxyPassMatch ^/hsqldb(.*)$ ajp://localhost:8009/tmui/hsqldb$1 retry=5
|
Các requests này sẽ được handle bởi các servlet
định nghĩa trong web.xml (
/usr/local/www/tmui/WEB-INF/web.xml
). Về cơ bản, các ứng dụng servlet của F5 sử dụng không hề có cơ chế xác
thực mà chỉ phụ thuộc vào PAM module với httpd (/config/httpd/modules/mod_auth_pam.so
).
Tôi
có nhớ lại bài nói của Orange
về việc xử lý path không đồng nhất giữa các thành phần trong server stack tại
BlackHat 2018: Breaking Parser Logic: Take your path normalization off and pop
0days out. Thời điểm đó, tôi cũng chỉ đọc cho biết vậy thôi nhưng chưa có ý
định xem cụ thể nó là gì. Tại đây, tác giả có trình bày một “lỗ hổng” tồn tại mặc
định khi sử dụng apache proxy và Tomcat khi normalize path. Nguyên nhân là do
Tomcat hỗ trợ một tính năng là path parameter. Đây không phải là lần đầu tiên mà vấn đề security liên quan đến
tính năng này được nhắc đến, tuy nhiên có lẽ Orange là người đầu tiên (công
bố) tận dụng để nâng cao mức độ nguy hiểm.
Về cơ bản, tính năng path parameter cho phép xác định
một số biến và giá trị thông qua URL, phân cách bởi các ký tự
;
và /
theo dạng: /path/name;param1=value1;param2=value2/. Tuy nhiên, do httpd không hỗ trợ tính năng này nên
nó sẽ xem ..;
hoặc ;
như một phần hợp lệ của request mà không “normalize”.
Đây chính là nguyên nhân dẫn đến lỗ hổng này.Ref: https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf |
Chúng ta sẽ tìm hiểu cách thức Tomcat parse các URI để rõ hơn vấn đề
này. Tìm kiếm từ khóa path parameter trên mã nguồn của Tomcat, tôi tìm thấy đoạn
code sau tại hàm postParseRequest() của lớp CoyoteAdapter:
Hàm parsePathParameters() parse các path parameter từ URL và loại bỏ các thành phần này ra khỏi URL, bao
gồm dấu
;
Hàm
normalize()
sẽ normalize các chuỗi trong path bao gồm \, //,
/./ và /../
Trong PoC video (debug Tomcat) dưới đây, có vẻ
như Eclipse cập nhật thông tin biến hơi chậm. Thực tế thì uri đã được
normalized sau khi thực thi hàm
normalize()
.
Về câu hỏi tại sao chỉ post đến
/tmui/login.jsp
, tôi tiến hành phân tích hàm register_hooks
của
module mod_f5_auth_cookie.so
. Để xác định hàm này, các bạn có thể tìm kiếm giá trị MODULE_MAGIC_COOKIE
trong cấu trúc STANDARD20_MODULE_STUFF
khi khai báo một module.
Hook handler sẽ so sánh uri của request với một
số chuỗi URL có sẵn, trong đó có
/tmui/login.jsp
và trả về mã OK. Phần này tôi có một số ngoại cảm về “how it work”, bao
gồm việc module này trả về OK thì module mod_auth_pam.so
có check nữa hay không, đành dành cho bạn nào muốn tìm hiểu sâu hơn về
viết module cho httpd.
Nhân tiện, anh QuangNX vừa gửi lại cho tôi một bài
viết của NCCGroup cũng phân tích về root
cause của lỗ hổng này
ngay trước thời điểm đăng bài viết, bạn đọc có thể tham khảo.
Khi câu chuyện đi xa hơn
Sau vài ngày (08/07/2020) thì một nhóm tên là
TeamAres cũng lấy số bằng việc công bố một blog bypass mitigation được F5 đề xuất trên KB. Nhóm này phát hiện có thể
(ab)use endpoint
/hsqldb
để có thể thực hiện RCE — thực thi các Java static method tuỳ ý (hỗ trợ
bởi hsqldb). Xin lưu ý rằng đây là bypass mitigation mà F5 đã đề xuất trên KB, không
phải là bypass bản cập nhật. Mặc dù bản cập nhật họ cũng có cấu hình httpd y hệt,
tuy nhiên endpoint /hsqldb
đã bị họ bỏ đi, không còn truy cập thông qua giao diện web được nữa.
(Tôi không rõ tại sao lại có endpoint này, có thể là cho quá trình phát triển/gỡ
lỗi).
Có 2 điều khiến cho tôi chú ý về write-up này:
- Endpoint
/hsqldb
có thể bypass authen với một ký tự;
, thay vì chuỗi..;
như với exploit ban đầu. - Tác giả nói rằng khi khai thác lỗ hổng này, họ kết
nối tới CSDL thông qua
/hsqldb;
thì bị lỗi, cho nên đã sử dụng exploit ban đầu và encode ký tự;
(0x3b) để bypass regex pattern của httpd, tức/tmui/login.jsp/..%3b/hsqldb/
Với
câu hỏi thứ nhất, tại sao bypass auth chỉ với ký tự
;
, chúng ta hãy xem cấu hình httpd:# HSQLDB
#
<Location /hsqldb>
<RequireAll>
AuthType Basic
AuthName “BIG\-IP”
AuthPAM_Enabled on
AuthPAM_IdleTimeout 1200
require valid-user
Require all granted
</RequireAll>
</Location>
|
Theo cấu hình này, truy cập
/hsqldb
sẽ
cần phải xác thực qua PAM. Tài liệu cấu hình httpd cho directive Location
có
ghi:
Nghĩa là, location này chỉ match chính xác với cụm
/hsqldb
hoặc
/hsqldb/*
. Như vậy, nếu sử dụng /hsqldb
;
thì request của chúng ta sẽ không bị hạn chế bởi directive này. Mặt khác, như
đã nói về phần đầu của bài viết về URL path parameter, Tomcat sẽ xem request
này giống như /hsqldb
(code parse path parameter sẽ loại bỏ ký tự ;
)
và servlet tương ứng sẽ được thực thi.
Với
câu hỏi thứ 2, ngay khi đọc đến đoạn này trong bài viết của TeamAres, tôi tự hỏi
là làm sao có thể bypass rule này của httpd chỉ bằng cách encode ký tự chấm phẩy
(
;
). Tôi thấy vô lý và cần kiểm chứng (vì bản thân
tôi tin rằng uri sẽ được decode trước khi matching). Sau khi cấu hình httpd như
trong khuyến nghị của F5, tôi thực hiện truy cập theo pattern kia (encoded url)
nhưng server đều trả về 404, nghĩa là không thể vượt qua được matching của
httpd. Có chăng kết nối được tạo ra bởi SQL client (mã khai thác) có gì đó vi
diệu!? Tôi chạy thử exploit nhưng không thành công, tôi cũng đã thử nhờ vài
người khác, độc lập chạy thử nhưng đều không được.
Đến
đây, tôi tạm chấp nhận rằng, có thể tác giả đúng với một cấu hình và ngữ cảnh
nào đó, hoặc do tác giả cố tình làm vậy vì một lý do nào đó. Tuy nhiên, rõ ràng
GET request đến
/hsqldb
;
đã vượt
được xác thực, tại sao tác giả lại “failed” khi sử dụng path này để exploit. Vậy
liệu có thể khai thác với /hsqldb
;
hay không? Tôi băn khoăn rằng nguyên nhân
failed ở đây là do phía server hay client, tức từ mã khai thác của chính tác giả.
Tôi
muốn xem các request thực sự được tạo bởi mã khai thác Java sẽ như thế nào, tuy
nhiên lại gặp khó trong vấn đề làm sao để MITM ứng dụng Java. Ở đây hoàn toàn
có thể đọc log phía server, nhưng tôi thấy quen với việc dùng Burp Suite để trực
quan và tiện hơn nên tôi sử dụng một cách khác, đó là viết một đoạn script để serve http và relay đến máy chủ BIG-IP thông qua
proxy Burp Suite, đóng vai trò giống như reverse + upstream proxy. Từ đây hoàn
toàn có thể xem được nội dung các request.
Tôi
bỏ cấu hình mitigation và thực hiện chạy mã khai thác với lần lượt hai path là
/tmui/login.jsp/..;/hsqldb/
và /hsqldb;
Quan
sát trên Burp Suite tôi thấy rằng ở lần chạy thứ hai với path là
/hsqldb;
,
request POST được thực hiện đến /
. Điều này khiến tôi tin rằng nguyên nhân tác giả không thể exploit đến
từ mã khai thác/thư viện phía client. Tôi sẽ không đi sâu tìm hiểu chính xác
nguyên nhân ở đây mà tập trung vào xác nhận phỏng đoán rằng lỗ hổng này có thể
khai thác thành công nếu gửi crafted payload đến /hsqldb;
.
Thực tế, ký tự
;
đã break connection string khi code parse chuỗi này, có thể là
convention, cho nên trong exploit (bằng mã Java dưới đây) thực sự vẫn phải dùng
%3b
thay cho ký tự ;
.
Mỗi
lần chạy exploit thành công, có hai HTTP requests được gửi đến web server,
trong đó request đầu tiên như là việc khởi tạo và request sau tương ứng với
execute hàm
call()
. Xin nói thêm rằng, ở đây tôi đã gộp hai lệnh
call trong PoC vào làm một để có thể giảm số lượng request cần thiết. Bốn bytes
đầu post data là độ dài của dữ liệu (tương tự Content-Length). Request khởi tạo
trả về chứa 4 bytes có thể xem như là “session id”, các bytes này sẽ được sử dụng
sau đó để authenticate. Ngoài ra còn một số bytes khác nhưng vì tôi thấy không
thay đổi nên tôi tạm thời bỏ qua.
Tôi
replay các requests khai thác tới
/hsqldb;
với post data được chỉnh sửa tương ứng và đã có
thể khai thác thành công. Kết quả tôi thống kê ngày 09/07/2020 với mã khai thác
này thì vẫn còn rất nhiều máy chủ vẫn có thể bị khai thác với danh sách tìm kiếm
từ Shodan, tiếc là không thể so sánh với exploit “gốc” được do có việc bận mấy
ngày tiếp đó.
Cập
nhật thời điểm ngày 18/07/2020, tôi có thực hiện một thống kê nhanh trên Shodan, kết quả cho thấy vẫn còn khoảng 1.380 máy
chủ public Internet có thể bị khai thác bởi lỗ hổng này.
Hiện
tại, F5 đã cập nhật lại mitigation trên K52145254.
Riêng về phần cấu hình httpd cho TMUI, họ đã thay đổi pattern để chặn khai thác
với các uri chứa bất kỳ chuỗi
;
hoặc hsqldb
.
Ngoài ra, họ cũng phát hiện thêm rằng sử dụng chuỗi hsqldb%0a
cũng có thể bypass auth thành công. Tình cờ là
thời điểm tôi viết bài này, ngay trước khi đọc lại thông tin trên F5 thì tôi
cũng đã phát hiện ra điểm này. Tuy nhiên thì tôi thấy đã dành đủ thời gian cho
lỗ hổng này, phần còn lại dành cho bạn đọc nào có hứng thú.Tài liệu tham khảo
Researcher:
Tạ Quang Khánh
VinCSS
(a member of Vingroup)
No comments:
Post a Comment