Response playbooks, investigation guides, and Atomic Red Team tests are Pro-only. Upgrade to unlock the full detection package for THREAT-CodeRepo-GistExfil.

Upgrade to Pro
THREAT-CodeRepo-GistExfil Microsoft Sentinel · KQL

Detect Data Exfiltration via GitHub Gists and Private Code Repositories in Microsoft Sentinel

Adversaries and malicious insiders increasingly use code-hosting platforms (GitHub, GitLab, Bitbucket) as covert exfiltration channels because traffic to these domains is rarely blocked by web proxies and blends with routine developer activity. Two distinct abuse patterns are observed: (1) anonymous or throwaway-account Gist/paste creation used as a low-friction dead drop for small stolen artifacts (credentials, config files, session tokens) — documented in Turla dead-drop resolver infrastructure and multiple commodity loader families that stage stolen data via the GitHub Gist API before onward retrieval; and (2) bulk exfiltration via `git push` or GitHub API PUT/POST calls to a personal or attacker-controlled repository, seen in APT41 intrusions abusing developer tooling and in Lazarus Group operations staging stolen source code and credentials on GitHub/GitLab ahead of retrieval. The same channel is a leading insider-threat vector: departing employees push proprietary source code or customer data to a personal GitHub account under the cover of routine commits. Detection must distinguish these from the overwhelming volume of legitimate CI/CD and developer git traffic, so the strongest signals are (a) git remotes that do not match the organisation's registered GitHub/GitLab organisation, (b) anonymous Gist creation (no owning account, effectively unlisted/unattributable), and (c) API calls using PUT/POST verbs against content endpoints from processes other than the organisation's recognised CI/CD runners.

MITRE ATT&CK

Tactic
Exfiltration

KQL Detection Query

Microsoft Sentinel (KQL)
kusto
let CorpGitOrg = "<YOUR_GITHUB_ORG>"; // e.g. "df00tech" - replace with your registered org/group name
let CodeRepoApiDomains = dynamic(["api.github.com", "api.gitlab.com", "api.bitbucket.org"]);
// Signal 1: git push to a remote that does not reference the corporate org/group
let NonCorpGitPush = DeviceProcessEvents
| where Timestamp > ago(24h)
| where FileName =~ "git.exe" or FileName =~ "git"
| where ProcessCommandLine has "push"
| where ProcessCommandLine has_any ("github.com", "gitlab.com", "bitbucket.org")
| where not(ProcessCommandLine has CorpGitOrg)
| extend Signal = "GitPushNonCorpRemote"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, Signal;
// Signal 2: anonymous Gist / paste creation via scripting engines calling the Gist API
let AnonGistCreate = DeviceProcessEvents
| where Timestamp > ago(24h)
| where FileName has_any ("curl", "curl.exe", "powershell.exe", "pwsh.exe", "python", "python.exe")
| where ProcessCommandLine has "api.github.com/gists"
| where ProcessCommandLine has_any ("-X POST", "-X PUT", "Invoke-RestMethod", "Invoke-WebRequest", "requests.post", "method='POST'")
| extend Signal = "AnonGistCreate"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, Signal;
// Signal 3: direct Contents API PUT (file upload without git client) from a non-CI/CD host
let ContentsApiUpload = DeviceProcessEvents
| where Timestamp > ago(24h)
| where FileName has_any ("curl", "curl.exe", "powershell.exe", "pwsh.exe", "python", "python.exe")
| where ProcessCommandLine has_any (CodeRepoApiDomains)
| where ProcessCommandLine has_any ("/contents/", "/repos/") and ProcessCommandLine has_any ("-X PUT", "-X POST", "requests.put", "requests.post")
| where DeviceName !endswith "-runner" and DeviceName !endswith "-agent" // exclude known CI/CD runner naming convention
| extend Signal = "ContentsApiUpload"
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName, Signal;
union NonCorpGitPush, AnonGistCreate, ContentsApiUpload
| sort by Timestamp desc
high severity medium confidence

Three-signal detection for exfiltration to code repositories: (1) `git push` commands whose remote URL does not reference the organisation's registered GitHub/GitLab/Bitbucket namespace — the strongest indicator that data is leaving to a personal or attacker-controlled repository; (2) scripting-engine calls to the GitHub Gist API using PUT/POST, which covers anonymous or throwaway-account Gist creation used as a dead drop; (3) direct Contents/Repos API uploads from hosts that do not match the naming convention of known CI/CD runners. Replace `<YOUR_GITHUB_ORG>` and the runner-hostname suffix check with values matching your environment before deployment.

Data Sources

Microsoft Defender for Endpoint (DeviceProcessEvents)Sysmon Event ID 1Proxy/web gateway logs (GitHub/GitLab/Bitbucket categories)

Required Tables

DeviceProcessEvents

False Positives & Tuning

  • Developers legitimately pushing personal fork branches to their own GitHub account as part of an approved open-source contribution workflow
  • Security researchers or DevRel staff authorised to publish public Gists of sanitised code snippets
  • New CI/CD runners or self-hosted agents that do not yet match the expected hostname naming convention — update the exclusion list rather than suppressing the whole signal
  • Contractors or partner organisations pushing to a shared cross-org repository that legitimately falls outside the primary corporate GitHub org name

Other platforms for THREAT-CodeRepo-GistExfil


Testing Methodology

Validate this detection against 2 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 1Simulate Exfiltration via Anonymous GitHub Gist

    Expected signal: Sysmon Event ID 1: curl.exe process creation with command line referencing api.github.com/gists and a POST body.

  2. Test 2Simulate Exfiltration via Git Push to Non-Corporate Remote

    Expected signal: Process creation events for git with 'push' in the command line and a remote URL not matching the corporate org.

Unlock playbooks & atomic tests with Pro

Get the full detection package for THREAT-CodeRepo-GistExfil — response playbook and atomic red team tests, plus investigation guidance and hunting queries.

df00tech Pro — £29/user/month

Response PlaybookInvestigation GuideHunting QueriesAtomic Red Team TestsTuning Guidance

Related Detections