Na kratko: Zakrita koda PHP je koda, ki je bila namenoma narejena neberljiva, večinoma s kodiranjem in stiskanjem, ki se razreši šele med izvajanjem. Tipični vzorci za prepoznavanje so eval skupaj z base64_decode, gzinflate ali str_rot13, zelo dolge vrstice, šestnajstiški nizi in imena funkcij, sestavljena iz delov. Iščite z neškodljivimi ukazi grep, kode nikoli ne izvajajte in najdbe primerjajte z izvirnim paketom.
Zakaj napadalci svojo kodo skrivajo
Kdor na strežnik odloži webshell ali stranska vrata, želi, da dolgo ostanejo neodkrita. Berljiva koda bi padla v oči vsakemu skrbniku, ki datoteko odpre, in vsak pregledovalnik bi jo našel. Zato napadalci kodo pakirajo: kodirana je, stisnjena in šele ob priklicu znova sestavljena in izvedena.
Za PHP2Shell ni potrjenih indikatorjev ogroženosti (Indicators of Compromise). Zakrita koda ni značilnost te ranljivosti, ampak splošen znak, da je nekdo vnesel kodo, ne glede na pot. Kako webshelle v celoti najdete in odstranite, opisuje Prepoznavanje in odstranjevanje webshellov. Ta prispevek se osredotoča na vprašanje, po čem zakrito kodo prepoznate.
eval, base64 in drugo: tipični gradniki
Zakritje skoraj vedno sledi istemu načelu: neberljiv podatkovni blok ena ali več funkcij spremeni nazaj v kodo, ki se potem izvede. Naslednji gradniki se pri tem pojavljajo vedno znova.
| Gradnik | Primeri | Zakaj je opazen |
|---|---|---|
| Izvajanje | eval, assert z nizom (do PHP 7.x), create_function (do PHP 7.x) |
besedilo izvede kot kodo |
| Dekodiranje | base64_decode, str_rot13, hex2bin, strrev, convert_uudecode |
podatke naredi spet berljive |
| Razširjanje | gzinflate, gzuncompress, gzdecode |
razpakira vdelano kodo |
| Sestavljanje | verige chr(), združevanje kratkih delov, ubežne sekvence \x |
skrije imena funkcij |
| Dinamičen klic | spremenljivke kot ime funkcije, call_user_func, array_map z nizom |
klic nikjer ni v jasnem besedilu |
| Dostop do sistema | system, exec, shell_exec, passthru, proc_open, popen |
ukazi na strežniku |
Po priročniku PHP assert() od PHP 8.0 nizov ne vrednoti več kot kodo, create_function() pa je bila v PHP 8.0 odstranjena. Na strežnikih s starejšimi različicami PHP ostajata oba pomembna iskalna izraza.
Posamezen gradnik je redko sumljiv, base64_decode na primer mnoge aplikacije uporabljajo legitimno. Sumljiva postane kombinacija: dekodiranje, katerega rezultat teče neposredno v izvajanje.
Iskanje brez nevarnosti: neškodljivi ukazi grep
Naslednji ukazi datoteke preiskujejo samo kot besedilo. Kode PHP ne izvajajo. Pot prilagodite.
# Izvajanje v kombinaciji z dekodiranjem ali razširjanjem
grep -rlE "(eval|assert)\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin)" /var/www --include="*.php*"
# Zelo dolge vrstice (pogosto zapakirana koda)
grep -rlE ".{2000,}" /var/www --include="*.php*"
# Veliko šestnajstiških ubežnih sekvenc ali verig chr() v eni vrstici
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*"
# Klic funkcije prek spremenljivke neposredno po dodelitvi iz nizov
grep -rnE "\\\$[a-zA-Z_]+\s*=\s*['\"][a-z_]{4,}['\"]\s*;\s*\\\$[a-zA-Z_]+\s*\(" /var/www --include="*.php*" | head -50
Zadetke najprej preverite z ls -l in časovnim žigom, nato jih odprite samo kot besedilo, na primer z less. Sumljivih datotek nikoli ne prikličite prek brskalnika, saj bi jih strežnik s tem izvedel.
Kje zakrita koda tipično sedi
Zakrita koda redko stoji v lastni, opazni datoteki. Pogosteje je vstavljena v obstoječe datoteke, povsem na začetek ali povsem na konec, pogosto za mnogimi praznimi vrsticami, da ob odpiranju ne pade takoj v oči. Priljubljene so datoteke, ki se naložijo ob vsakem priklicu: osrednji index.php, konfiguracijske datoteke in funkcijske datoteke teme.
Posebej neopazna pot vodi prek konfiguracije. Z nastavitvijo auto_prepend_file PHP pred vsakim skriptom naloži dodatno datoteko. Če stoji v .user.ini ali .htaccess, ki ju je ustvaril napadalec, njegova koda teče ob vsakem priklicu strani, ne da bi bila spremenjena ena sama datoteka aplikacije. Vključena datoteka pri tem rada nosi neškodljivo končnico, kot sta .ico ali .png.
# Ali se pred ali po vsakem skriptu nalaga dodatna koda?
grep -rnE "auto_(prepend|append)_file" /var/www --include=".user.ini" --include=".htaccess" --include="*.conf" 2>/dev/null
# Koda PHP v datotekah s končnico slike ali ikone
grep -rlE "<\?php" /var/www --include="*.ico" --include="*.png" --include="*.jpg" --include="*.gif" 2>/dev/null
Ločevanje legitimnega zakritja od škodljive kode
Ni vsaka neberljiva koda zlonamerna. Pogosti so trije primeri:
- Komercialni kodirniki: Nekateri proizvajalci vtičnikov in programske opreme svojo kodo ščitijo z orodji, kot sta ionCube ali SourceGuardian. Datoteke so neberljive, a praviloma kažejo na potreben nalagalnik (loader). Primerjajte jih z izvirnim paketom.
- Minificirana ali ustvarjena koda: Predpomnilniki in mehanizmi predlog ustvarjajo dolge vrstice brez prelomov, vendar brez izvajanja dekodiranega besedila.
- Vdelani podatki: Slike ali pisave kot niz Base64 v datoteki PHP so nenavadni, a neškodljivi, dokler se rezultat ne izvaja.
Najzanesljivejši preizkus je primerjava z virom: če datoteka nespremenjena izvira iz paketa proizvajalca, je legitimna. Če ni v nobenem izvirnem paketu ali od njega odstopa, jo je treba raziskati.
Zakrite kode ne poskušajte dešifrirati na produkcijskem strežniku, na primer tako, da eval zamenjate z izpisom in datoteko prikličete. Že napaka pri tem izvede škodljivo kodo. Analiza sodi v izolirano okolje.
Meje iskanja po vzorcih
Napadalci poznajo običajne iskalne ukaze. Imena funkcij razdelijo, jih sestavijo med izvajanjem ali pravo kodo preselijo v podatkovno bazo, slikovne datoteke ali konfiguracijske vnose. Iskanje po vzorcih zato najde veliko, a ne vsega. Zakaj tudi pregledovalniki tu naletijo na meje, pojasnjuje Pregledovalniki škodljive programske opreme za spletne strani: kaj najdejo in česa ne.
Najdba je poleg tega redko edina. Napadalci v več datotekah pogosto uporabijo isti gradnik, na primer opazno oznako spremenljivke ali značilen niz na začetku kodiranega bloka. Po potrjeni najdbi zato ciljno iščite natanko ta niz v celotnem spletnem imeniku, tudi v drugih spletnih straneh na istem strežniku. Tako najdete sorojence, ki jih nobeno splošno iskanje po vzorcih ni zajelo.
Iskanje dopolnite s primerjavo z izvirnimi datotekami in pogledom v podatkovno bazo, glejte Preverjanje podatkovne baze glede vnesene kode. Preventivno je mogoče nepotrebne sistemske funkcije zakleniti, kot opisuje vodnik Pravilna uporaba disable_functions.
Kako PHP SOS pomaga konkretno?
- Iskanje: Datoteke in podatkovno bazo preiščemo z razširjenimi vzorci in primerjamo z izvirnimi paketi.
- Analiza: Sumljivo kodo raziščemo v izoliranem okolju, ne da bi ogrozili vaš sistem.
- Čiščenje: Z odstranjevanjem škodljive programske opreme odstranimo vse sestavne dele in zapremo pot vdora.
- Zaščita: Omejimo funkcije PHP in pravice pisanja ter nadzorujemo nove ali spremenjene datoteke.
Pogosta vprašanja
Ali je eval z base64_decode vedno škodljiva koda?
Ne vedno, a zelo pogosto. Legitimna koda te kombinacije skoraj ne potrebuje. V datotekah, ki ne sodijo k vaši aplikaciji ali znanemu vtičniku, je močan opozorilni znak.
Ali smem zakrito kodo za branje dekodirati?
Samo brez izvajanja in ne na produkcijskem strežniku. Že nenamerno izvajanje lahko povzroči škodo. Ob dvomih analizo prepustite strokovnjakom, ki delajo z izoliranimi okolji.
Zakaj moj pregledovalnik škodljive programske opreme kode ne najde?
Pregledovalniki iščejo znane vzorce. Napadalci svojo kodo nenehno spreminjajo, razdelijo imena funkcij ali jih sestavijo med izvajanjem. Prazen pregled zato ni dokaz za čist strežnik.
Kaj pa komercialno šifrirani vtičniki?
Nekateri proizvajalci svojo kodo ščitijo s kodirniki, kot sta ionCube ali SourceGuardian. Take datoteke so neberljive, a legitimne, če izvirajo od proizvajalca. Primerjajte jih z izvirnim paketom.
Ali disable_functions pomaga proti zakriti kodi?
Deloma. Funkcije, kot sta exec ali system, je mogoče zakleniti, eval pa ne, ker je jezikovna konstrukcija in ne funkcija. Zapora napadalcem otežuje naslednji korak, ne prepreči pa vsake škodljive kode.