Với một vài dòng code đơn giản, tôi đã tìm CVE của Joomla như thế nào?

Với một vài dòng code đơn giản, tôi đã tìm CVE của Joomla như thế nào?

Sau một thời gian nghiên cứu và tìm lỗ hổng về framework Joomla và tìm được vài CVEs. Sau đây mình sẽ write up về 3 lỗ hổng và phương pháp tìm.

  1. CVE-2019-19845
    Lỗi được phát hiện trong quá trình tìm chain dựa vào CVE-2019-18650 để tìm WEBROOT, tuy nhiên lại chỉ khai thác được nếu máy chủ chưa tắt thông báo lỗi.
    Như vậy, ý tưởng để tìm lỗi này sẽ như sau:
  • Bật chế độ thông báo lỗi của máy chủ.
  • Tìm toàn bộ các file thỏa điều kiện sau:
    - Sử dụng các hàm gọi thư viện như use, include, include_once, require, require_once
    - Không sử dụng _JEXEC , là cơ chế ngăn chặn truy cập trực tiếp vào file của Joomla.
Hình 1 Ví dụ cách sử dụng JEXEC chặn truy cập trực tiếp vào file ở Joomla
Hình 2 Kết quả chạy script
Hình 3 Lộ webroot ở file DelegatingPsrLogger do path mapping sai địa chỉ.

2. CVE-2019-18650
Joomla sử dụng component com_template cho phép người dùng có thể thêm/xóa/sửa code của template trực tiếp từ giao diện web và thư mục template này được đặt ở WEBROOT.

Hình 4 ví dụ thư mục template protostar

Các chức năng template đều được hardening, để truy cập phải thông qua quá trình routing của Joomla bao gồm kiểm tra quyền, session của người dùng như vậy ta rất khó để tìm ra lỗi nào cho phép ta thực hiện các chức năng template mà không qua xác thực. Lúc này mình nghĩ đến lỗi CSRF.
Sau khi tìm hiểu, Joomla sử dụng JSession::checkToken hoặc JRequest::checkToken để phòng chống lỗi CSRF. Ví dụ:

Hình 5 Ví dụ kiểm tra CSRF ở chức năng UploadFile

Như vậy ý tưởng tìm lỗi CSRF của như sau:

  • Duyệt toàn bộ file controller vì joomla hoạt động theo mô hình MVC, từ controller  có thể call được action trong controller đó. Ví dụ
    http://your_joomla/index.php?option=[component_name]&task=[controller_name].[action]
  • Tại mỗi file controller, kiểm tra các chức năng có sử dụng code kiểm tra token hay không, nếu không tiếp tục kiểm tra xem có cho nhận input từ người dùng hay không.
    Script mình sử dụng: https://gist.github.com/komang4130/d0516194e22a77882f845fd4324f07a6
    Kết quả:

Có khá nhiều chức năng ở component com_template không kiểm tra CSRF, trong đó mình khoanh vùng 2 file này và tiến hành phân tích.
Phân tích chức năng overrides()

Hình 6 Phân tích chức năng overrides ở Template

Chức năng nhận vào 3 tham số:

  • $file: file cần ghi
  • $override: thư mục chứa file template để ghi vào thư mục template tương ứng với $id
  • $id: id của template ( giá trị mặc định tương ứng với từng template )
    Joomla sử dụng JInput để xử lý các kiểu dữ liệu khác nhau từ người dùng, để phòng chống các lỗi nguy hiểm như XSS, SQLi, Path traversal, …. ví dụ:
Hình 7 xử lý dữ liệu string và int từ dữ liệu người dùng gửi lên.

Và ở $override ta thấy đây là nhận dữ liệu folder của người dùng, Joomla sẽ xử lý dữ liệu về folder như sau:

Hình 8 xử lý dữ liệu path từ người dùng

Tuy nhiên, biến $override được nhận theo kiểu base64

Hình 9 Xử lý dữ liệu base64 từ người dùng

Nếu trong luồng nghiệp vụ của chức năng không kiểm tra lại $override sau khi giải mã base64 sẽ xảy ra lỗi Path traversal, cho phép kẻ tấn công có thể gọi đến các thư mục ngoài WEBROOT.
Theo luồng nghiệp vụ, phân tích hàm createOverride($override) với tham số truyền vào là $override.

Hình 10 Phân tích chức năng createOverride

Như đã phân tích ở trên, dữ liệu $override tiếp tục không được kiểm tra để chống lỗi Path traversal.
Tiếp tục phân tích hàm createTemplateOverride($override, $htmlPath). Trong đó $htmlPath là thư mục tương ứng với $id, có thể truy cập từ WEBROOT.

