Background

Since late 2025, Lazarus-linked DPRK operators have been routing command-and-control traffic through GitHub repositories and the pCloud cloud storage API. The technique is a variant of T1102.001 (Web Service: Dead Drop Resolver): the implant fetches an initial “dead-drop” from a GitHub gist or repository blob that contains the real C2 IP or domain encoded in a comment or README field, then pivots to communicate with actual infrastructure.

Because outbound HTTPS traffic to github.com, api.github.com, and api.pcloud.com passes through most enterprise proxy allowlists without inspection, network-level detection fires on zero sessions. Traditional perimeter controls are blind. The detection challenge moves entirely into behavioral telemetry on endpoints and cloud egress anomaly analysis.

Fortinet’s threat research team documented this pattern in April 2026 against South Korean targets, and it has since been observed in campaigns targeting financial and defense-adjacent supply chain firms.


How the Chain Works

  1. Initial access: Spearphish or trojanised npm/PyPI package delivers a first-stage loader
  2. Dead-drop resolution: Loader contacts a public GitHub gist or repository file (e.g., raw.githubusercontent.com/[victim-acct]/[repo]/main/config.md) and parses embedded C2 IP from HTML comment or Base64 blob
  3. C2 establishment: Secondary implant beacons to the resolved IP via a custom binary protocol over HTTPS/443 or WebSocket
  4. Exfiltration: Data staged to api.pcloud.com upload endpoints using a stolen or actor-controlled API key
  5. Clean-up: Dead-drop gist is deleted post-delivery; the GitHub account often has a plausible history (star counts, prior commits) to appear legitimate

Sigma Rules

Rule 1 — Process Making Suspicious GitHub Raw Content Requests

title: Suspicious Process Fetching GitHub Raw Content
id: a3f17e82-2b44-4d91-8c59-f2b1e6a04dc3
status: experimental
description: Detects non-browser processes making HTTPS requests to raw.githubusercontent.com,
  which is characteristic of dead-drop resolver C2 patterns used by DPRK-linked actors.
references:
  - https://www.fortinet.com/blog/threat-research/dprk-related-campaigns-with-lnk-and-github-c2
author: SOC Analyst Hub
date: 2026-07-03
tags:
  - attack.command_and_control
  - attack.t1102.001
  - attack.t1105
  - detection.emerging_threat
logsource:
  category: network_connection
  product: windows
detection:
  selection:
    DestinationHostname|contains:
      - 'raw.githubusercontent.com'
      - 'gist.githubusercontent.com'
      - 'api.github.com'
    Initiated: 'true'
  filter_legit_browsers:
    Image|endswith:
      - '\chrome.exe'
      - '\msedge.exe'
      - '\firefox.exe'
      - '\safari.exe'
      - '\opera.exe'
      - '\brave.exe'
      - '\iexplore.exe'
  filter_legit_dev:
    Image|endswith:
      - '\git.exe'
      - '\gh.exe'
      - '\node.exe'
      - '\Code.exe'
      - '\devenv.exe'
      - '\rider64.exe'
      - '\idea64.exe'
  filter_system:
    Image|startswith:
      - 'C:\Windows\System32\'
      - 'C:\Windows\SysWOW64\'
    Image|endswith:
      - '\svchost.exe'
      - '\MicrosoftEdgeUpdate.exe'
  condition: selection and not filter_legit_browsers and not filter_legit_dev and not filter_system
falsepositives:
  - Developer tools, CI/CD agents, package managers (pip, npm, cargo)
  - Security tools pulling threat intel from GitHub
level: medium

Rule 2 — pCloud Upload by Non-User Process

title: Non-Interactive Process Uploading to pCloud API
id: b8c22d13-9e55-4b07-af3c-71d4e0ab7612
status: experimental
description: Detects processes connecting to pCloud upload API endpoints. Legitimate pCloud
  usage is typically via the desktop client, not arbitrary executables. DPRK actors use
  pCloud as an exfiltration staging platform.
references:
  - https://www.fortinet.com/blog/threat-research/dprk-related-campaigns-with-lnk-and-github-c2
author: SOC Analyst Hub
date: 2026-07-03
tags:
  - attack.exfiltration
  - attack.t1567.002
  - attack.command_and_control
  - attack.t1102
logsource:
  category: network_connection
  product: windows
detection:
  selection_host:
    DestinationHostname|contains:
      - 'api.pcloud.com'
      - 'eapi.pcloud.com'
      - 'binapi.pcloud.com'
  selection_method:
    DestinationPort: 443
  filter_pcloud_client:
    Image|endswith:
      - '\pCloud.exe'
      - '\pcloud.exe'
      - '\PCLoud.exe'
  condition: (selection_host and selection_method) and not filter_pcloud_client
falsepositives:
  - Custom pCloud integrations and scripts with business justification
  - Developer test environments
level: high

Rule 3 — Encoded Payload Parsed from Remote File Content

title: Script Process Fetching and Decoding Remote File Content
id: c1d48f96-5a11-4e22-b827-0f93cc8d1a4f
status: experimental
description: Detects PowerShell or script interpreters fetching content from known
  cloud platforms and immediately invoking decoders (Base64, Inflate, XOR), consistent
  with dead-drop resolver resolution.
