Atak Supply Chain na Axios: Jak Sprawdzić, Czy Twój System Został Skompromitowany [Windows + Linux]

Zespół clev.one
36 min czytania
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ś:

  1. Uruchom skrypty diagnostyczne na wszystkich maszynach deweloperskich i serwerach CI/CD
  2. Zweryfikuj wersje axios we wszystkich projektach: grep -rE "axios.*1\.14\.1|axios.*0\.30\.4|plain-crypto-js" package-lock.json
  3. Wdróż ignore-scripts i save-exact w konfiguracji npm
  4. Zablokuj domenę sfrclak.com i IP 142.11.206.73 na firewallu/routerze
  5. Przejdź na npm ci w pipeline’ach CI/CD
  6. Rozważ canary tokens w repozytoriach kodu jako warstwę wczesnego ostrzegania
  7. 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.

Tagi: #supply-chain #npm #malware #RAT #Node.js #NIS2 #DevSecOps

Chcesz chronić swoje dane?

Zapisz się na listę oczekujących i otrzymaj 2 miesiące gratis.

Zapisz się teraz