Hình 11 Phân tích chức năng createTemplateOverride

Chức năng sẽ lấy toàn bộ file có định dạng đuôi ‘php’ từ thư mục $override để ghi vào $htmlPath. Và vẫn không có kiểm tra phòng chống lỗi Path traversal.
Như vậy, chức năng này bị hai lỗi đó là CSRF và Path traversal. Tao chain khai thác lỗi:
- Upload file mã độc php lên nơi nào đó trong máy chủ. (ftp-anon, local attack, … ).
- Tìm WEBROOT. (path disclosure, directory listing, enable error page.. ).Lừa admin ấn vào liên kết mã độc. ( XSS, Phishing )
- Mã khai thác: http://victim/joomla_path/administrator/index.php?option=com_templates&view=template&task=template.overrides&folder=base64(folder_of_malicious_php_code)&id=506&file=base64(foo_any)
Nếu kẻ tấn công có quyền quản lí thư mục template, thì kẻ tấn công có thể liệt kê các các file php trên server thư mục template và từ đó lấy thông tin các ứng dụng php có trên máy chủ. Có thể Dos hoặc ghi tràn Disk.
Mã khai thác:
http://victim/joomla_path/administrator/index.php?option=com_templates&view=template&task=template.overrides&folder=base64([WEBROOT/../../../../])&id=506&file=base64(foo_any)
trong đó
WEBROOT: path_to_joomla\components..\..\..\[to_your_malicious_folder_to_RCE]
Và với trường hợp enum thì chỉ cần đặt nhiều “..\”
Tùy target là Windows hay Linux mà ta sử dụng “../” hoặc “..\”
Minh họa lỗi:

  • Ta đã upload được lên server file mã độc php. Ở mức demo dùng chức năng CSRF Generate PoC của Burp Suite để tạo trang phishing:
  • Khi Admin click submit, mã độc được đã được thực thi.
Hình 12 Trang phishing được tạo bởi chức năng Generate CSRF PoC của Burp Suite
Hình 13 Ghi file mã độc thành công ra webroot
Hình 14 Vị trí mã độc ở WEBROOT.
Hình 15 Truy cập mã độc trực tiếp từ Web.

Mã độc liệt kê toàn bộ file php trong thư mục:
Mã khai thác trong trường hợp máy local:
WEBROOT: D:\xampp\htdocs\joomla_3.9.12
Liệt kê toàn bộ thư mục ở thư htdocs: D:\xampp\htdocs\joomla_3.9.12\components..\..\..
http://victim/joomla_path/administrator/index.php?option=com_templates&view=template&task=template.overrides&folder=base64(“D:\xampp\htdocs\joomla_3.9.12\components..\..\..\
”)&id=506&file=XZXZ
Kết quả:

Hình 16 Liệt kê toàn bộ thư mục và file php trong thư mục htdocs vào thư mục template

3. CVE-2020-8420: 1-click RCE

Phân tích hàm less():

Hình 1 Phân tích chức năng less() ở template

Chức năng này nhận vào các tham số:

- $id : id của template ( protostar hoặc beez, hoặc tự custom )

- $file : file mà ta cần xử lý.

Chức năng dùng xem nội dung của $file. Nếu kết hợp với lỗi CSRF thì rất khó để có thể khai thác vì nội dung của $file chỉ hiển thị ở trang của admin. Tiếp tục phân tích hàm compileLess($file):

Hình 2 Phân tích hàm compileLess

Tham số $file truyền vào được kiểm tra nếu có kí tự “/” thì sẽ cắt $file thành mảng dựa theo "/" và lấy giá trị cuối của mảng , như vậy có thể chống được Path Traversal ở máy chủ chạy hệ điều hành Linux.  Tuy nhiên với máy chủ chạy hệ điều hành Windows có thể bypass với kí tự “\”. Như vậy chức năng bị lỗi Path Traversal nhưng chỉ áp dụng với hệ điều hành Windows.

Phân tích hàm compileFile($fname,$outFname) trong đó có tham số $fname được tạo từ phép cộng chuỗi $path$infile , trong đó:

- $path thư mục của template tương ứng với $id.

- $infile $file sau khi giải mã base64.

Hình 3 Phân tích hàm compileFile

Hàm kiểm tra nếu như $fname truyền vào tồn tại trên máy chủ, nếu không sẽ raise exception “load error: failed to find.”$fname.

Hình 4 exception khi file không tồn tại ở chức năng compileFile

