Mở đầu
Bonobo Git Server là một Git Server dành cho máy chủ Windows, khi mà Linux đã có rất nhiều các giải pháp xịn xò như Git Lab, thì có vẻ đối với nền tảng Windows, người dùng không có nhiều lựa chọn. Bonobo Git Server hiện đang có hơn 1.7K star trên Github và version hiện tại đang là 6.5. Phiên bản bị lỗi là 6.3. Trước tiên mình sẽ đi vào setup trước đã. Bài này mình sẽ ghi cụ thể các bước, coi như là note lại về cách debug .NET cho khỏi quên và luyện skill debug .NET .
Cài đặt
IIS
Trước tiên là chúng ta cần máy ảo, Mình đã chuẩn bị sẵn một máy ảo Win10, và bắt đầu cài. Chúng ta vào phần Turn Windows features on or off từ Control Panel và bật các tính năng sau để có thể chạy được ứng dụng:
- Internet Information Services
- IIS Management Console
- ASP.NET 4.8
- Static Content
Sau khi cài xong và khởi động lại chúng ta đã có thể truy cập vào màn hình quản lý của IIS.
Cài đặt app
Trên trang chủ đang là phiên bản mới nhất, chúng ta tải phiên bản lỗi từ link sau: https://bonobogitserver.com/resources/releases/6_3_0.zip và giải nén. Để được thư mục Bonobo.Git.Server. Copy thư mục này vô C:\inetpub\wwwroot
Ở đây ta cần điều chỉnh quyền thư mục để user của IIS có thể chỉnh sửa được. Bật cấu hình Properties của thư mục App_Data lên, vào chỉnh full quyền cho IIS_IUSRS:
Giờ chuyển qua màn hình của Internet Information Services (IIS) Manager:
Chọn Convert to Application, OK với cấu hình mặc định. Giờ vào phần Authentication của IIS và đảm bảo là Anonymous Authentication được Enabled:
Truy cập vào http://localhost/Bonobo.Git.Server thấy hiện ra giao diện và setup user admin như vầy là xong. Nếu đến đây bạn vẫn chưa chạy được thì có thể tham khảo thêm video: https://www.youtube.com/watch?v=S3Dm2Z-hc1o
Cài đặt debug
Khi vào thư mục bin ta có thể thấy các file dll:
File mình cần quan tâm là: Bonobo.Git.Server.dll. Bật DnSpy lên, clear all tất cả các dll hiện có và load dll này vào:
Tuy nhiên, hiện giờ vẫn chưa debug được do code đang được precompiled, nên dù đặt breakpoint ở file này, cũng sẽ không hit breakpoint (vì server dùng file precompiled và có dùng file dll này đâu ). Để có thể debug được thì mình cần disable tính năng này, chi tiết tham khảo ở đây: https://blog.richardszalay.com/2019/06/14/debugging-sitecore-assemblies/
Sau khi tải file IISAssemblyDebugging.psm1 từ link gist trong bài, bật powershell mới với quyền admin và chạy:
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Import-Module .\IISAssemblyDebugging.psm1
Enable-IISAssemblyDebugging C:\inetpub\wwwroot\Bonobo.Git.Server\
Giờ chúng ta đã có thể đặt breakpoint và debug được rồi, bấm Ctrl + Alt + P hoặc từ menu Debug > Attach to Process, và chọn w3wp.exe:
CVE 2019-11217
Ngoài ra còn có CVE 2019-11218 liên quan đến leo quyền từ auth user lên admin, mình sẽ để sau.
Tìm sink và source
Xem thông tin trên https://nvd.nist.gov/vuln/detail/CVE-2019-11217:
The GitController in Jakub Chodounsky Bonobo Git Server before 6.5.0 allows execution of arbitrary commands in the context of the web server via a crafted http request.
Vậy đây là một lỗi RCE và có vẻ như là unauthenticated. Tuy nhiên thông tin chưa được đầy đủ lắm, tìm thêm ở link https://flab.cesnet.cz/advisories/cve-2019-11217 cho chúng ta cái nhìn chi tiết hơn:
Bonobo.Git.Server/Controllers/GitController.cs
atprivate ActionResult GetInfoRefs(String repositoryName, String service)
callsGitService.ExecuteServiceByName(…)
without sanitizing user supplied stringserviceName
. Succesfull exploitation process involves creation of the new repository, management of repository configuration and executing arbitrary command throughcore.editor
setting.
Chi tiết đã có, nhưng thay vì đọc patch thì mình sẽ đọc code trước, tìm đến file GitController.cs
:
Chúng ta chú ý đến dòng:
string serviceName = service.Substring(4);
Vậy tham số cần có độ dài tối thiểu 4 ký tự:
chọn Analyze:
method GetInfoRefs
được dùng duy nhất bởi SecureGetInfoRefs
Chúng ta thấy đã xuất hiện tham số service
như trong thông tin. Đọc nhanh qua hàm này ta có thể hiểu flow như sau:
- Tham số
service
sẽ được kiểm tra vớigit-receive-pack
, nếu đúng là service này thì sẽ kiểm tra xem User hiện tại có quyền push đối với repo hay không. Nếu không thì văng 401. - Trường hợp còn lại, nếu user (đã đăng nhập hoặc anonymous) có quyền xem repo thì sẽ đi vào case trigger lỗi.
Tuy nhiên hàm này được gọi từ đâu hoặc endpoint là gì thì chúng ta chưa biết. Thử tìm nhanh trên github ta phát hiện ra endpoint:
Như vậy, để trigger được bug unauth thì ta cần GET đến link http://localhost/Bonobo.Git.Server/{repositoryName}.git/info/refs?service=xxxx và như vậy, server cần phải có repo được public ra bên ngoài (chỉ cần xem, không cần push code). Mình setup một repo mới public-repo
như sau:
Giờ đặt breakpoint ở đầu method SecureGetInfoRefs
và truy cập vào link: http://localhost/Bonobo.Git.Server/public-repo.git/info/refs?service=git-receive-pack thì hit breakpoint:
OK, vậy là xong phần source, giờ chúng ta sẽ xem tiếp phần sink. Tìm đến GitService.ExecuteServiceByName
, đặt breakpoint ở dòng 31 và continue:
Chúng ta có thể thấy rất rõ các tham số được truyền vào. Tham số serviceName
đã bị cắt đi 4 ký tự đầu và chỉ còn receive-pack
và được nối chuỗi với các tham số khác để đưa vào làm tham số cho git.exe
(nằm ở C:\inetpub\wwwroot\Bonobo.Git.Server\App_Data\Git) và thực thi.
Và đúng là bản patch đã confirm format của tham số như vậy: https://github.com/jakubgarfield/Bonobo-Git-Server/commit/2e547948f394e78e4cd03c46543b33059ff444cd#diff-45dd4dd61d7c23aa8787e129094a7ba2842de4ea2067538a9f685f6c2cc2e7ef
Setup Process Monitor và filter các process git.exe
:
sau đó continue, ta sẽ thấy git.exe
được thực thi, sau đó spawn git-receive-pack.exe
và thực thi:
Bế Stuck
Sau một hồi thử các tham số khác nhau thì mình nhận thấy:
- Chúng ta chỉ có thể execute các sub command của git chứ không break out ra ngoài và chạy các cmd tùy ý được (cũng giống như trong miêu tả của CVE). Lý do là payload của chúng ta đã được đưa vào làm tham số của
ProcessStartInfo
:
var info = new ProcessStartInfo(gitPath, args)
- Các câu lệnh thực thi đều sẽ có 2 tham số
--stateless-rpc
và--advertise-refs
và cả những tham số đằng sau nữa, nên không phải command nào cũng chạy được, thường sẽ văng ra exit code 129 hoặc 128, do dùng sai hướng dẫn sử dụng 😥 và mình đang stuck ở đây. - Thử thêm hướng add thêm config thông qua
git config
để thêm cấu hìnhcore.editor
vào cũng vẫn đang chưa làm sao để ignore đống param củ chuối đằng sau được.
Update 1
Nhờ sự trợ giúp của người em @minhtuan.nguy , mình đã có thể update được setting của core.editor
thành binary mong muốn.
@minhtuan.nguy sử dụng trick thêm "
vào trước binary để đưa toàn bộ phần đằng sau thành cmd, và với calc.exe
không nhận tham số thì khi trigger RCE sẽ không bị ảnh hưởng:
Với cách này, cmd ban đầu:
"C:\inetpub\wwwroot\Bonobo.Git.Server\App_Data\Git\git.exe" {payload} --stateless-rpc --advertise-refs C:\inetpub\wwwroot\Bonobo.Git.Server\App_Data\Repositories\public-repo
Sẽ trở thành
"C:\inetpub\wwwroot\Bonobo.Git.Server\App_Data\Git\git.exe" "{payload} --stateless-rpc --advertise-refs C:\inetpub\wwwroot\Bonobo.Git.Server\App_Data\Repositories\public-repo"
và kết quả
Bước tiếp theo là trigger RCE thông qua việc invoke core.editor
, theo mình biết thì có thể thông qua một số lệnh
git config -e -f {tên bất kỳ}
git commit
git commit --amend
Tuy nhiên vẫn là vấn đề cần điều chỉnh đưa phần param thừa đằng sau --stateless-rpc...
làm sao cho thành câu lệnh hợp lệ
Updating... (nếu tìm ra được cách trigger RCE)
The End
🇻🇳🇻🇳🇻🇳