Threat Summary
AlienVault OTX pulse data confirms an active supply chain intrusion campaign tracked as PolinRider, targeting the software development ecosystem itself as an infiltration vector. Operators compromised a legitimate GitHub account and used it to inject malicious code into development builds of visanduma/nova-two-factor — a Laravel Nova two-factor authentication package on Packagist with over 700,000 downloads. The choice of target is deliberate and dangerous: a security-focused package that sits directly in authentication flows, giving the attacker a trusted foothold inside application login pipelines.
The attack chain observed across this campaign follows a consistent pattern:
- Account compromise — developer/maintainer credentials are harvested (likely via prior infostealer infections, phishing, or credential stuffing against GitHub).
- Git history rewriting — malicious commits are injected and history is manipulated (
git push --force, rebasing, amended commits) to conceal the tampering from casual repository review. - Payload staging in package registries — poisoned development versions are published to Packagist and, per the campaign's broader footprint, npm, PyPI, Go modules, and Chrome extension channels.
- Downstream execution — CI/CD pipelines and developer workstations pull the compromised dependency, executing the infostealer payload during install/build hooks.
- Credential harvesting — browser credential stores, session tokens, SSH keys,
.envfiles, and cloud provider credentials are staged and exfiltrated to actor-controlled infrastructure.
The objective is clear: harvest developer and enterprise credentials at scale, then monetize or pivot — stolen maintainer credentials feed the next round of package poisoning, creating a self-propagating supply chain worm effect across ecosystems.
Threat Actor / Malware Profile
Actor: PolinRider (infostealer operator collective, multi-ecosystem supply chain focus) Family: PolinRider infostealer Targeted sector: Technology — specifically developer workstations, CI/CD runners, and organizations consuming open-source PHP dependencies
Distribution method: Compromised maintainer accounts on GitHub used to push backdoored releases to Packagist. Campaign infrastructure extends to npm, PyPI, Go modules, and Chrome Web Store extensions, indicating a mature multi-registry operation rather than a one-off compromise.
Payload behavior: The malicious code rides inside the package's development branch and executes during Composer install/update hooks or at application runtime. Primary objectives on the endpoint:
- Enumeration and extraction of browser credential databases (Chrome/Edge/Firefox Login Data, cookies, autofill)
- Theft of SSH private keys (
~/.ssh/), Git credentials (~/.git-credentials, credential helper stores), and cloud CLI tokens (~/.aws/,~/.azure/, gcloud config) - Harvesting of
.envfiles and project-level secrets — directly weaponized for further supply chain compromise - Session token theft enabling MFA bypass against GitHub, npm, and Packagist maintainer accounts
C2 communication: HTTPS-based exfiltration to rotating actor infrastructure, typically disguised as telemetry or analytics beacons. Exfiltration commonly occurs via POST requests with compressed/encrypted archives shortly after payload execution.
Persistence: On developer endpoints, persistence is secondary to speed — the payload prioritizes rapid credential theft. However, dropped artifacts may register scheduled tasks or modify shell profiles to re-execute on login if the host is deemed high-value (e.g., a maintainer's machine).
Anti-analysis: Git history rewriting to erase malicious commits from visible history, payload obfuscation within legitimate-looking package code, and execution gated behind install hooks that blend into normal Composer dependency resolution noise.
IOC Analysis
This pulse contains 5 FileHash-SHA256 indicators — file artifacts associated with the PolinRider malicious package payloads:
7d47c430e6e404dc2fa8b4837678d1cbdb4d0aeacec9b405655cab79d54a2ad9b7ede935d4979146b55f12b9eec7c83b61962b478f5dc9b8db251e539ec2abd3ccb187dc9de0cc7477c9817ae53365d273e121407c0305f863e2ab67c35d6395139ea03dcddf4aa810d55740be3cf6c92ce7a9f3cbcbbb35440e25b769a87683515a53291d25d229e1f9fa72e66407e1cfd7e77c91478400b24d5185af68531a
Operationalization guidance for SOC teams:
- EDR hash blocking — push all five SHA256 values into your EDR blocklist (CrowdStrike, Defender for Endpoint, SentinelOne). Hash indicators are high-fidelity here because poisoned package payloads are identical across all victims who pulled the compromised version.
- Build artifact scanning — sweep
vendor/directories (Composer), build caches, and CI runner workspaces for files matching these hashes. Tools:Get-FileHash(PowerShell),sha256sum, or enterprise file integrity monitoring. - Dependency audit — run
composer show visanduma/nova-two-factoracross codebases and container images to identify deployments on the compromised dev branch. Cross-reference lock file commit hashes against known-good releases. - Git forensics — audit repositories for force-push events, rewritten history, and commits from maintainer accounts at unusual hours or from unfamiliar IPs. GitHub audit log and
git reflogare your primary sources. - Detonation — submit hashes to any sandbox for behavioral extraction of C2 endpoints if your team needs network IOCs; the OTX pulse reference (socket.dev analysis) provides additional payload context.
Detection Engineering
---
title: Suspicious Composer/Packagist Install Hook Spawning Credential Access
id: 9f2c1a7e-polin-0001-0001-000000000001
status: experimental
description: Detects Composer/PHP package install hooks spawning child processes that access browser credential stores, SSH keys, or cloud credential files — behavior consistent with PolinRider poisoned package execution.
author: Security Arsenal Threat Intel
references:
- https://socket.dev/blog/polinrider-github-packagist
date: 2026/10/18
logsource:
category: process_creation
product: windows
definition: windows
detection_placeholder: null
detection:
selection_parent:
ParentImage|endswith:
- '\php.exe'
- '\composer.phar'
- '\composer.exe'
selection_target:
CommandLine|contains:
- 'Login Data'
- 'Cookies'
- '.ssh'
- 'id_rsa'
- 'id_ed25519'
- '.git-credentials'
- '.aws\credentials'
- '.azure'
- '.env'
condition: selection_parent and selection_target
falsepositives:
- Legitimate PHP deployment scripts reading environment configuration
level: high
tags:
- attack.t1555
- attack.t1552.001
- attack.t1195.002
---
title: Git Force Push or History Rewrite on Protected Repository
id: 9f2c1a7e-polin-0002-0002-000000000002
status: experimental
description: Detects git history rewriting operations (force push, reset --hard followed by push) commonly used by PolinRider operators to conceal malicious commits after compromising maintainer accounts.
author: Security Arsenal Threat Intel
date: 2026/10/18
logsource:
category: process_creation
product: windows
definition: linux
detection_placeholder: null
detection:
selection_img:
Image|endswith:
- '\git.exe'
- '/git'
selection_cmd:
CommandLine|contains:
- 'push --force'
- 'push -f'
- 'reset --hard'
- 'commit --amend'
- 'filter-branch'
- 'rebase'
condition: selection_img and selection_cmd
falsepositives:
- Developer workflow on feature branches; correlate with pushes to main/release branches via git server audit logs
level: medium
tags:
- attack.t1195.002
- attack.t1078
---
title: Infostealer Browser Credential Store Access
id: 9f2c1a7e-polin-0003-0003-000000000003
status: experimental
description: Detects non-browser processes accessing Chromium/Firefox credential databases, consistent with PolinRider infostealer payload execution on developer workstations.
author: Security Arsenal Threat Intel
date: 2026/10/18
logsource:
category: file_event
product: windows
definition: sysmon
detection_placeholder: null
detection:
selection_file:
TargetFilename|contains:
- '\Google\Chrome\User Data\'
- '\Microsoft\Edge\User Data\'
- '\BraveSoftware\'
- '\Mozilla\Firefox\Profiles\'
TargetFilename|endswith:
- 'Login Data'
- 'Cookies'
- 'Web Data'
- 'logins.json'
- 'key4.db'
filter_browser:
Image|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\firefox.exe'
- '\brave.exe'
condition: selection_file and not filter_browser
falsepositives:
- Backup software, enterprise password managers, forensic tooling
level: high
tags:
- attack.t1555.003
- attack.t1539
// PolinRider supply chain hunt — poisoned package execution + credential theft + known payload hashes
// Microsoft Sentinel / Defender XDR
let PolinRiderHashes = dynamic([
"7d47c430e6e404dc2fa8b4837678d1cbdb4d0aeacec9b405655cab79d54a2ad9",
"b7ede935d4979146b55f12b9eec7c83b61962b478f5dc9b8db251e539ec2abd3",
"ccb187dc9de0cc7477c9817ae53365d273e121407c0305f863e2ab67c35d6395",
"139ea03dcddf4aa810d55740be3cf6c92ce7a9f3cbcbbb35440e25b769a87683",
"515a53291d25d229e1f9fa72e66407e1cfd7e77c91478400b24d5185af68531a"
]);
// Leg 1: file hits for known PolinRider payload hashes
let HashHits = DeviceFileEvents
| where TimeGenerated > ago(14d)
| where SHA256 in (PolinRiderHashes)
| project HashHitTime=TimeGenerated, DeviceName, DeviceId, FileName, FolderPath, SHA256, InitiatingProcessFileName;
// Leg 2: package manager install hooks spawning credential-accessing children
let SuspiciousInstallExec = DeviceProcessEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ ("php.exe", "composer.exe", "composer.phar", "node.exe", "npm.cmd", "pip.exe", "python.exe")
| where ProcessCommandLine has_any ("Login Data", "Cookies", ".ssh", "id_rsa", "id_ed25519", ".git-credentials", ".aws", ".azure", ".env")
| project InstallExecTime=TimeGenerated, DeviceName, DeviceId, FileName, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessCommandLine;
// Leg 3: git history rewriting on developer endpoints
let GitRewrite = DeviceProcessEvents
| where TimeGenerated > ago(14d)
| where FileName =~ "git.exe"
| where ProcessCommandLine has_any ("push --force", "push -f", "filter-branch", "reset --hard", "commit --amend")
| project GitTime=TimeGenerated, DeviceName, DeviceId, ProcessCommandLine, AccountName;
union HashHits, SuspiciousInstallExec, GitRewrite
| summarize by DeviceName, DeviceId
| join kind=leftouter (HashHits) on DeviceId
| join kind=leftouter (SuspiciousInstallExec) on DeviceId
| join kind=leftouter (GitRewrite) on DeviceId
| project DeviceName, HashHitTime, FolderPath, SHA256, InstallExecTime, ProcessCommandLine, GitTime
| sort by HashHitTime desc
# PolinRider Supply Chain IOC Hunt — Security Arsenal
# Sweep developer workstations & build servers for poisoned package artifacts and credential-theft staging
$ErrorActionPreference = 'SilentlyContinue'
$PolinRiderHashes = @(
'7d47c430e6e404dc2fa8b4837678d1cbdb4d0aeacec9b405655cab79d54a2ad9',
'b7ede935d4979146b55f12b9eec7c83b61962b478f5dc9b8db251e539ec2abd3',
'ccb187dc9de0cc7477c9817ae53365d273e121407c0305f863e2ab67c35d6395',
'139ea03dcddf4aa810d55740be3cf6c92ce7a9f3cbcbbb35440e25b769a87683',
'515a53291d25d229e1f9fa72e66407e1cfd7e77c91478400b24d5185af68531a'
)
Write-Host '[*] === PolinRider IOC Hunt Started ===' -ForegroundColor Cyan
# 1. Scan Composer vendor dirs and build caches for known-bad hashes
$ScanRoots = @("$env:USERPROFILE\vendor", "$env:USERPROFILE\source", "$env:USERPROFILE\Projects", 'C:\builds', 'C:\workspace')
foreach ($root in $ScanRoots) {
if (Test-Path $root) {
Write-Host "[*] Hashing files under $root ..."
Get-ChildItem -Path $root -Recurse -File | ForEach-Object {
$h = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash.ToLower()
if ($PolinRiderHashes -contains $h) {
Write-Host "[!] POLINRIDER PAYLOAD MATCH: $($_.FullName) [$h]" -ForegroundColor Red
}
}
}
}
# 2. Identify installs of the compromised package
Write-Host '[*] Checking composer.lock files for visanduma/nova-two-factor ...'
Get-ChildItem -Path $env:USERPROFILE -Recurse -Filter 'composer.lock' | ForEach-Object {
$content = Get-Content $_.FullName -Raw
if ($content -match 'visanduma/nova-two-factor') {
Write-Host "[WARNING] Compromised package present in: $($_.FullName)" -ForegroundColor Yellow
}
}
# 3. Check for git history rewriting artifacts in reflogs
Write-Host '[*] Auditing local git reflogs for force-push / history rewrite traces ...'
Get-ChildItem -Path $env:USERPROFILE -Recurse -Directory -Filter '.git' | ForEach-Object {
$repo = $_.Parent.FullName
Push-Location $repo
$reflog = git reflog --all 2>$null | Select-String -Pattern 'reset|rebase|amend|filter-branch'
if ($reflog) { Write-Host "[WARNING] History rewrite activity in $repo" -ForegroundColor Yellow; $reflog | ForEach-Object { Write-Host " $_" } }
Pop-Location
}
# 4. Hunt persistence artifacts (scheduled tasks spawned near install windows)
Write-Host '[*] Reviewing non-Microsoft scheduled tasks ...'
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike '\Microsoft*' } | ForEach-Object {
$action = $_.Actions[0].Execute
if ($action -match 'php|composer|powershell|cmd|wscript|node') {
Write-Host "[WARNING] Suspicious task: $($_.TaskName) -> $action" -ForegroundColor Yellow
}
}
# 5. Flag recent outbound connections from PHP/Composer processes (exfil check)
Write-Host '[*] Checking network connections from php/composer/node processes ...'
Get-NetTCPConnection -State Established | ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess
if ($proc.Name -match 'php|composer|node') {
Write-Host "[WARNING] $($proc.Name) (PID $($_.OwningProcess)) -> $($_.RemoteAddress):$($_.RemotePort)" -ForegroundColor Yellow
}
}
Write-Host '[*] === Hunt Complete ===' -ForegroundColor Cyan
Response Priorities
Immediate (0-4 hours):
- Block all five SHA256 indicators in EDR and email/web gateways; sweep endpoints, build servers, and artifact repositories for matches.
- Inventory every codebase, container image, and CI pipeline consuming
visanduma/nova-two-factor— pin to a known-good release or remove pending validation. - Freeze pulls of development (
dev-*) branches of the affected package across all projects. - Audit GitHub organization audit logs for force-pushes, unexpected maintainer logins, new deploy keys, and OAuth app authorizations.
24 hours:
- Treat any host that executed the poisoned package as fully credential-compromised: rotate browser-stored passwords, SSH keypairs, cloud CLI tokens, GitHub PATs, npm/Packagist tokens, and any secrets present in
.envfiles on the affected machine. - Enforce phishing-resistant MFA (hardware keys) re-enrollment for all maintainer accounts; revoke all active sessions on GitHub, Packagist, npm, and PyPI.
- Review authentication logs for the two-factor package's downstream applications — the malicious code sat inside an auth component and may have intercepted credentials in transit.
- Hunt for downstream lateral movement using stolen developer credentials (VPN, SSO, CI systems).
1 week:
- Implement dependency pinning with signed commits and lock-file integrity verification in CI/CD; block unsigned or history-rewritten commits from merging to release branches.
- Deploy a software composition analysis (SCA) gate that alerts on maintainer account changes, sudden version bumps from new committers, and packages with rewritten Git history.
- Require Sigstore/cosign or equivalent artifact signing for internally built packages; enable GitHub's required signed commits for critical repos.
- Conduct tabletop exercise: maintainer account takeover → package poisoning scenario, and update IR playbooks accordingly.
Related Resources
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.