Do hàm compileLess($input) dùng try catch để handle exception nên sẽ gọi đến hàm enqueueMessage() khi có exception xảy ra.

Hình 5 Handle exception ở hàm compileLess
Hình 6 Thông báo lỗi được đưa vào hàng chờ
Hình 7 Hiển thị thông báo lỗi ra màn hình

Thông báo lỗi không được escape, như vậy với $file = base64_encode(“<script>alert(1)”), mã độc javascript được hiển thị trực tiếp ra màn hình dẫn đến lỗi XSS.

Hình 8 Mã độc XSS được inject vào câu hiển thị thông báo lỗi
Hình 9 Mã độc javascript được thi.

Mã khai thác:

http://victim/joomla_path/administrator/index.php?option=com_templates&task=template.less&view=template&id=506&file=[base64(payload_javascript)]

Ngoài ra khi phát hiện lỗi Path traversal Windows như trên, thử thêm kịch bản là đã upload được file chứa mã độc javascript lên máy chủ là payload.js

Hình 10 Tham số $fname ở compileFile bị path traversal gọi đến file payload.js
Hình 11 gọi đến hàm compile() ở model template
Hình 12 Hàm compile trong luồng nghiệp vụ của less

Biến $string là nội dung của file payload.js.

Hình 13 phân tích hàm compile trong luồng nghiệp vụ của less

Hàm parse() sẽ gọi đến một parser trong core ở file JOOMLA_PATH\libraries\vendor\leafo\lessphp\lessc.inc.php. Tương tự trả về thông báo lỗi chứa nội dung là mã độc javascript.

Hình 14 Thông báo lỗi sử dụng cộng chuỗi chưa được escape
Hình 15 Thông báo lỗi trả về không qua escape
Hinh 16

Từ những kịch bản như trên, có 2 cách để ta khai thác chiếm quyền máy chủ:

- Cách 1:

Ø Upload mã độc javascript lên server thông qua local attack hoặc ftp-anon.

Ø Dùng path traversal gọi mã độc (Chỉ áp dụng cho Windows).

Ø Lừa admin truy cập liên kết chứa mã độc ( Open redirect, Phishing).

- Cách 2:

Ø Lừa admin truy cập liên kết chứa mã độc ( Open redirect , Phishing).

Cách 1 thực hiện khai thác tương tự như bài viết ở phần 2. Ở đây định dạng file không quan trọng, chỉ cần upload được file chứa mã độc lên server ( pdf , txt , .... ).

Demo khai thác theo cách 2

Chức năng template cho phép sửa nội dung của file bất kì ở  template với request như sau

Hình 17 Request sửa file template của joomla

Để thực hiện ta cần csrf.token.

csrf.token nằm ở nội dung trang html của admin ở  file hợp lệ thuộc đường dẫn http://victim/administrator/* ( Ví dụ index.php ).

Hình 18

Viết mã độc javascript ghi mã độc php thực hiện lệnh whoami lên file error.php ở template protostar dựa vào request ghi file ở hình 32: https://gist.github.com/komang4130/0ed6f8cffd3e57d20cfd6052f6db2e8c

Mã khai thác:

http://your_host/path_to_joomla/administrator/index.php?option=com_templates&task=template.less&view=template&id=506&file=[base64_javascript]

Minh họa lỗi:

Hình 19 Khi admin click hoặc truy cập mà liên kết chứa mã độc trên.

4. CVE-2020-8419

Dựa theo kết quả của script csrf_check ở phần 2.

Lỗi CSRF xảy ra ở controller Redirect, action purge() ở file administrator\components\com_redirect\controllers\links.php do không có kiểm tra CSRF token ở chức năng của admin.

Hình 1 action purge() ở controller redirect.

Mã khai thác: http://your_joomla/administrator/index.php?option=com_redirect&view=links&list%5Bfullordering%5D=a.old_url+ASC&list%5Blimit%5D=20&filter%5Bstate%5D=1&filter%5Bhttp_status%5D=&limitstart=0&new_url=&comment=&batch_urls=&task=links.purge&boxchecked=1

Tương tự, mình tìm được thêm 3 chỗ lỗi tương tự

Action purge()administrator\components\com_languages\controllers\overrides.php

Hình 2 action purge ở controller languages

Mã khai thác: http://your_joomla/administrator/index.php?option=com_languages&view=overrides&language_client=vi-VN1&filter%5Bsearch%5D=&list%5Blimit%5D=20&limitstart=0&task=overrides.purge&boxchecked=0&list%5Bfullordering%5D=null+ASC

