Eldoria Realms
Eldoria Realms - Web Challenge
Author: tan3ora
- Tên: Eldoria Realms
- Description: A portal that allows players of Eldoria to transport between realms, take on quests, and manage their stats. See if it’s possible to break out of the realm to gather more info on Malakar’s spells inner workings.
- Có source code
Phân tích
Trang web có 2 service :
- Website viết bằng Ruby
- gRPC Server
Ruby class pollution qua gộp đệ quy
Phân tích đoạn code dưới đây ta biết được cách server merge JSON object người dùng nhập vào với object $player đã có:
1 | class Adventurer |
Class pollution trong Ruby
Ruby phụ thuộc rất nhiều vào mô hình lập trình hướng đối tượng. Khi gọi object.singleton_class, hệ thống sẽ tạo ra một lớp ẩn, chỉ dành riêng cho đối tượng đó. Tuy nhiên, nếu thực hiện đệ quy theo cách cho phép truy cập vào các lớp cha, đặc biệt là khi tham chiếu đến superclass thì có khả năng “leo lên” chuỗi thừa kế. Điều này có thể khiến attacker ghi đè các biến cấp lớp hoặc thậm chí là các biến cấp module.
Chi tiết về Class Pollution trong Ruby có thể đọc thêm tại đây.
Quay lại với bài này,tại endpoint /merge-fates hàm merge_with đã sử dụng recursive_merge() với input là JSON từ user:
1 | post "/merge-fates" do |
Điều quan trọng là recursive_merge sẽ tạo các biến instance mới bất cứ khi nào một key không tồn tại. Nó không bao giờ giới hạn các key có thể được gộp. Do vậy ta có thể thao túng thuộc tính của class Object, là class được thừa kế bởi tất cả class khác.
Payload:
1 | { |
Ta đã thao túng được từ player -> (player's) class -> (its) superclass -> realm_url,vì vậy mỗi khi gọi Adventurer.realm_url đều sẽ trả về attacker_url.
Khi Adventurer.realm_url bị ghi đè,mỗi khi truy cập /connect-realm đều sẽ thực hiện lệnh curl đến attacker_url:
1 | get "/connect-realm" do |
Curl Gopher SSRF -> gRPC
Ở gRPC server cung cấp cho chúng ta 2 phương thức. Trong phương thức CheckHealth() tồn tại lỗ hổng OS Command Injection:
1 | func (s *server) CheckHealth(ctx context.Context, req *pb.HealthCheckRequest) (*pb.HealthCheckResponse, error) { |
Từ Dockerfile phát hiện ra rằng phiên bản 7.70.0 được sử dụng, vốn dễ bị tấn công bằng cách chuyển đổi giao thức chéo sử dụng gopher:// :
1 | # Install curl with shared library support |
Bằng cách thiết lập realm_url thành 1 URL với giao thức gopher://,ta buộc curl kết nối trực tiếp đến 127.0.0.50051 qua TCP.Thay vì xử lý nó như 1 HTTP request,curl coi đó là raw bytes qua giao thức gopher ,ta có thể dựa vào điều này để tương tác với gRPC.
Tìm hiểu thêm về gopher tại đây.
Khai thác
Flow: Ruby class pollution -> Curl gopher SSRF -> GRPC protocol -> Command injection
Đầu tiên ta cần tạo payload để gửi đi bằng gopher.
Sau khi cố craft bằng tay không được thì cách dùng các công cụ sẫn có như grpcurl để gửi request hợp lệ đến gRPC server và capture lại bằng wireshark thì sẽ hợp lý hơn.
1 | grpcurl \ |
Follow TCP Stream để lấy request.
image
Sau đó export ra dạng raw để xử lý.
image
Dùng python để xử lý hex và URLencode payload:
1 | import urllib.parse |
Khai thác Class Pollution để thay đổi realm-url thành gopher URL:
image
Gửi request tới endpoint /connect-realm để trigger tới lệnh curl
image
image
Decode để lấy flag:
1 | echo "SFRCe2Y0azNfZmw0Z19mMHJfdDM1dDFuZ30=" | base64 -d |