
Grafana OSS 13.2.3 - High - Remote - SSRF - Git Sync URLValidator is admission-only DNS
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · grafana-gitsync-dns-rebind
Class: SSRF Reach: Remote
Grafana OSS 13.2.3 (6193dc0) - Grafana Labs
Git Sync URLValidator checks the repository URL host at admission. nanogit / Test re-resolve later with no IP pin. isBlockedIP is loopback, RFC1918, link-local, and unspecified only. Literal 0.0.0.1 is not blocked. A hostname that is public at validate time can rebind to RFC1918 for the clone GET.
Org Admin Test of a rebinding Git URL makes Grafana GET an internal host the validator was written to refuse.
| ID | no CVE yet |
| Class | SSRF (DNS rebind / 0.0.0.0/8 gap) |
| Reach | Remote (Org Admin repositories:create) |
| CWE | CWE-918 / CWE-367 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N |
| Product | Grafana OSS |
| Affected | 13.2.3 (6193dc0); Git Sync on by default |
| Auth | Org Admin (repositories:create / :write). Not Viewer/Editor create. |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Actor is Org Admin, not a stranger. Org Admin can already point datasources at internal URLs on OSS. This leftover is bypass of the Git Sync URLValidator that was meant to close that class for repository URLs.
| Privilege | Method / sink | Recon / integrity / availability | Lab-proven or inferred |
|---|---|---|---|
| Make Grafana GET a rebinding Git URL | Org Admin POST .../repositories/{name}/test with spec.git.url=http://git.lab/... | Recon (outbound HTTP from the Grafana process) | Lab-proven. DNS A1 public, later A RFC1918. Rebind oracle hits=3, witness in body |
Reach a literal 0.0.0.1 Git URL | Same Test with http://0.0.0.1:18080/lab/gitsync.git | Recon | Lab-proven not blocked at admission (status 400 transport, not 422 public-or-allowed) |
| Read Git Smart HTTP / HTTP body from that host into Test errors | gitRepository.Test interpolates transport error | Recon | Inferred from pin. Lab used a 200 git advertisement |
| Rebind to RFC1918 / CGNAT after admission | TOCTOU: validate DNS then nanogit dial | Recon | Lab-proven for RFC1918 sidecar 172.30.60.3 |
Viewer read of stored spec.*.url including userinfo | repositories:read | Recon | Inferred from pin. Not this lab |
Closed on this leftover (lab-proven): literal loopback 127.0.0.1 422; RFC1918 literal 10.0.0.1 422; IMDS 169.254.169.254 422. Closed: Editor cannot create repos. Not RCE. Not Grafana Admin required. Skip Grafana G2 (IAM SSOSetting k8s authorizer).
Admission in apps/provisioning/pkg/repository/validator.go calls URLValidator.ValidateURL. isBlockedIP is four predicates. Unspecified is 0.0.0.0 / :: only. Remainder of 0.0.0.0/8 and CGNAT pass. Clone later uses nanogit.NewHTTPClient(gitConfig.URL) and re-resolves.
Wrong turns already recorded: treating Org Admin DS URLs as this leftover (those are intended; this is the Git Sync validator bypass); expecting IMDS 169.254 to pass (blocked at admission); Editor create (denied); a reverse shell. Theatre.
python3 grafana-gitsync-dns-rebind-Abraxas-Labs.py
or:
cd lab
./run.sh
Loopback: Grafana 127.0.0.1:19360, DNS control 127.0.0.1:19361, rebind oracle 127.0.0.1:19362. Image grafana/grafana:13.2.3.
SUCCESS GRAFANA-GITSYNC-DNS-REBIND how=live actor=org-admin path=rebind dns_lookups=4 negatives=3 GRAFANA-GITSYNC-DNS-REBIND-WITNESS
Pin the resolved address through clone/Test, or re-validate on every dial. Treat 0.0.0.0/8 and CGNAT as blocked. Do not skip DNS on allowlisted hosts without pinning.
GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.