Created
April 1, 2026 09:13
-
-
Save Qwizi/f6a83dee93f2b2698d9725c113b407de to your computer and use it in GitHub Desktop.
check_axios.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================================ | |
| # Axios Supply-Chain Compromise Scanner — Windows (PowerShell) | |
| # Date: 2026-03-31 | |
| # Based on: https://gist.github.com/joe-desimone/36061dabd2bc2913705e0d083a9673e7 | |
| # | |
| # Sprawdza: | |
| # 1. Skompromitowane wersje axios (1.14.1, 0.30.4) w lockfiles i node_modules | |
| # 2. Złośliwy pakiet "plain-crypto-js" | |
| # 3. Stage-2 payload IOCs (wt.exe, .vbs, .ps1) | |
| # 4. Aktywne połączenia C2 do sfrclak.com | |
| # 5. Globalne pakiety npm/yarn/pnpm | |
| # 6. Zanieczyszczenie cache npm | |
| # | |
| # Użycie: | |
| # .\Scan-AxiosCompromise.ps1 # skanuje $HOME | |
| # .\Scan-AxiosCompromise.ps1 -ScanRoot C:\Projects # wybrany katalog | |
| # | |
| # Wymagania: PowerShell 5.1+ (lub PowerShell 7+) | |
| # ============================================================================ | |
| param( | |
| [string]$ScanRoot = $env:USERPROFILE | |
| ) | |
| $ErrorActionPreference = "SilentlyContinue" | |
| $script:FoundIssues = 0 | |
| # ── Kolory ────────────────────────────────────────────────────────────────── | |
| function Write-Banner { | |
| Write-Host "" | |
| Write-Host "========================================" -ForegroundColor White | |
| Write-Host " Axios Compromise Scanner (2026-03-31)" -ForegroundColor White | |
| Write-Host "========================================" -ForegroundColor White | |
| Write-Host "" | |
| } | |
| function Write-Section($text) { | |
| Write-Host "" | |
| Write-Host "[*] $text" -ForegroundColor Cyan | |
| Write-Host ("-" * 50) -ForegroundColor Cyan | |
| } | |
| function Write-Found($text) { | |
| Write-Host "[!!!] FOUND: $text" -ForegroundColor Red | |
| $script:FoundIssues++ | |
| } | |
| function Write-Warn($text) { | |
| Write-Host "[!] WARNING: $text" -ForegroundColor Yellow | |
| } | |
| function Write-Safe($text) { | |
| Write-Host "[OK] $text" -ForegroundColor Green | |
| } | |
| function Write-Info($text) { | |
| Write-Host " $text" | |
| } | |
| # ============================================================================ | |
| # 1. FILESYSTEM IOCs — Stage-2 Payloads (Windows) | |
| # ============================================================================ | |
| Write-Banner | |
| Write-Host "Platform: Windows" | |
| Write-Host "Scan root: $ScanRoot" | |
| Write-Host "Date: $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ' -AsUTC)" | |
| Write-Section "Sprawdzanie stage-2 payload IOCs na dysku" | |
| $ProgramData = $env:ProgramData | |
| $TempDir = $env:TEMP | |
| $iocFiles = @( | |
| "$ProgramData\wt.exe", | |
| "$TempDir\6202033.vbs", | |
| "$TempDir\6202033.ps1" | |
| ) | |
| $iocFound = $false | |
| foreach ($f in $iocFiles) { | |
| if (Test-Path $f) { | |
| Write-Found "Windows IOC: $f" | |
| Get-Item $f | Select-Object FullName, Length, LastWriteTime | Format-List | |
| $iocFound = $true | |
| } | |
| } | |
| if (-not $iocFound) { | |
| Write-Safe "Brak stage-2 IOCs (wt.exe, 6202033.vbs, 6202033.ps1)" | |
| } | |
| # ============================================================================ | |
| # 2. SIEĆ — Aktywne połączenia C2 | |
| # ============================================================================ | |
| Write-Section "Sprawdzanie aktywnych połączeń do C2 (sfrclak.com)" | |
| # Rozwiąż IP domeny C2 | |
| $c2Domain = "sfrclak.com" | |
| $c2IPs = @() | |
| try { | |
| $c2IPs = [System.Net.Dns]::GetHostAddresses($c2Domain) | ForEach-Object { $_.IPAddressToString } | |
| if ($c2IPs) { | |
| Write-Info "Domena C2 $c2Domain rozwiązuje się do: $($c2IPs -join ', ')" | |
| Write-Info "Sprawdź logi DNS/firewall pod kątem połączeń do tych IP" | |
| } | |
| } catch { | |
| Write-Info "Nie udało się rozwiązać $c2Domain (może być już zablokowany — dobrze)" | |
| } | |
| # Sprawdź aktywne połączenia TCP | |
| $c2Active = $false | |
| $netConns = Get-NetTCPConnection -State Established, TimeWait, CloseWait 2>$null | |
| foreach ($conn in $netConns) { | |
| $remoteIP = $conn.RemoteAddress | |
| if ($c2IPs -contains $remoteIP) { | |
| Write-Found "Aktywne połączenie do C2 ($c2Domain / $remoteIP)!" | |
| Write-Info " LocalPort=$($conn.LocalPort) RemotePort=$($conn.RemotePort) State=$($conn.State)" | |
| # Spróbuj zidentyfikować proces | |
| $proc = Get-Process -Id $conn.OwningProcess 2>$null | |
| if ($proc) { Write-Info " Proces: $($proc.Name) (PID $($proc.Id))" } | |
| $c2Active = $true | |
| } | |
| } | |
| # Sprawdź też po nazwie hosta w DNS cache | |
| $dnsCache = Get-DnsClientCache 2>$null | Where-Object { $_.Entry -match "sfrclak" } | |
| if ($dnsCache) { | |
| Write-Warn "sfrclak.com znaleziony w lokalnym cache DNS — połączenie mogło mieć miejsce!" | |
| $dnsCache | Format-Table Entry, RecordName, Data -AutoSize | |
| } | |
| if (-not $c2Active) { | |
| Write-Safe "Brak aktywnych połączeń do C2" | |
| } | |
| # ============================================================================ | |
| # 3. GLOBALNE PAKIETY npm / yarn / pnpm | |
| # ============================================================================ | |
| Write-Section "Sprawdzanie globalnie zainstalowanych pakietów npm" | |
| function Test-GlobalPackage($listOutput, $pkgPattern, $manager) { | |
| $matches = $listOutput | Select-String $pkgPattern | |
| if ($matches) { | |
| Write-Found "$pkgPattern znaleziony w globalnych pakietach $manager!" | |
| $matches | ForEach-Object { Write-Info " $_" } | |
| } | |
| } | |
| # npm | |
| if (Get-Command npm -ErrorAction SilentlyContinue) { | |
| Write-Info "Skanowanie globalnych pakietów npm..." | |
| $npmGlobal = npm list -g --depth=0 2>$null | |
| Test-GlobalPackage $npmGlobal "axios@1\.14\.1" "npm" | |
| Test-GlobalPackage $npmGlobal "axios@0\.30\.4" "npm" | |
| Test-GlobalPackage $npmGlobal "plain-crypto-js" "npm" | |
| $npmGlobalDeep = npm list -g --all 2>$null | |
| if ($npmGlobalDeep | Select-String "plain-crypto-js") { | |
| Write-Found "plain-crypto-js jako tranzytywna zależność globalnych pakietów npm!" | |
| } | |
| $npmRoot = (npm root -g 2>$null) | |
| if ($npmRoot -and (Test-Path "$npmRoot\plain-crypto-js")) { | |
| Write-Found "Katalog plain-crypto-js istnieje: $npmRoot\plain-crypto-js" | |
| } | |
| if ($npmRoot -and (Test-Path "$npmRoot\axios\package.json")) { | |
| $axiosVer = node -p "require('$($npmRoot.Replace('\','/'))/axios/package.json').version" 2>$null | |
| if ($axiosVer -in "1.14.1","0.30.4") { | |
| Write-Found "Skompromitowana globalna wersja axios: $axiosVer" | |
| } else { | |
| Write-Safe "Globalna wersja axios: $axiosVer (bezpieczna)" | |
| } | |
| } | |
| } else { | |
| Write-Warn "npm nie znaleziony, pomijam skan globalnych pakietów npm" | |
| } | |
| # yarn | |
| if (Get-Command yarn -ErrorAction SilentlyContinue) { | |
| Write-Info "Skanowanie globalnych pakietów yarn..." | |
| $yarnGlobal = yarn global list 2>$null | |
| Test-GlobalPackage $yarnGlobal "axios@1\.14\.1" "yarn" | |
| Test-GlobalPackage $yarnGlobal "axios@0\.30\.4" "yarn" | |
| Test-GlobalPackage $yarnGlobal "plain-crypto-js" "yarn" | |
| } | |
| # pnpm | |
| if (Get-Command pnpm -ErrorAction SilentlyContinue) { | |
| Write-Info "Skanowanie globalnych pakietów pnpm..." | |
| $pnpmGlobal = pnpm list -g 2>$null | |
| Test-GlobalPackage $pnpmGlobal "axios@1\.14\.1" "pnpm" | |
| Test-GlobalPackage $pnpmGlobal "axios@0\.30\.4" "pnpm" | |
| Test-GlobalPackage $pnpmGlobal "plain-crypto-js" "pnpm" | |
| } | |
| # ============================================================================ | |
| # 4. CACHE npm | |
| # ============================================================================ | |
| Write-Section "Sprawdzanie cache npm pod kątem skompromitowanych pakietów" | |
| if (Get-Command npm -ErrorAction SilentlyContinue) { | |
| $npmCacheDir = (npm config get cache 2>$null) | |
| if ($npmCacheDir -and (Test-Path "$npmCacheDir\_cacache")) { | |
| Write-Info "Skanowanie $npmCacheDir\_cacache ..." | |
| $cacheHits = Get-ChildItem "$npmCacheDir\_cacache" -Recurse -Filter "*.json" -ErrorAction SilentlyContinue | | |
| Select-String "plain-crypto-js" -ErrorAction SilentlyContinue | | |
| Select-Object -First 20 -ExpandProperty Path | |
| if ($cacheHits) { | |
| Write-Warn "plain-crypto-js znaleziony w cache npm (mogło mieć miejsce wcześniej):" | |
| $cacheHits | ForEach-Object { Write-Info " $_" } | |
| Write-Info "Zalecenie: npm cache clean --force" | |
| } else { | |
| Write-Safe "Cache npm czysty (brak plain-crypto-js)" | |
| } | |
| } | |
| } | |
| # ============================================================================ | |
| # 5. LOCKFILES — Skanowanie projektów | |
| # ============================================================================ | |
| Write-Section "Skanowanie lockfiles w $ScanRoot ..." | |
| $lockfileCount = 0 | |
| $lockfileNames = @("package-lock.json","yarn.lock","pnpm-lock.yaml") | |
| $lockfiles = Get-ChildItem -Path $ScanRoot -Recurse -Include $lockfileNames ` | |
| -ErrorAction SilentlyContinue -Depth 8 | | |
| Where-Object { $_.FullName -notmatch "\\node_modules\\" -and $_.FullName -notmatch "\\.git\\" } | |
| foreach ($lf in $lockfiles) { | |
| $hit = $false | |
| $content = Get-Content $lf.FullName -Raw -ErrorAction SilentlyContinue | |
| if ($content -match "axios@1\.14\.1" -or ($content -match '"1\.14\.1"' -and $content -match '"axios"')) { | |
| Write-Found "axios@1.14.1 w $($lf.FullName)" | |
| $hit = $true | |
| } | |
| if ($content -match "axios@0\.30\.4" -or ($content -match '"0\.30\.4"' -and $content -match '"axios"')) { | |
| Write-Found "axios@0.30.4 w $($lf.FullName)" | |
| $hit = $true | |
| } | |
| if ($content -match "plain-crypto-js") { | |
| Write-Found "plain-crypto-js w $($lf.FullName) — TEN PAKIET JEST ZŁOŚLIWY" | |
| $hit = $true | |
| } | |
| if (-not $hit) { $lockfileCount++ } | |
| } | |
| Write-Safe "$lockfileCount lockfiles przeskanowanych bez znalezisk" | |
| # ============================================================================ | |
| # 6. NODE_MODULES — Bezpośrednia inspekcja | |
| # ============================================================================ | |
| Write-Section "Skanowanie katalogów node_modules" | |
| $nmDirs = Get-ChildItem -Path $ScanRoot -Recurse -Filter "node_modules" ` | |
| -Directory -ErrorAction SilentlyContinue -Depth 7 | | |
| Where-Object { $_.FullName -notmatch "\\node_modules\\node_modules" } | |
| $nmScanned = 0 | |
| foreach ($nm in $nmDirs) { | |
| $nmScanned++ | |
| $nmPath = $nm.FullName | |
| # plain-crypto-js | |
| if (Test-Path "$nmPath\plain-crypto-js") { | |
| Write-Found "plain-crypto-js zainstalowany w $nmPath\plain-crypto-js" | |
| if (Test-Path "$nmPath\plain-crypto-js\setup.js") { | |
| Write-Found "setup.js NADAL OBECNY — payload nie został wyczyszczony!" | |
| } | |
| $pcjPkg = "$nmPath\plain-crypto-js\package.json" | |
| if (Test-Path $pcjPkg) { | |
| $pcjContent = Get-Content $pcjPkg -Raw | |
| if ($pcjContent -match "postinstall") { | |
| Write-Found "package.json zawiera hak postinstall (payload nie wykonał się jeszcze?)" | |
| } else { | |
| Write-Warn "package.json BEZ postinstall — prawdopodobnie podmieniony (payload już wykonał się na tym systemie!)" | |
| } | |
| } | |
| } | |
| # axios | |
| $axiosPkg = "$nmPath\axios\package.json" | |
| if (Test-Path $axiosPkg) { | |
| $axiosVer = node -p "try{require('$(($axiosPkg).Replace('\','/'))').version}catch(e){'err'}" 2>$null | |
| if ($axiosVer -in "1.14.1","0.30.4") { | |
| Write-Found "Skompromitowany axios@$axiosVer w $nmPath\axios\" | |
| $axiosContent = Get-Content $axiosPkg -Raw | |
| if ($axiosContent -match "plain-crypto-js") { | |
| Write-Found "Ten axios ma plain-crypto-js jako zależność — POTWIERDZONY KOMPROMIS" | |
| } | |
| } | |
| } | |
| } | |
| Write-Safe "$nmScanned katalogów node_modules przeskanowanych" | |
| # ============================================================================ | |
| # 7. URUCHOMIONE PROCESY | |
| # ============================================================================ | |
| Write-Section "Sprawdzanie uruchomionych procesów" | |
| $suspiciousPatterns = @("com.apple.act.mond","ld.py","6202033","sfrclak","wt") | |
| $procHit = $false | |
| $processes = Get-Process -ErrorAction SilentlyContinue | |
| foreach ($pattern in $suspiciousPatterns) { | |
| $matches = $processes | Where-Object { $_.Name -match $pattern -or $_.Path -match $pattern } | |
| if ($matches) { | |
| foreach ($m in $matches) { | |
| Write-Found "Podejrzany proces pasujący do '$pattern': $($m.Name) (PID $($m.Id)) — $($m.Path)" | |
| $procHit = $true | |
| } | |
| } | |
| } | |
| # Sprawdź też procesy node.js uruchamiające podejrzane skrypty | |
| $nodeProcs = $processes | Where-Object { $_.Name -eq "node" } | |
| foreach ($np in $nodeProcs) { | |
| try { | |
| $cmdLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($np.Id)").CommandLine | |
| if ($cmdLine -match "sfrclak|plain-crypto|6202033") { | |
| Write-Found "Podejrzana linia poleceń node.js: $cmdLine" | |
| $procHit = $true | |
| } | |
| } catch {} | |
| } | |
| if (-not $procHit) { | |
| Write-Safe "Brak podejrzanych procesów" | |
| } | |
| # ============================================================================ | |
| # 8. HISTORIA POWERSHELL (informacyjnie) | |
| # ============================================================================ | |
| Write-Section "Sprawdzanie historii PowerShell (informacyjnie)" | |
| $histFile = (Get-PSReadlineOption 2>$null).HistorySavePath | |
| if (-not $histFile) { | |
| $histFile = "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" | |
| } | |
| if (Test-Path $histFile) { | |
| $recentInstalls = Get-Content $histFile | | |
| Where-Object { $_ -match "npm install|npm i |yarn add|pnpm add|bun add|bun install" } | | |
| Select-Object -Last 10 | |
| if ($recentInstalls) { | |
| Write-Info "Ostatnie komendy instalacji pakietów:" | |
| $recentInstalls | ForEach-Object { Write-Info " $_" } | |
| } | |
| } | |
| # ============================================================================ | |
| # PODSUMOWANIE | |
| # ============================================================================ | |
| Write-Host "" | |
| Write-Host "========================================" -ForegroundColor White | |
| Write-Host " SKAN ZAKOŃCZONY" -ForegroundColor White | |
| Write-Host "========================================" -ForegroundColor White | |
| Write-Host "" | |
| if ($script:FoundIssues -gt 0) { | |
| Write-Host "⚠️ $($script:FoundIssues) PROBLEM(Y) ZNALEZIONY — SYSTEM MOŻE BYĆ SKOMPROMITOWANY" -ForegroundColor Red | |
| Write-Host "" | |
| Write-Host "Natychmiastowe działania:" -ForegroundColor Yellow | |
| Write-Host " 1. Odłącz się od sieci jeśli znaleziono stage-2 IOCs" | |
| Write-Host " 2. Usuń skompromitowany axios: npm uninstall axios && npm install axios@1.14.0" | |
| Write-Host " 3. Usuń plain-crypto-js ze wszystkich node_modules" | |
| Write-Host " 4. Usuń payloady stage-2:" | |
| Write-Host " del `"$env:ProgramData\wt.exe`"" | |
| Write-Host " del `"$env:TEMP\6202033.vbs`"" | |
| Write-Host " del `"$env:TEMP\6202033.ps1`"" | |
| Write-Host " 5. Wyczyść cache npm: npm cache clean --force" | |
| Write-Host " 6. ZMIEŃ WSZYSTKIE DANE DOSTĘPOWE — tokeny, klucze API, hasła, klucze SSH" | |
| Write-Host " 7. Zablokuj sfrclak.com na DNS/firewall" | |
| Write-Host " 8. Sprawdź pipeline CI/CD pod kątem tego samego kompromisu" | |
| } else { | |
| Write-Host "✅ Brak wskaźników kompromisu." -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host "Zalecenia prewencyjne:" | |
| Write-Host " • Przypnij axios do wersji 1.14.0 w lockfiles" | |
| Write-Host " • Uruchom: npm audit" | |
| Write-Host " • Rozważ --ignore-scripts dla niezaufanych instalacji" | |
| Write-Host " • Zablokuj sfrclak.com na DNS/firewall prewencyjnie" | |
| } | |
| Write-Host "" | |
| Write-Host "Skan zakończony: $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ' -AsUTC)" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment