Chuyện là trong hệ thống công ty mình đang chạy 1 con server ManageEngine ServiceDesk gần chục năm tuổi (xin phép được giấu version). Lúc mới vào công ty khoảng 8 tháng trước cũng được giao việc check xem con này có lỗi gì không thì mình nhận thấy nó cổ đến mức không bị một cái CVE nào nghiêm trọng, vì thế cũng không chứng minh được impact để yêu cầu update.
Dạo gần đây công ty có triển khai thử nghiệm một số giải pháp SOC mới, mình được giao nhiệm vụ redteam vào những con được giám sát để test, trong đó có con này. Nghĩ lại vẫn có gì đó cay cay, và mình cũng muốn xem trình độ của mình sau 8 tháng có tăng thêm tí nào không nên mình đã quyết định focus vào nó. Đây là hành trình từ không có gì đến RCE được hệ thống.
1. Tìm lỗ hổng trong ServiceDesk
Tải version của con hàng này về, mình tập trung tìm các endpoint unauth trước. Ngày trước mình hay tìm lỗi dựa vào sink, cứ search string ví dụ Runtime.exec
nhưng như thế sót khá nhiều nên dạo gần đây mình hay tìm theo source.
Nhanh chóng phát hiện ra 1 endpoint unauth dính LFI là /fosagent/repl/download-file?basedir=4&filepath=<file>
(Sau này check lại mới biết nó cũng đã được gắn CVE từ trước)
Servlet handle là com.zoho.clustering.agent.filerepl.api.DownloadFileServlet
, nhận trực tiếp filepath mà không check gì cả, sau đó read file và trả về dưới dạng attachment
Mừng rơi nước mắt, mình nhanh chóng check xem trong folder install ServiceDesk (SD) có file nào nhạy cảm hay account admin không. Tuy nhiên cuộc đời không dễ dàng như vậy. Cũng có một số file log nhưng không có gì có thể lợi dụng.
Lúc này thử nmap IP thì phát hiện hệ thống public port 1433 của MS SQL ra ngoài.
Khi mình install local thì mình dùng Postgresql theo mặc định nhưng đọc document của SD thì có thể đổi sang MS SQL thông qua changeDBServer.bat
command trong folder bin
.
Vì thế mình thử xem có thể lợi dụng MS SQL không.
2. TÌm lỗ hổng để vào được MS SQL
Theo mặc định, MS SQL Server khi được cài sẽ nằm ở C:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA
, và database của SD tên là servicedesk
Hí hửng download file này về nhưng không
Bị access denied vì không có quyền đọc?? Ta thấy nó ghi FileNotFound
nhưng thực tế nó access denied, vì nếu là FileNotFound
thì nó sẽ như sau
Bỏ qua chuyện đó, mình tìm được chức năng backup trong SD. Khi chạy backUpData.bat
Một file backup được tạo ra ở trong folder backup (có thể custom)
Tên của file sẽ được gắn thêm thời gian chạy backup. Mình cũng không cần bruteforce để biết tên nó vì tên của nó đã được lưu trong log tại C:\ManageEngine\ServiceDesk\bin\SDPbackup.log
Nhìn tên thì mình thấy khá yên tâm khi nó được backup hàng ngày (nhưng cũng không yên tâm lắm vì backup nằm ngay trên server 🙄).
Bây giờ thì chỉ cần đọc file đó. Nhưng một lần nữa số phận lại trêu ngươi. Mình có thể đọc trên con server local của mình, nhưng như các bạn thấy, folder backup của con SD cần tấn công lại nằm ở ổ D. Trước giờ chưa từng gặp trường hợp như này nên cũng chưa thử, nhưng đến bây giờ mình mới biết trong Windows mặc định khi các bạn path traversal từ ổ C thì không thể ra được ổ D.
Kết quả không bất ngờ
Bây giờ chúng ta chỉ có 1 lỗi LFI đọc được các file ở ổ C, chúng ta có thể làm gì với nó?
Mình trở lại với việc tìm lỗi trên SD. Mình phát hiện thêm một endpoint mới khá tiềm năng là /fosagent/repl/take-snapshot
, servlet com.zoho.clustering.agent.filerepl.api.TakeSnapshotServlet
. Chức năng của nó là snap shot lại ổ đĩa sau đó zip và lưu lại vào C:\ManageEngine\ServiceDesk\server\default\deploy\fosagent.war\snapshots
Nếu kết hợp với lỗi LFI kia thì hoàn toàn có thể đọc được toàn bộ data.
Mình định sử dụng lỗi này để tải về cả ổ đĩa nhưng sau khi chạy được 1 lúc thì server mình báo run out of disk space mặc dù trước đó mình để thừa gần 100GB.
Full disk
Tình hình layoff đang khá căng, giờ mà bị đuổi việc thì chết đói, do vậy mình cũng không dám chạy trên server target, sợ nó lại sập.
Đến đây thì mình cũng đã sức cùng lực kiệt, tìm đến giải pháp cuối cùng là cố gắng crack sa
password của MS SQL Server. Password được lưu tại C:\Program Files\Microsoft SQL Server\110\Setup Bootstrap\Log\<time_of_something_random>\Datastore\ProductSettings_SqlEngine_Public.xml
Mình cũng không cần quan tâm cái chỗ folder kia là gì vì mình có thể đọc nó trong file C:\Program Files\Microsoft SQL Server\110\Setup Bootstrap\Log\Summary.txt
Đã có được password
Lúc này đi hỏi ChatGPT nhưng cũng không biết đây là thuật toán gì
Trong document của Microsoft cũng nói rằng cách mã hóa SA password không được công khai vì lí do bảo mật. Chẳng lẽ lại reverse SQL Server. Kiệt sức vì nó, mình quay đi làm việc khác.
Trong quá trình đó mình cũng có một công việc khác là audit con SupportCenter của ManageEngine. Nhận thấy code khá giống nhau, và trong khi debug thì có 1 tính năng mình có thể extract raw password của SQL Server, mình quyết định tìm hiểu sâu hơn. (Sau này nhận được tin sét đánh từ đồng nghiệp là SupportCenter là tên khác bản mới của ServiceDesk).
3. Lấy password của MS SQL Server
Như mình đã nhắc ở trên, SD cho phép người dùng thay đổi database server bằng cách chạy changeDBServer.bat
, giao diện của nó như thế này đây
Sau một hồi mày mò, mình nhận thấy khi save config, bật lên trở lại và test connection thì nó thành công luôn, nghĩa là nó đã lưu password ở đâu đó??? Đáng lẽ mình phải nghĩ tới điều này sớm hơn.
Thử debug vào quá trình nó check connection, mình nhận ra điều đó là đúng, hơn nữa nó đã load được cả password clear text.
Hàm thực hiện load config là com.adventnet.servicedesk.tools.ChangeDBServer#getDBDetails
.
Hàm này thực hiện đọc file config như sau
Như vậy với MS SQL Server, file config sẽ nằm trong C:\ManageEngine\ServiceDesk\server\default\deploy\mssql-ds.xml
Tuy nhiên trong file này không có thông tin đăng nhập
Tiếp tục debug, mình thấy nơi nó get username và password là hàm com.adventnet.servicedesk.tools.ChangeDBServer#getDBUserNameAndPassword
Như vậy file lưu user và pass là C:\ManageEngine\ServiceDesk\server\default\conf\login-config.xml
Ngon luôn, đã tìm được user và password, tuy nhiên đang ở bản encryption.
Phía trên có password cleartext, vì vậy chắc chắn có đoạn decrypt ở đâu đó, tiếp tục debug mình thấy hàm giải mã là org.jboss.resource.security.SecureIdentityLoginModule#decode
Sau đó password về dạng clear text
Mừng rơi nước mắt một lần nữa, mình thực hiện đọc file lấy password encrypt trên server.
Không cần biết cái thuật toán mã hóa kia là gì, mình chỉ cần thay cái mật khẩu đã mã hóa vào trong file config của mình, sau đó run debug lại, để tự server của mình giải mã cái password của server target, kết quả decrypt thành công
Thành công login tới con này từ remote, đoạn này rõ ràng đội vận hành đã không cấu hình kĩ càng.
4. Leo quyền Admin, thực hiện RCE
Khi đã chọc được DB, lên admin khá đơn giản, chỉ mất hơi nhiều thời gian mày mò xem user, quyền các thứ ở trong bảng nào để sửa. Tạo hẳn 1 nick mới, up lên quyền Admin, mình thành công trở thành admin của SD. Đoạn này không có gì quá technical nên mình xin được skip, cũng là một bài test kiên trì cho độc giả muốn thử làm.
Khi đã là Admin, ta có thể thực hiện RCE thông qua chức năng Custom Trigger.
ServiceDesk có 2 chức năng để RCE cho admin là Custom Trigger và Custom Schedule. Khác với Custom Schedule là tạo một command chạy theo thời gian định sẵn, Custom Trigger chạy khi có một ticket được tạo / sửa match với điều kiện. Vì vậy cứ mỗi lần phải tạo / sửa request mới có thể RCE -> thông báo được gửi tới người quản lý ticket.
Lời kết
Qua lần này, bên mình cũng đã có thêm nhiều bài học về việc làm sao để cấu hình hệ thống chuẩn hơn, luôn luôn cập nhật hệ thống lên bản mới nhất cũng như tăng cường khả năng giám sát.