Stručne: Zahmlený kód PHP je kód, ktorý bol zámerne urobený nečitateľným, zvyčajne kódovaním a kompresiou, ktoré sa rozlúštia až za behu. Typické vzorce na rozpoznanie sú eval spolu s base64_decode, gzinflate alebo str_rot13, veľmi dlhé riadky, hexadecimálne reťazce a názvy funkcií skladané z jednotlivých častí. Hľadajte neškodnými príkazmi grep, kód nikdy nespúšťajte a nálezy porovnajte s pôvodným balíkom.
Prečo útočníci svoj kód ukrývajú
Kto uloží na server webshell alebo zadné vrátka, chce, aby ostali dlho neodhalené. Čitateľný kód by bol nápadný každému adminovi, ktorý súbor otvorí, a každý skener by ho našiel. Útočníci preto svoj kód balia: kóduje sa, komprimuje a až pri zavolaní sa opäť poskladá a spustí.
Pre PHP2Shell neexistujú potvrdené indikátory kompromitácie (Indicators of Compromise). Zahmlený kód nie je znakom tejto zraniteľnosti, ale všeobecným znakom toho, že niekto votrel kód, bez ohľadu na cestu. Ako webshelly nájsť a odstrániť ako celok, opisuje Rozpoznanie a odstránenie webshellu. Tento článok sa sústreďuje na otázku, podľa čoho zahmlený kód spoznáte.
eval, base64 a spol.: typické stavebné prvky
Zahmlenie sa takmer vždy riadi rovnakým princípom: nečitateľný blok dát sa jednou alebo viacerými funkciami premení späť na kód, ktorý sa potom spustí. Nasledujúce stavebné prvky sa pritom stále objavujú.
| Stavebný prvok | Príklady | Prečo upúta |
|---|---|---|
| Vykonanie | eval, assert s reťazcom (do PHP 7.x), create_function (do PHP 7.x) |
vykoná text ako kód |
| Dekódovanie | base64_decode, str_rot13, hex2bin, strrev, convert_uudecode |
robí dáta opäť čitateľnými |
| Dekompresia | gzinflate, gzuncompress, gzdecode |
rozbalí vložený kód |
| Skladanie | reťazce chr(), spájanie krátkych čiastkových reťazcov, úniky \x |
skrýva názvy funkcií |
| Dynamické volanie | premenné ako názov funkcie, call_user_func, array_map s reťazcom |
volanie sa nikde neobjaví v čistom texte |
| Prístup k systému | system, exec, shell_exec, passthru, proc_open, popen |
príkazy na serveri |
Podľa príručky PHP assert() od PHP 8.0 už nevyhodnocuje reťazce ako kód a create_function() bola v PHP 8.0 odstránená. Na serveroch so staršími verziami PHP ostávajú oba relevantnými hľadanými pojmami.
Jednotlivý stavebný prvok je zriedka podozrivý, base64_decode používa legitímne mnoho aplikácií. Podozrivou sa stáva kombinácia: dekódovanie, ktorého výsledok ide priamo do vykonania.
Hľadať bezpečne: neškodné príkazy grep
Nasledujúce príkazy prehľadávajú súbory len ako text. Nespúšťajú žiadny kód PHP. Prispôsobte cestu.
# Vykonanie v kombinácii s dekódovaním alebo dekompresiou
grep -rlE "(eval|assert)\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin)" /var/www --include="*.php*"
# Veľmi dlhé riadky (často zabalený kód)
grep -rlE ".{2000,}" /var/www --include="*.php*"
# Veľa hexadecimálnych únikov alebo reťazcov chr() v jednom riadku
grep -rlE "(\\\\x[0-9a-fA-F]{2}){20,}" /var/www --include="*.php*"
grep -rlE "(chr\s*\(\s*[0-9]+\s*\)\s*\.\s*){8,}" /var/www --include="*.php*"
# Volanie funkcie cez premennú hneď po priradení z reťazcov
grep -rnE "\\\$[a-zA-Z_]+\s*=\s*['\"][a-z_]{4,}['\"]\s*;\s*\\\$[a-zA-Z_]+\s*\(" /var/www --include="*.php*" | head -50
Zásahy najprv skontrolujte pomocou ls -l a časovej pečiatky a potom ich otvorte len ako text, napríklad pomocou less. Podozrivé súbory nikdy nevolajte cez prehliadač, pretože by ich server vykonal.
Kde sa zahmlený kód typicky nachádza
Zahmlený kód zriedka stojí vo vlastnom nápadnom súbore. Častejšie sa vkladá do existujúcich súborov, celkom na začiatok alebo celkom na koniec, často za mnoho prázdnych riadkov, aby pri otvorení hneď nepadol do oka. Obľúbené sú súbory, ktoré sa načítavajú pri každom volaní: centrálny index.php, konfiguračné súbory a funkčné súbory témy.
Mimoriadne nenápadná cesta vedie cez konfiguráciu. Nastavením auto_prepend_file načíta PHP pred každým skriptom ďalší súbor. Ak je v .user.ini alebo .htaccess, ktorý vytvoril útočník, beží jeho kód pri každom zobrazení stránky, bez toho, aby sa zmenil jediný súbor aplikácie. Vložený súbor pritom rád nesie neškodnú príponu ako .ico alebo .png.
# Načítava sa pred alebo po každom skripte ďalší kód?
grep -rnE "auto_(prepend|append)_file" /var/www --include=".user.ini" --include=".htaccess" --include="*.conf" 2>/dev/null
# Kód PHP v súboroch s príponou obrázka alebo ikony
grep -rlE "<\?php" /var/www --include="*.ico" --include="*.png" --include="*.jpg" --include="*.gif" 2>/dev/null
Legitímne zahmlenie odlíšiť od škodlivého kódu
Nie každý nečitateľný kód je zlomyseľný. Často sa vyskytujú tri prípady:
- Komerčné enkódery: Niektorí výrobcovia pluginov a softvéru chránia svoj kód nástrojmi ako ionCube alebo SourceGuardian. Súbory sú nečitateľné, no typicky odkazujú na potrebný loader. Porovnajte ich s pôvodným balíkom.
- Minifikovaný alebo generovaný kód: Cache a šablónovacie nástroje vytvárajú dlhé riadky bez zalomení, avšak bez vykonávania dekódovaného textu.
- Vložené dáta: Obrázky alebo písma ako reťazec Base64 v súbore PHP sú neobvyklé, ale neškodné, kým sa výsledok nevykonáva.
Najspoľahlivejším testom je porovnanie so zdrojom: ak súbor pochádza nezmenený z balíka výrobcu, je legitímny. Ak sa nenachádza v žiadnom pôvodnom balíku alebo sa od neho odlišuje, patrí na vyšetrenie.
Nepokúšajte sa zahmlený kód rozlúštiť na produkčnom serveri, napríklad nahradením eval výpisom a zavolaním súboru. Už jedna chyba pritom spustí škodlivý kód. Analýza patrí do izolovaného prostredia.
Hranice vyhľadávania podľa vzorcov
Útočníci poznajú bežné hľadacie príkazy. Rozdeľujú názvy funkcií, skladajú ich za behu alebo skutočný kód vyčleňujú do databázy, do súborov obrázkov alebo do konfiguračných záznamov. Vyhľadávanie podľa vzorcov preto nájde veľa, ale nie všetko. Prečo narážajú na hranice aj skenery, vysvetľuje Skenery malvéru pre weby: čo nájdu a čo nie.
Nález je navyše zriedka jediný. Útočníci používajú vo viacerých súboroch často ten istý stavebný prvok, napríklad nápadné označenie premennej alebo výrazný reťazec znakov na začiatku kódovaného bloku. Po potvrdenom náleze preto cielene hľadajte presne tento reťazec v celom webovom adresári, aj v iných weboch na tom istom serveri. Tak nájdete súrodencov, ktorých nezachytilo žiadne všeobecné vyhľadávanie podľa vzorcov.
Vyhľadávanie doplňte o porovnanie s pôvodnými súbormi a o pohľad do databázy, pozri Kontrola databázy na votrelecký kód. Preventívne sa dajú zablokovať nepotrebné systémové funkcie, ako opisuje sprievodca Správne používanie disable_functions.
Ako konkrétne pomáha PHP SOS?
- Hľadať: Prehľadáme súbory a databázu rozšírenými vzorcami a porovnáme s pôvodnými balíkmi.
- Analyzovať: Podozrivý kód skúmame v izolovanom prostredí bez ohrozenia vášho systému.
- Vyčistiť: Prostredníctvom odstránenia malvéru odstránime všetky súčasti a uzavrieme cestu vstupu.
- Zabezpečiť: Obmedzíme funkcie PHP a práva zápisu a sledujeme nové alebo zmenené súbory.
Časté otázky
Je eval s base64_decode vždy škodlivý kód?
Nie vždy, ale veľmi často. Legitímny kód túto kombináciu takmer nepotrebuje. V súboroch, ktoré nepatria k vašej aplikácii ani k známemu pluginu, je to silný varovný signál.
Smiem zahmlený kód dekódovať na čítanie?
Len bez jeho spustenia a nie na produkčnom serveri. Už náhodné spustenie môže spôsobiť škodu. Analýzu nechajte v pochybnostiach odborníkom, ktorí pracujú s izolovanými prostrediami.
Prečo môj skener malvéru kód nenájde?
Skenery hľadajú známe vzorce. Útočníci svoj kód neustále menia, rozdeľujú názvy funkcií alebo ich skladajú za behu. Prázdny sken preto nie je dôkazom čistého servera.
Čo komerčne šifrované pluginy?
Niektorí výrobcovia chránia svoj kód enkódermi ako ionCube alebo SourceGuardian. Takéto súbory sú nečitateľné, ale legitímne, ak pochádzajú od výrobcu. Porovnajte ich s pôvodným balíkom.
Pomáha disable_functions proti zahmlenému kódu?
Čiastočne. Funkcie ako exec alebo system sa dajú zablokovať, eval však nie, pretože ide o jazykový konštrukt, nie o funkciu. Zablokovanie sťažuje útočníkom ďalší krok, no nezabráni každému škodlivému kódu.