Atak Supply Chain na Axios: Jak Sprawdzić, Czy Twój System Został Skompromitowany [Windows + Linux]
31 marca 2026 roku społeczność JavaScript została skonfrontowana z jednym z najbardziej wyrafinowanych ataków na łańcuch dostaw w historii ekosystemu npm. Axios — biblioteka HTTP używana w ponad 80% środowisk chmurowych i pobierana ponad 100 milionów razy tygodniowo — została zainfekowana trojanem typu RAT (Remote Access Trojan) zdolnym do przejęcia pełnej kontroli nad maszyną dewelopera.
Atak nie wykorzystał żadnej luki w kodzie Axios. Zamiast tego atakujący przejął konto głównego maintainera projektu na platformie npm i opublikował zatrute wersje bezpośrednio w rejestrze, omijając pipeline CI/CD, ochronę gałęzi kodu oraz procesy recenzji. To klasyczny scenariusz ataku na łańcuch dostaw, w którym zaufanie do ekosystemu dystrybucji oprogramowania jest wykorzystywane jako wektor kompromitacji.
W niniejszym artykule przedstawiamy pełną anatomię incydentu, wskaźniki kompromitacji (IOC) oraz — co najważniejsze — gotowe do uruchomienia skrypty diagnostyczne dla systemów Windows 11 i Ubuntu/Linux, pozwalające natychmiastowo zweryfikować, czy Twoje środowisko zostało zainfekowane.
Czym jest Axios i dlaczego ten atak ma znaczenie
Axios to bazująca na obietnicach (promise-based) biblioteka klienta HTTP dla Node.js i przeglądarek. W praktyce jest to narzędzie, które deweloperzy używają do komunikacji aplikacji z serwerami — pobierania danych, wysyłania formularzy, obsługi API. Można ją porównać do instalacji wodociągowej w budynku: użytkownik końcowy nie wie, że jest tam zainstalowana, ale woda (dane) płynie dzięki niej.
Axios jest zależnością w projektach opartych na React, Vue, Angular, Electron, React Native i tysiącach narzędzi SaaS, paneli administracyjnych oraz pipeline’ów CI/CD. Nawet jeśli nigdy nie instalowałeś Axios bezpośrednio, Twoje aplikacje niemal na pewno z niego korzystają pośrednio.
| Metryka | Wartość |
|---|---|
| Pobrania tygodniowe (npm) | ponad 100 milionów |
| Obecność w środowiskach chmurowych | ok. 80% |
| Frameworki wykorzystujące Axios | React, Vue, Angular, Next.js, Nuxt |
| Technologie mobilne/desktopowe | Electron, React Native |
Anatomia ataku: 39 minut, które wstrząsnęły ekosystemem
Atak został przeprowadzony z chirurgiczną precyzją i składał się z kilku etapów rozłożonych na 18 godzin.
Etap 1: Przygotowanie gruntu (T-18h)
30 marca 2026 roku o godzinie 05:57 UTC atakujący opublikował czystą wersję pakietu plain-crypto-js@4.2.0 na npm pod kontem nrwise (email: nrwise@proton.me). Pakiet imitował popularną bibliotekę crypto-js — kopiował jej opis, autora i URL repozytorium. Nie zawierał żadnego złośliwego kodu.
Cel był czysto taktyczny: wstępna publikacja zapewniła, że nazwa pakietu została zaindeksowana i zcache’owana na mirrorach npm i warstwach CDN, zanim pojawiła się uzbrojona wersja.
Etap 2: Przejęcie konta maintainera
Atakujący skompromitował konto npm jasonsaayman — głównego maintainera projektu Axios. Prawdopodobnie uzyskał dostęp do długożywotnego klasycznego tokena npm. Po przejęciu konta zmienił zarejestrowany adres email na ifstap@proton.me — adres pod kontrolą atakującego. To podręcznikowy manewr przejęcia poświadczeń: odcina prawowitego właściciela od mechanizmów odzyskiwania konta, jednocześnie dając atakującemu pełną kontrolę nad przyszłymi ścieżkami weryfikacji i resetowania 2FA.
Kluczowy wniosek dla architektów bezpieczeństwa: rejestr npm traktuje ważny token jako jedyną granicę uwierzytelniania przy publikacji.
Etap 3: Uzbrojenie zależności (T-0h)
23:59 UTC, 30 marca — atakujący opublikował plain-crypto-js@4.2.1, wstrzykując złośliwy ładunek do hooka postinstall w package.json.
Etap 4: Publikacja zatrutych wersji Axios
W ciągu 39 minut atakujący opublikował dwie zatrute wersje Axios:
| Wersja | Czas publikacji (UTC) | Gałąź |
|---|---|---|
| axios@1.14.1 | 00:21, 31 marca | 1.x (główna) |
| axios@0.30.4 | 01:00, 31 marca | 0.x (legacy) |
Jedyna modyfikacja polegała na dodaniu jednej linii do package.json w sekcji dependencies:
"plain-crypto-js": "4.2.1"
Prawdziwy Axios posiada wyłącznie trzy zależności: follow-redirects, form-data i proxy-from-env. Dodanie plain-crypto-js stanowi jednoznaczny dowód manipulacji. Każdy projekt używający zakresu karetki (^1.14.0 lub ^0.30.0) automatycznie pobrałby zainfekowaną wersję przy następnym npm install.
Etap 5: Łańcuch infekcji
Po zainstalowaniu pakietu npm automatycznie uruchamiał hook postinstall, inicjując następujący łańcuch:
npm install axios@1.14.1
└─ instaluje plain-crypto-js@4.2.1
└─ postinstall: node setup.js
└─ _entry("6202033")
├─ Wykrywa system operacyjny (os.platform())
├─ Kontaktuje C2: http://sfrclak[.]com:8000/6202033
└─ Pobiera platformowo-specyficzny RAT
├─ macOS: /Library/Caches/com.apple.act.mond
├─ Windows: %PROGRAMDATA%\wt.exe + łańcuch PowerShell
└─ Linux: /tmp/ld.py via nohup python3
Ciekawy detal: liczby „6202033” odczytane od tyłu dają „3-30-2026” — datę ataku. Ciała żądań POST zawierają ciągi packages.npm.org/product0 (macOS), packages.npm.org/product1 (Windows) i packages.npm.org/product2 (Linux). Prefiks packages.npm.org to technika evasji SIEM — w logach przypomina legitymowy ruch npm, choć domena npm.org w rzeczywistości należy do Narodowego Stowarzyszenia Muzyków Pastoralnych od 1997 roku.
Dropper setup.js wykorzystywał dwuwarstwowy schemat kodowania: odwrócony Base64 z zamianą podkreśleń na znaki dopełnienia, następnie szyfr XOR z kluczem OrDeR_7077 i stałą 333. Wszystkie 18 obfuskowanych ciągów — URL C2, polecenia powłoki, ścieżki plików — były ukryte za tym kodowaniem. To nie minifikacja — to celowo zaprojektowany mechanizm ukrywania wskaźników malware.
Platformowo-specyficzne ładunki RAT
Atakujący przygotował trzy oddzielne warianty trojana. Wszystkie współdzielą identyczny protokół C2 i słownik poleceń, potwierdzając wspólne autorstwo. Wszystkie używają tego samego archaicznego ciągu User-Agent: mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) — Internet Explorer 8 na Windows XP. W 2026 roku jest to wysoce anomalny wskaźnik detekcji.
Windows: łańcuch PowerShell z evasją EDR
Najbardziej wyrafinowany wariant. Dropper lokalizuje PowerShell poleceniem where powershell, kopiuje go do %PROGRAMDATA%\wt.exe — maskując się jako Windows Terminal w celu ominięcia heurystyk EDR. Następnie zapisuje VBScript do %TEMP%\6202033.vbs, uruchamiany przez cscript //nologo w ukrytym oknie. VBScript pobiera ładunek PowerShell (%TEMP%\6202033.ps1) i uruchamia go z flagami -WindowStyle Hidden i -ExecutionPolicy Bypass.
Kluczowy artefakt: plik %PROGRAMDATA%\wt.exe przetrwa reinstalacje i restarty systemu. Firma Huntress zidentyfikowała dodatkowo mechanizm persystencji: klucz rejestru MicrosoftUpdate wskazujący na plik system.bat.
Linux: skrypt Python bez persystencji
RAT w Pythonie (/tmp/ld.py) jest uruchamiany jako osierocony proces przez nohup python3, odłączony od sesji terminala. Nie posiada wbudowanego mechanizmu persystencji — działa tylko do restartu. Przy pierwszym kontakcie wysyła listingi katalogów $HOME, $HOME/.config, $HOME/Documents i $HOME/Desktop. Obsługuje komendy: runscript (wykonanie skryptu), peinject (pobranie binarnego ładunku), dirlist (listowanie katalogów) i killself.
Brak persystencji sugeruje ukierunkowanie na szybką eksfiltrację sekretów z efemeralnych środowisk CI/CD — kontenerów i runnerów, gdzie persystencja jest zbędna.
macOS: binarny RAT maskujący się jako demon Apple
Dropper pobiera 657 KB binarny plik Mach-O do /Library/Caches/com.apple.act.mond — imitując plik cache Apple. Analiza Elastic Security wykazała znaczące podobieństwa z backdoorem WAVESHAPER, przypisywanym północnokoreańskiej grupie UNC1069.
Antyforensyka: malware, który po sobie sprząta
Po dostarczeniu ładunku dropper wykonuje trzy operacje: kasuje setup.js, usuwa złośliwy package.json i przemianowuje czysty package.md z powrotem na package.json. Inspekcja katalogu node_modules/plain-crypto-js/ po infekcji ujawnia całkowicie czysty manifest. Uruchomienie npm audit lub manualna recenzja nie ujawni kompromitacji. Tradycyjne narzędzia skanujące zależności są bezużyteczne po infekcji.
Wskaźniki kompromitacji (IOC)
| Kategoria | Wskaźnik | Opis |
|---|---|---|
| Domena C2 | sfrclak[.]com | Serwer dowodzenia i kontroli |
| IP C2 | 142.11.206.73 | Adres IP serwera C2 |
| Port C2 | 8000 | Port komunikacji |
| URL C2 | http://sfrclak[.]com:8000/6202033 | Pełny URL kampanii |
| User-Agent | mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) | Archaiczny IE8 — anomalny w 2026 |
| Plik Windows | %PROGRAMDATA%\wt.exe | Kopia PowerShell jako Windows Terminal |
| Plik Windows | %TEMP%\6202033.vbs | VBScript launcher (krótkotrwały) |
| Plik Windows | %TEMP%\6202033.ps1 | Ładunek PowerShell RAT (krótkotrwały) |
| Rejestr Win | HKCU...\Run\MicrosoftUpdate | Klucz persystencji -> system.bat |
| Plik Linux | /tmp/ld.py | Python RAT |
| Plik macOS | /Library/Caches/com.apple.act.mond | Binarny RAT jako demon Apple |
| npm: axios | 1.14.1 (SHA: 2553649f…) | Zatruta wersja 1.x |
| npm: axios | 0.30.4 (SHA: d6f3f62f…) | Zatruta wersja 0.x |
| npm | plain-crypto-js@4.2.1 (SHA: 07d889e2…) | Złośliwa zależność-dropper |
Jak sprawdzić, czy Twój system został skompromitowany
Poniżej przedstawiamy gotowe do uruchomienia skrypty diagnostyczne dla systemów Windows 11 i Ubuntu/Linux. Każdy skrypt sprawdza osiem obszarów: artefakty plików RAT, mechanizmy persystencji, podejrzane procesy, połączenia sieciowe do C2, logi systemowe, projekty npm, cache npm oraz zabezpieczenia systemu.
Ważna uwaga: ze względu na mechanizm samoczyszczenia malware, brak wykrytych wskaźników nie oznacza z pewnością braku kompromitacji. Jeśli uruchomiłeś npm install w oknie ekspozycji (00:21–03:29 UTC, 31 marca 2026), potraktuj system jako potencjalnie skompromitowany i zrotuj wszystkie poświadczenia.
Skrypt diagnostyczny: Windows 11 (PowerShell)
Uruchom PowerShell jako Administrator i wykonaj:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; .\Check-AxiosCompromise-Windows.ps1
Pełny kod skryptu:
#Requires -Version 5.1
# ============================================================================
# Axios Supply Chain Attack (31 marca 2026) - Windows Compromise Checker
# ============================================================================
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host " AXIOS SUPPLY CHAIN ATTACK - WINDOWS 11 IOC CHECKER" -ForegroundColor Cyan
Write-Host " Attack Date: March 31, 2026 | C2: sfrclak[.]com" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan
Write-Host ""
$compromised = $false
# --- 1. ARTEFAKTY PLIKOW RAT ---
Write-Host "[1/8] Sprawdzanie artefaktow plikow RAT..." -ForegroundColor Yellow
$fileIOCs = @(
@{ Path = "$env:ProgramData\wt.exe"; Desc = "Kopia PowerShell (evasja EDR)" },
@{ Path = "$env:TEMP\6202033.vbs"; Desc = "VBScript dropper/launcher" },
@{ Path = "$env:TEMP\6202033.ps1"; Desc = "Ladunek PowerShell RAT" },
@{ Path = "$env:TEMP\6202033"; Desc = "Plik tymczasowy staging" }
)
foreach ($ioc in $fileIOCs) {
if (Test-Path $ioc.Path) {
Write-Host " [!] ZNALEZIONO: $($ioc.Path)" -ForegroundColor Red
Write-Host " -> $($ioc.Desc)" -ForegroundColor Red
$compromised = $true
} else {
Write-Host " [OK] Nie znaleziono: $($ioc.Path)" -ForegroundColor Green
}
}
if (Test-Path "$env:ProgramData\wt.exe") {
$wtHash = (Get-FileHash "$env:ProgramData\wt.exe" -Algorithm SHA256).Hash
$psPath = (Get-Command powershell.exe -ErrorAction SilentlyContinue).Source
if ($psPath -and $wtHash -eq (Get-FileHash $psPath -Algorithm SHA256).Hash) {
Write-Host " [!!] POTWIERDZONE: wt.exe to kopia powershell.exe!" -ForegroundColor Red
$compromised = $true
}
}
Write-Host ""
# --- 2. MECHANIZMY PERSYSTENCJI ---
Write-Host "[2/8] Sprawdzanie persystencji..." -ForegroundColor Yellow
$regPaths = @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run"
)
foreach ($regPath in $regPaths) {
if (Test-Path $regPath) {
$entries = Get-ItemProperty $regPath -ErrorAction SilentlyContinue
if ($entries.PSObject.Properties.Name -contains "MicrosoftUpdate") {
Write-Host " [!] KLUCZ PERSYSTENCJI 'MicrosoftUpdate' w $regPath" -ForegroundColor Red
$compromised = $true
}
foreach ($prop in $entries.PSObject.Properties) {
if ($prop.Value -is [string] -and $prop.Value -match "system\.bat|wt\.exe|6202033") {
Write-Host " [!] PODEJRZANY WPIS: '$($prop.Name)' -> $($prop.Value)" -ForegroundColor Red
$compromised = $true
}
}
}
}
Write-Host " [OK] Sprawdzono klucze rejestru Run/RunOnce" -ForegroundColor Green
Write-Host ""
# --- 3. PROCESY ---
Write-Host "[3/8] Sprawdzanie procesow..." -ForegroundColor Yellow
$suspProcs = Get-Process -EA SilentlyContinue | Where-Object {
$_.Path -like "*ProgramData*wt.exe*" -or $_.Path -like "*6202033*"
}
if ($suspProcs) {
foreach ($p in $suspProcs) {
Write-Host " [!] PODEJRZANY PROCES: PID=$($p.Id) $($p.Path)" -ForegroundColor Red
$compromised = $true
}
} else { Write-Host " [OK] Brak podejrzanych procesow" -ForegroundColor Green }
Write-Host ""
# --- 4. SIEC ---
Write-Host "[4/8] Sprawdzanie DNS i polaczen sieciowych..." -ForegroundColor Yellow
try {
$dns = Get-DnsClientCache -EA SilentlyContinue | Where-Object { $_.Entry -match "sfrclak" }
if ($dns) {
Write-Host " [!] DOMENA C2 W CACHE DNS" -ForegroundColor Red
$compromised = $true
} else { Write-Host " [OK] Domena C2 nie w cache DNS" -ForegroundColor Green }
} catch { Write-Host " [~] Wymaga uprawnien administratora" -ForegroundColor DarkYellow }
$c2 = Get-NetTCPConnection -EA SilentlyContinue | Where-Object {
$_.RemoteAddress -eq "142.11.206.73" -or ($_.RemotePort -eq 8000 -and $_.State -eq "Established")
}
if ($c2) {
Write-Host " [!] AKTYWNE POLACZENIE Z C2!" -ForegroundColor Red
$compromised = $true
} else { Write-Host " [OK] Brak polaczen z C2" -ForegroundColor Green }
Write-Host ""
# --- 5. LOGI ZDARZEN ---
Write-Host "[5/8] Sprawdzanie logow zdarzen Windows..." -ForegroundColor Yellow
try {
$logs = Get-WinEvent -FilterHashtable @{
LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104;
StartTime=(Get-Date).AddDays(-3)
} -MaxEvents 500 -EA SilentlyContinue | Where-Object {
$_.Message -match "sfrclak|plain-crypto-js|packages\.npm\.org/product"
}
if ($logs) {
Write-Host " [!] PODEJRZANE BLOKI SKRYPTOW POWERSHELL" -ForegroundColor Red
$compromised = $true
} else { Write-Host " [OK] Brak podejrzanych blokow skryptow" -ForegroundColor Green }
} catch { Write-Host " [~] Wymaga uprawnien administratora" -ForegroundColor DarkYellow }
Write-Host ""
# --- 6. PROJEKTY npm ---
Write-Host "[6/8] Skanowanie projektow npm..." -ForegroundColor Yellow
$found = $false
@("$env:USERPROFILE\Desktop","$env:USERPROFILE\Documents","$env:USERPROFILE\Projects",
"$env:USERPROFILE\repos","$env:USERPROFILE\dev","C:\Projects") | ForEach-Object {
if (Test-Path $_) {
Get-ChildItem $_ -Recurse -Name "package-lock.json" -EA SilentlyContinue -Depth 5 | ForEach-Object {
$fp = Join-Path $_ $_
$c = Get-Content $fp -Raw -EA SilentlyContinue
if ($c -match "axios.*1\.14\.1|axios.*0\.30\.4|plain-crypto-js") {
Write-Host " [!] SKOMPROMITOWANY PAKIET: $fp" -ForegroundColor Red
$script:compromised = $true; $script:found = $true
}
}
}
}
if (-not $found) { Write-Host " [OK] Nie znaleziono skompromitowanych pakietow" -ForegroundColor Green }
Write-Host ""
# --- 7. CACHE npm ---
Write-Host "[7/8] Sprawdzanie cache npm..." -ForegroundColor Yellow
if (Test-Path "$env:APPDATA\npm-cache") {
$hits = Get-ChildItem "$env:APPDATA\npm-cache" -Recurse -EA SilentlyContinue | Where-Object {
$_.Name -match "plain-crypto-js" -or ($_.Name -match "axios" -and $_.FullName -match "1\.14\.1|0\.30\.4")
}
if ($hits) {
Write-Host " [!] SKOMPROMITOWANE PAKIETY W CACHE" -ForegroundColor Red
$compromised = $true
} else { Write-Host " [OK] Cache npm czysty" -ForegroundColor Green }
} else { Write-Host " [~] Katalog cache npm nie znaleziony" -ForegroundColor DarkYellow }
Write-Host ""
# --- 8. WINDOWS DEFENDER ---
Write-Host "[8/8] Sprawdzanie Windows Defender..." -ForegroundColor Yellow
try {
$threats = Get-MpThreatDetection -EA SilentlyContinue | Where-Object {
$_.InitialDetectionTime -gt (Get-Date).AddDays(-7) -and
$_.Resources -match "wt\.exe|6202033|plain-crypto-js|sfrclak"
}
if ($threats) {
Write-Host " [!] DEFENDER ZABLOKOWAL POWIAZANE ZAGROZENIA" -ForegroundColor Red
$compromised = $true
} else { Write-Host " [OK] Brak powiazanych wykryc" -ForegroundColor Green }
} catch { Write-Host " [~] Wymaga uprawnien administratora" -ForegroundColor DarkYellow }
Write-Host ""
Write-Host "============================================================" -ForegroundColor Cyan
if ($compromised) {
Write-Host " [!!!] WYKRYTO WSKAZNIKI KOMPROMITACJI" -ForegroundColor Red
Write-Host " 1. Odlacz od sieci" -ForegroundColor Red
Write-Host " 2. Zrotuj WSZYSTKIE poswiadczenia" -ForegroundColor Red
Write-Host " 3. Usun $env:ProgramData\wt.exe" -ForegroundColor Red
Write-Host " 4. Zablokuj sfrclak.com i 142.11.206.73" -ForegroundColor Red
Write-Host " 5. npm cache clean --force" -ForegroundColor Red
Write-Host " 6. Przypnij axios do 1.14.0 lub 0.30.3" -ForegroundColor Red
} else {
Write-Host " [OK] NIE WYKRYTO WSKAZNIKOW KOMPROMITACJI" -ForegroundColor Green
Write-Host " Uwaga: mechanizm samoczyszczenia RAT usuwa slady." -ForegroundColor Yellow
Write-Host " Jesli uruchamiales npm install 31.03 00:21-03:29 UTC," -ForegroundColor Yellow
Write-Host " zrotuj poswiadczenia profilaktycznie." -ForegroundColor Yellow
}
Write-Host "============================================================" -ForegroundColor Cyan
Skrypt diagnostyczny: Ubuntu / Linux
Uruchom w terminalu:
chmod +x check-axios-ubuntu.sh && sudo ./check-axios-ubuntu.sh
Pełny kod skryptu:
#!/bin/bash
# ============================================================================
# Axios Supply Chain Attack (31 marca 2026) - Linux/Ubuntu Compromise Checker
# ============================================================================
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD_RED='\033[1;31m'; NC='\033[0m'
COMPROMISED=0
echo ""
echo -e "${CYAN}============================================================${NC}"
echo -e "${CYAN} AXIOS SUPPLY CHAIN ATTACK - UBUNTU/LINUX IOC CHECKER${NC}"
echo -e "${CYAN} Attack Date: March 31, 2026 | C2: sfrclak[.]com${NC}"
echo -e "${CYAN}============================================================${NC}"
echo ""
# --- 1. ARTEFAKTY PLIKOW RAT ---
echo -e "${YELLOW}[1/8] Sprawdzanie artefaktow plikow RAT...${NC}"
if [ -f "/tmp/ld.py" ]; then
echo -e " ${RED}[!] ZNALEZIONO: /tmp/ld.py (Python RAT)${NC}"
echo -e " ${RED} -> SHA256: $(sha256sum /tmp/ld.py 2>/dev/null | cut -d' ' -f1)${NC}"
COMPROMISED=1
HASH=$(sha256sum /tmp/ld.py 2>/dev/null | cut -d' ' -f1)
KNOWN="fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf"
if [ "$HASH" = "$KNOWN" ]; then
echo -e " ${BOLD_RED}[!!] POTWIERDZONE: hash pasuje do znanego RAT!${NC}"
fi
else
echo -e " ${GREEN}[OK] Nie znaleziono: /tmp/ld.py${NC}"
fi
if [ -f "/tmp/6202033" ]; then
echo -e " ${RED}[!] ZNALEZIONO: /tmp/6202033 (plik staging)${NC}"
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Nie znaleziono: /tmp/6202033${NC}"
fi
# Ukryte pliki w /tmp (potencjalne ladunki stage-3)
HIDDEN_TMP=$(find /tmp -maxdepth 1 -name ".*" -type f -mtime -3 2>/dev/null)
if [ -n "$HIDDEN_TMP" ]; then
echo -e " ${YELLOW}[~] Ukryte pliki w /tmp (sprawdz recznie):${NC}"
echo "$HIDDEN_TMP" | while read -r f; do
echo -e " ${YELLOW} -> $f${NC}"
done
fi
echo ""
# --- 2. PROCESY ---
echo -e "${YELLOW}[2/8] Sprawdzanie procesow...${NC}"
LDPY=$(ps aux 2>/dev/null | grep -E "python3?.*ld\.py|python3?.*/tmp/ld" | grep -v grep)
if [ -n "$LDPY" ]; then
echo -e " ${RED}[!] AKTYWNY PROCES RAT:${NC}"
echo "$LDPY" | while read -r l; do echo -e " ${RED} -> $l${NC}"; done
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Brak procesow ld.py${NC}"
fi
SUSP=$(ps aux 2>/dev/null | grep -E "sfrclak|6202033|packages\.npm\.org/product" | grep -v grep)
if [ -n "$SUSP" ]; then
echo -e " ${RED}[!] PROCESY Z WSKAZNIKAMI C2:${NC}"
echo "$SUSP" | while read -r l; do echo -e " ${RED} -> $l${NC}"; done
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Brak procesow powiazanych z C2${NC}"
fi
echo ""
# --- 3. SIEC ---
echo -e "${YELLOW}[3/8] Sprawdzanie polaczen sieciowych i DNS...${NC}"
C2=$(ss -tunp 2>/dev/null | grep -E "142\.11\.206\.73|:8000" || \
netstat -tunp 2>/dev/null | grep -E "142\.11\.206\.73|:8000")
if [ -n "$C2" ]; then
echo -e " ${RED}[!] AKTYWNE POLACZENIE Z C2:${NC}"
echo "$C2" | while read -r l; do echo -e " ${RED} -> $l${NC}"; done
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Brak polaczen z C2 (142.11.206.73)${NC}"
fi
DNS=$(getent hosts sfrclak.com 2>/dev/null)
if [ -n "$DNS" ]; then
echo -e " ${RED}[!] DOMENA C2 RESOLWUJE: $DNS${NC}"
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Domena sfrclak.com nie resolwuje${NC}"
fi
echo ""
# --- 4. LOGI SYSTEMOWE ---
echo -e "${YELLOW}[4/8] Sprawdzanie logow systemowych...${NC}"
if command -v journalctl &>/dev/null; then
HITS=$(journalctl --since "3 days ago" --no-pager 2>/dev/null | \
grep -E "sfrclak|142\.11\.206\.73|plain-crypto-js|packages\.npm\.org/product" | \
grep -v "check-axios\|Check-Axios\|COMMAND=.*grep\|COMMAND=.*ls.*ld.py\|COMMAND=.*pgrep" | head -10)
if [ -n "$HITS" ]; then
echo -e " ${RED}[!] WSKAZNIKI ATAKU W LOGACH:${NC}"
echo "$HITS" | while read -r l; do echo -e " ${RED} -> $l${NC}"; done
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Brak wskaznikow ataku w logach (ostatnie 3 dni)${NC}"
fi
fi
for logfile in /var/log/syslog /var/log/auth.log; do
if [ -f "$logfile" ]; then
LOG=$(grep -E "sfrclak|142\.11\.206\.73|6202033" "$logfile" 2>/dev/null | head -5)
if [ -n "$LOG" ]; then
echo -e " ${RED}[!] WSKAZNIKI C2 W $logfile${NC}"
COMPROMISED=1
fi
fi
done
echo ""
# --- 5. PROJEKTY npm ---
echo -e "${YELLOW}[5/8] Skanowanie projektow npm...${NC}"
FOUND_BAD=0
for dir in "$HOME" "/var/www" "/opt" "/srv"; do
if [ -d "$dir" ]; then
LOCKS=$(find "$dir" -maxdepth 6 \
\( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" \) \
-not -path "*/node_modules/*" 2>/dev/null)
while IFS= read -r lf; do
[ -z "$lf" ] && continue
if grep -qE "axios.*1\.14\.1|axios.*0\.30\.4|plain-crypto-js" "$lf" 2>/dev/null; then
echo -e " ${RED}[!] SKOMPROMITOWANY PAKIET: $lf${NC}"
COMPROMISED=1; FOUND_BAD=1
fi
done <<< "$LOCKS"
PC=$(find "$dir" -maxdepth 8 -type d -name "plain-crypto-js" 2>/dev/null)
if [ -n "$PC" ]; then
echo "$PC" | while read -r p; do
echo -e " ${RED}[!] ZLOSLIWA ZALEZNOSC: $p${NC}"
done
COMPROMISED=1; FOUND_BAD=1
fi
fi
done
[ "$FOUND_BAD" -eq 0 ] && echo -e " ${GREEN}[OK] Nie znaleziono skompromitowanych pakietow${NC}"
echo ""
# --- 6. npm GLOBALNY I CACHE ---
echo -e "${YELLOW}[6/8] Sprawdzanie globalnych pakietow npm i cache...${NC}"
if command -v npm &>/dev/null; then
NG=$(npm ls -g --depth=0 2>/dev/null | grep -E "axios@1\.14\.1|axios@0\.30\.4|plain-crypto-js")
if [ -n "$NG" ]; then
echo -e " ${RED}[!] SKOMPROMITOWANY GLOBALNY PAKIET npm${NC}"
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Globalne pakiety npm czyste${NC}"
fi
else
echo -e " ${YELLOW}[~] npm nie zainstalowany${NC}"
fi
echo ""
# --- 7. FIREWALL ---
echo -e "${YELLOW}[7/8] Sprawdzanie firewalla...${NC}"
if command -v iptables &>/dev/null; then
BLK=$(iptables -L -n 2>/dev/null | grep "142\.11\.206\.73")
if [ -n "$BLK" ]; then
echo -e " ${GREEN}[OK] C2 IP zablokowane w iptables${NC}"
else
echo -e " ${YELLOW}[i] C2 IP NIE jest zablokowane${NC}"
echo -e " ${YELLOW} Blokada: sudo iptables -A OUTPUT -d 142.11.206.73 -j DROP${NC}"
fi
fi
if command -v ufw &>/dev/null; then
echo -e " ${YELLOW}[i] Blokada UFW: sudo ufw deny out to 142.11.206.73${NC}"
fi
echo ""
# --- 8. PERSYSTENCJA ---
echo -e "${YELLOW}[8/8] Sprawdzanie mechanizmow persystencji...${NC}"
CRON=$(crontab -l 2>/dev/null | grep -E "sfrclak|ld\.py|6202033|142\.11\.206\.73")
if [ -n "$CRON" ]; then
echo -e " ${RED}[!] PODEJRZANE WPISY CRONTAB${NC}"
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Crontab czysty${NC}"
fi
SYSD=$(grep -rlE "sfrclak|ld\.py|6202033" /etc/systemd/ "$HOME/.config/systemd/" 2>/dev/null)
if [ -n "$SYSD" ]; then
echo -e " ${RED}[!] PODEJRZANE PLIKI USLUG SYSTEMD${NC}"
COMPROMISED=1
else
echo -e " ${GREEN}[OK] Uslugi systemd czyste${NC}"
fi
echo -e " ${YELLOW}[i] Uwaga: linuksowy RAT nie ma wbudowanej persystencji.${NC}"
echo -e " ${YELLOW} Dziala tylko do restartu, ale atakujacy mogl${NC}"
echo -e " ${YELLOW} wdrozyc persystencje przez komendy runscript/peinject.${NC}"
echo ""
# --- WERDYKT ---
echo -e "${CYAN}============================================================${NC}"
if [ "$COMPROMISED" -eq 1 ]; then
echo -e " ${BOLD_RED}[!!!] WYKRYTO WSKAZNIKI KOMPROMITACJI${NC}"
echo ""
echo -e " ${RED}NATYCHMIASTOWE DZIALANIA:${NC}"
echo -e " ${RED}1. Odlacz od sieci: sudo ip link set eth0 down${NC}"
echo -e " ${RED}2. Zabij RAT: pkill -f 'python3.*/tmp/ld.py'${NC}"
echo -e " ${RED}3. Usun RAT: rm -f /tmp/ld.py /tmp/6202033${NC}"
echo -e " ${RED}4. Zrotuj WSZYSTKIE poswiadczenia, klucze API, SSH, tokeny npm${NC}"
echo -e " ${RED}5. Zablokuj C2: sudo iptables -A OUTPUT -d 142.11.206.73 -j DROP${NC}"
echo -e " ${RED}6. Uruchom: npm cache clean --force${NC}"
echo -e " ${RED}7. Przypnij axios do 1.14.0 lub 0.30.3${NC}"
echo -e " ${RED}8. Przebuduj kontenery/VM z czystych obrazow${NC}"
else
echo -e " ${GREEN}[OK] NIE WYKRYTO WSKAZNIKOW KOMPROMITACJI${NC}"
echo ""
echo -e " ${GREEN}System wyglada na czysty wg znanych IOC.${NC}"
echo -e " ${YELLOW}Uwaga: linuksowy RAT samoczynnie sie czysci.${NC}"
echo -e " ${YELLOW}Jesli uruchamiales npm install miedzy 00:21-03:29 UTC 31.03,${NC}"
echo -e " ${YELLOW}zrotuj poswiadczenia profilaktycznie.${NC}"
fi
echo -e "${CYAN}============================================================${NC}"
Natychmiastowa remediacja
Jeśli którykolwiek ze skryptów wykrył wskaźniki kompromitacji — lub jeśli uruchomiłeś npm install w oknie ekspozycji — potraktuj maszynę jako w pełni skompromitowaną. RAT obsługiwał wykonywanie dowolnych poleceń, enumerację plików i wstrzykiwanie binarne. Zakładaj, że atakujący miał pełną kontrolę.
Krok 1: Izolacja. Odłącz maszynę od sieci natychmiast. Na Linux: sudo ip link set eth0 down. Na Windows: wyłącz adapter sieciowy.
Krok 2: Neutralizacja RAT. Na Linux: pkill -f 'python3.*/tmp/ld.py' && rm -f /tmp/ld.py /tmp/6202033. Na Windows: usuń %PROGRAMDATA%\wt.exe i klucz rejestru MicrosoftUpdate ze ścieżki HKCU\Software\Microsoft\Windows\CurrentVersion\Run.
Krok 3: Rotacja poświadczeń. Zrotuj WSZYSTKIE sekrety obecne na skompromitowanej maszynie: klucze SSH, tokeny npm, klucze API (AWS, Azure, GCP), tokeny GitHub/GitLab, poświadczenia baz danych, hasła do kont email. Pierwszą akcją RAT jest eksfiltracja listingów katalogów $HOME, $HOME/.config, $HOME/Documents i $HOME/Desktop — atakujący wie, jakie sekrety posiadasz.
Krok 4: Czyszczenie npm. Uruchom npm cache clean --force. Przypnij axios do bezpiecznej wersji: 1.14.0 (gałąź 1.x) lub 0.30.3 (gałąź 0.x). Usuń plain-crypto-js z node_modules jeśli obecny: npm uninstall plain-crypto-js.
Krok 5: Odbudowa. Przebuduj obrazy Docker, kontenery i środowiska CI/CD od zera z czystymi zależnościami. Nie łataj — odbuduj.
Utwardzenie npm: ochrona przed przyszłymi atakami
Trzy konfiguracje npm, które skutecznie blokowałyby ten konkretny atak:
Wyłączenie skryptów postinstall — dokładny mechanizm wykorzystany w tym ataku:
npm config set ignore-scripts true
Gdy potrzebujesz skryptów dla zaufanej instalacji, uruchom je jawnie: npm ci --ignore-scripts && npm rebuild.
Przypinanie dokładnych wersji — eliminacja automatycznego przesuwania wersji przez zakres karetki (^):
npm config set save-exact true
Przyszłe npm install axios zapisze 1.14.0 zamiast ^1.14.0, więc nie pobierze automatycznie zatrutej 1.14.1.
Użycie npm ci zamiast npm install — ścisłe przestrzeganie lockfile:
npm ci
npm ci respektuje lockfile rygorystycznie i kończy się błędem, jeśli jest rozsynchronizowany — uniemożliwia ciche zainstalowanie nowej złośliwej wersji.
Dodatkowo, dla npm w wersji 11 lub nowszej, dostępna jest opcja minimalnego wieku pakietu: npm config set min-release-age 3 — odrzuca pakiety opublikowane mniej niż 3 dni temu. Uwaga: ta opcja nie jest dostępna w npm 10.x i starszych.
Kontekst regulacyjny: NIS2 i bezpieczeństwo łańcucha dostaw
Atak na Axios stanowi podręcznikowy przykład ryzyka, które dyrektywa NIS2 adresuje w swoich wymogach dotyczących bezpieczeństwa łańcucha dostaw. Nowelizacja ustawy o krajowym systemie cyberbezpieczeństwa nakłada na podmioty kluczowe i ważne obowiązek weryfikacji bezpieczeństwa dostawców — w tym dostawców oprogramowania i komponentów open-source.
Dla polskich firm wnioski są jednoznaczne. Po pierwsze, organizacje korzystające z pipeline’ów CI/CD instalujących zależności npm bez audytu są narażone na dokładnie ten typ ataku. Po drugie, wymóg NIS2 dotyczący mechanizmów wykrywania incydentów (art. 21) obejmuje monitorowanie anomalnych połączeń wychodzących ze środowisk budowania — co w tym przypadku stanowiłoby jedyny sposób wykrycia kompromitacji po fazie autoczyszczenia malware. Po trzecie, 24-godzinny termin na wczesne ostrzeżenie o incydencie do CSIRT wymaga, aby organizacja posiadała zdolność szybkiego ustalenia zakresu kompromitacji — właśnie do tego służą skrypty diagnostyczne przedstawione w tym artykule.
Sprawdź swoją kwalifikację w checkliście NIS2. Oszacuj potencjalne koszty incydentu w kalkulatorze kosztów naruszenia danych.
Wnioski operacyjne
Atak na Axios demonstruje ewolucję zagrożeń dla łańcucha dostaw oprogramowania. Warto podkreślić kluczowe cechy tego incydentu.
Wyrafinowanie operacyjne. Złośliwa zależność została wstępnie opublikowana 18 godzin wcześniej. Trzy osobne ładunki przygotowano dla trzech systemów operacyjnych. Oba kanały dystrybucji (gałęzie 1.x i 0.x) zostały zatruté w ciągu 39 minut. Każdy artefakt został zaprojektowany z funkcją samozniszczenia. To nie był atak oportunistyczny.
Potencjalny aktor państwowy. Analiza Elastic Security wskazuje na znaczące podobieństwa z backdoorem WAVESHAPER, przypisywanym północnokoreańskiej grupie UNC1069. Jeśli atrybucja się potwierdzi, atak na Axios wpisuje się w szerszą kampanię APT wymierzoną w ekosystem deweloperski.
Systemowa słabość npm. Rejestr npm traktuje ważny token publikacyjny jako jedyną granicę zaufania. Brak obowiązkowego MFA na kontach maintainerów popularnych pakietów, brak wymuszenia OIDC/trusted publishing jako domyślnego mechanizmu oraz możliwość publikacji pakietów z pominięciem pipeline’ów CI/CD — to luki strukturalne, które będą eksploatowane ponownie.
Bezużyteczność tradycyjnych skanerów post-factum. Mechanizm samoczyszczenia oznacza, że npm audit, inspekcja node_modules i przegląd package.json po infekcji nie ujawnią kompromitacji. Jedynym skutecznym mechanizmem detekcji jest monitoring behawioralny: anomalne połączenia wychodzące podczas npm install, nieoczekiwane procesy potomne i egzfiltracja danych z środowisk budowania.
Systemy wczesnego wykrywania oparte na technologii decepcji — takie jak canary tokens umieszczone w repozytoriach kodu, plikach .env i katalogach konfiguracyjnych — stanowią dodatkową warstwę obrony wykrywającą moment, w którym atakujący zaczyna eksplorować skompromitowane środowisko.
Twój plan na dziś:
- Uruchom skrypty diagnostyczne na wszystkich maszynach deweloperskich i serwerach CI/CD
- Zweryfikuj wersje axios we wszystkich projektach:
grep -rE "axios.*1\.14\.1|axios.*0\.30\.4|plain-crypto-js" package-lock.json - Wdróż
ignore-scriptsisave-exactw konfiguracji npm - Zablokuj domenę
sfrclak.comi IP142.11.206.73na firewallu/routerze - Przejdź na
npm ciw pipeline’ach CI/CD - Rozważ canary tokens w repozytoriach kodu jako warstwę wczesnego ostrzegania
- Sprawdź checklistę NIS2 — wymogi bezpieczeństwa łańcucha dostaw dotyczą również komponentów open-source
Chcesz zabezpieczyć łańcuch dostaw oprogramowania w Twojej organizacji przed atakami typu supply chain? Zapytaj o testy penetracyjne clev.one — weryfikacja bezpieczeństwa pipeline’ów CI/CD i repozytoriów kodu. Potrzebujesz stałego monitoringu zagrożeń? Sprawdź usługi wywiadowcze clev.one — OSINT, threat intelligence i ochrona infrastruktury dla polskich firm.
Chcesz chronić swoje dane?
Zapisz się na listę oczekujących i otrzymaj 2 miesiące gratis.
Zapisz się teraz