Action purge() và clean() ở administrator\components\com_associations\controllers\associations.php

Hình 3 action purge và clean ở controller associations

Mã khai thác action purge(): http://your_joomla/administrator/index.php?option=com_associations&task=associations.purge&itemtype=&language=vi-VN&filter%5Bsearch%5D=&list%5Bfullordering%5D=id+ASC&list%5Blimit%5D=25&filter%5Bstate%5D=&filter%5Bcategory_id%5D=&filter%5Bmenutype%5D=&filter%5Baccess%5D=&filter%5Blevel%5D=

Mã khai thác action clean():

http://your_joomla/administrator/index.php?option=com_associations&task=associations.clean&itemtype=&language=vi-VN&filter%5Bsearch%5D=&list%5Bfullordering%5D=id+ASC&list%5Blimit%5D=25&filter%5Bstate%5D=&filter%5Bcategory_id%5D=&filter%5Bmenutype%5D=&filter%5Baccess%5D=&filter%5Blevel%5D=

Như vậy để khai thác lỗ hổng này, ta phải lừa được người quản trị click vào liên kết độc hại, tuy nhiên không phải lúc nào ta cũng có thể làm được nếu người quản trị viên có kiến thức về security.  Vậy liệu có cách nào đó làm cho họ phải click mà không nghi ngờ gì không? Lúc này mình nghĩ đến chức năng check-in của admin.

Chức năng này tựa như là thông báo khi có bài viết mới và người quản trị viên thường sẽ click vào đọc mà không nghi ngờ gì.

Như vậy ta cần một tài khoản có quyền tạo bài viết, ở chức năng tạo bài viết của Joomla cho phép ta chèn image và sửa css ở thẻ style tuy nhiên lại không kiểm tra dữ liệu người dùng dẫn đến bị lỗi CSS Injection và có thể dẫn đến account takeover để đánh cắp số lượng lớn tài khoản người dùng nếu như server joomla đã bị kẻ tấn công chiếm quyền.

Ví dụ:

Hình 4 Tạo image với style với mã khai thác như hình

Mỗi khi người dùng truy cập vào bài viết thì kẻ tấn công có thể lấy được phiên làm việc của người dùng và đăng nhập.

Hình 5 phiên làm việc của người dùng.

Tương tự khi admin thấy thông báo có bài viết mới và check in thì phiên làm việc của admin cũng bị lộ.

Hình 6 Admin check-in bài viết
Hình 7 phiên làm việc của admin.

Tuy nhiên lỗi này chỉ khai thác dẫn đến lỗi Account takeover với các điều kiện:

-          Kẻ tấn công đã chiếm quyền máy chủ Joomla từ trước ( thông qua local-attack , …. )

-          Kẻ tấn công có tài khoản với quyền tạo bài viết.

Nhưng mà không phải lúc nào ta cũng chiếm quyền được máy chủ từ trước, vì khi đã chiếm quyền được máy chủ ta có thể làm nhiều việc khác thay vì chỉ takeover account. Tuy nhiên với các lỗi CSRF và ở phương thức GET, ta có thể kết hợp với CSS Injection để khai thác và chỉ cần điều kiện cần tài khoản với quyền tạo bài viết.

Như vậy, ví dụ trường hợp cụ thể , ta muốn xóa TOÀN BỘ bài viết được associations sang ngôn ngữ khác của người dùng. Ta sẽ tạo bài viết mới, chèn thẻ image và ở thẻ style chèn mã khai thác css injection.

Ví dụ hiện tại ta thấy có 3 bài viết tiếng Việt được associations sang tiếng Anh , có 2 public và 1 chưa public:

Hình 8 danh sách các bài viết association hiện tại ở Joomla

Để khai thác, tạo bài viết chứa mã độc:

Hình 9 Tạo bài viết chứa mã độc

Admin khi online sẽ nhận được thông báo có bài viết mới:

Hình 10 thông báo nhận bài viết mới từ người dùng

Khi admin click vào check in, ta thấy sẽ có request đến action purge.

Hình 11 Admin truy cập để đọc bài viết mới.
Hình 12: request được tạo từ css injection gửi đến action purge.
Hình 13 Toàn bộ bài viết association đều đã bị xóa

Lỗi này còn có có thể được chain với CVE-2019-18650 hoặc bất cứ lỗ hổng có thể được khai thác ở phương thức GET ( trừ các lỗ hổng cần hiển thị nội dung trang html như XSS ) và để khai thác kẻ tấn công chỉ cần quyền người dùng có thể tạo bài viết.