[EX005] Hành trình khai thác lỗ hổng phần mềm CVE-2020–5902


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ự ; / 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.

Refhttps://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 \, //, /.//../



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//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