author: SOC Analyst Hub
date: 2026-07-03
tags:
  - attack.command_and_control
  - attack.t1102.001
  - attack.t1059.001
logsource:
  category: process_creation
  product: windows
detection:
  selection_ps:
    CommandLine|contains|all:
      - 'githubusercontent.com'
      - 'FromBase64String'
  selection_iwr_decode:
    CommandLine|contains|all:
      - 'Invoke-WebRequest'
      - 'githubusercontent.com'
    CommandLine|contains:
      - 'Decompress'
      - 'MemoryStream'
      - 'Load('
  condition: selection_ps or selection_iwr_decode
falsepositives:
  - Legitimate PowerShell deployment scripts fetching from GitHub
level: high

KQL (Microsoft Sentinel / Defender XDR)

Anomalous GitHub Connections by Untrusted Binaries

// Identify processes connecting to GitHub raw content endpoints that aren't expected tooling
let LegitProcesses = dynamic([
    "chrome.exe", "msedge.exe", "firefox.exe", "git.exe", "gh.exe",
    "node.exe", "Code.exe", "devenv.exe", "python.exe", "pip.exe",
    "npm.exe", "cargo.exe", "curl.exe", "wget.exe"
]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where RemoteUrl has_any("raw.githubusercontent.com", "gist.githubusercontent.com")
| extend ProcessName = tostring(split(InitiatingProcessFileName, "\\")[-1])
| where ProcessName !in~ (LegitProcesses)
| where InitiatingProcessFileName !startswith "C:\\Windows\\System32\\"
| summarize
    ConnectionCount = count(),
    RemoteUrls = make_set(RemoteUrl, 20),
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated)
    by DeviceName, InitiatingProcessFileName, ProcessName
| where ConnectionCount < 10  // Low volume typical of dead-drop resolver (one-time fetch)
| order by FirstSeen desc

pCloud Exfiltration Pattern

// Hunt for pCloud API connections from non-pCloud processes with data upload characteristics
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where RemoteUrl has_any("api.pcloud.com", "eapi.pcloud.com", "binapi.pcloud.com")
| where InitiatingProcessFileName !endswith "pCloud.exe"
| project TimeGenerated, DeviceName, InitiatingProcessFileName,
          InitiatingProcessCommandLine, RemoteUrl, RemotePort,
          BytesSent, BytesReceived
| order by BytesSent desc

GitHub to pCloud Hop Sequence (Correlated)

// Detect the full dead-drop pattern: GitHub fetch followed by pCloud upload from same process
let GitHubFetches = DeviceNetworkEvents
| where TimeGenerated > ago(1d)
| where RemoteUrl has_any("raw.githubusercontent.com", "gist.githubusercontent.com")
| project TimeGenerated, DeviceName, ProcessId = InitiatingProcessId,
          InitiatingProcessFileName, GitHubTime = TimeGenerated;
let pCloudUploads = DeviceNetworkEvents
| where TimeGenerated > ago(1d)
| where RemoteUrl has "pcloud.com"
| where BytesSent > 10000
| project DeviceName, ProcessId = InitiatingProcessId,
          pCloudTime = TimeGenerated, BytesSent;
GitHubFetches
| join kind=inner pCloudUploads
    on DeviceName, ProcessId
| where pCloudTime > GitHubTime
| where (pCloudTime - GitHubTime) < 1h
| project DeviceName, InitiatingProcessFileName, GitHubTime, pCloudTime, BytesSent
| order by GitHubTime desc

Hunting Guidance

Network Proxy / DNS Telemetry

Query your DNS or web proxy logs for raw.githubusercontent.com requests, then filter by the process or user agent making the request. Legitimate CI/CD tooling and developer environments will create background noise; focus on:

  • Requests from endpoints that are not developer workstations (servers, KIOSK machines, OT jump hosts)
  • First-time connections to specific gist URLs (a raw gist URL is functionally unique)
  • Short session duration with small response body (typical of config blob retrieval)

Threat Intelligence Enrichment

Cross-reference GitHub usernames in observed raw.githubusercontent.com paths against:

  • VirusTotal URL analysis for the specific path
  • GitHub account creation date (actor accounts often < 30 days old)
  • Number of repositories and star count (legitimacy proxies)

False Positive Reduction

The biggest source of FPs is legitimate DevOps automation. Create a suppression list of known CI/CD service accounts and their expected GitHub org namespaces. Anything accessing content from personal accounts (github.com/<username>/ patterns) deserves scrutiny even from otherwise-allowed processes.


MITRE ATT&CK Mapping

TechniqueIDNotes
Web Service: Dead Drop ResolverT1102.001GitHub gist/repo used for IP resolution
Exfiltration to Cloud StorageT1567.002pCloud as staging platform
Command and Control via Web ServiceT1102Primary C2 channel
Ingress Tool TransferT1105Secondary payload fetch post-resolution
Obfuscated Files or InformationT1027Base64 encoded C2 config in README

References