Kort sagt: Tilsløret PHP-kode er kode, der med vilje er gjort ulæselig, oftest ved kodning og komprimering, som først opløses under kørslen. Typiske genkendelsesmønstre er eval sammen med base64_decode, gzinflate eller str_rot13, meget lange linjer, hex-tegnstrenge og funktionsnavne, der sættes sammen af enkeltdele. Søg med harmløse grep-kommandoer, udfør aldrig koden, og afstem fund med den oprindelige pakke.
Hvorfor angribere skjuler deres kode
Den, der lægger en webshell eller bagdør på en server, vil have, at den forbliver uopdaget længe. Læselig kode ville falde i øjnene på enhver administrator, der åbner filen, og enhver scanner ville finde den. Derfor pakker angribere deres kode ind: Den kodes, komprimeres og sættes først sammen igen og udføres ved kaldet.
For PHP2Shell findes der ingen bekræftede indicators of compromise. Tilsløret kode er ikke et kendetegn ved denne sårbarhed, men et generelt tegn på, at nogen har smuglet kode ind, uanset ad hvilken vej. Hvordan I overordnet finder og fjerner webshells, beskriver Genkend og fjern webshells. Dette indlæg koncentrerer sig om spørgsmålet, hvordan I genkender tilsløret kode.
eval, base64 og lignende: de typiske byggesten
Tilsløring følger næsten altid samme princip: En ulæselig datablok forvandles af en eller flere funktioner tilbage til kode, der så udføres. Følgende byggesten dukker op igen og igen.
| Byggesten | Eksempler | Hvorfor den falder i øjnene |
|---|---|---|
| Udførelse | eval, assert med tegnstreng (til og med PHP 7.x), create_function (til og med PHP 7.x) |
udfører tekst som kode |
| Afkodning | base64_decode, str_rot13, hex2bin, strrev, convert_uudecode |
gør data læselige igen |
| Dekomprimering | gzinflate, gzuncompress, gzdecode |
pakker indlejret kode ud |
| Sammensætning | chr()-kæder, sammenkædning af korte delstykker, \x-escapes |
skjuler funktionsnavne |
| Dynamisk kald | variabler som funktionsnavn, call_user_func, array_map med tegnstreng |
kaldet optræder ingen steder i klartekst |
| Systemadgang | system, exec, shell_exec, passthru, proc_open, popen |
kommandoer på serveren |
Ifølge PHP-manualen vurderer assert() siden PHP 8.0 ikke længere tegnstrenge som kode, og create_function() blev fjernet i PHP 8.0. På servere med ældre PHP-versioner forbliver begge relevante søgebegreber.
En enkelt byggesten er sjældent mistænkelig, base64_decode bruger mange applikationer f.eks. legitimt. Mistænkelig bliver kombinationen: afkodning, hvis resultat flyder direkte ind i en udførelse.
Søg risikofrit: harmløse grep-kommandoer
Følgende kommandoer gennemsøger filer kun som tekst. De udfører ingen PHP-kode. Tilpas stien.
# Udførelse kombineret med afkodning eller dekomprimering
grep -rlE "(eval|assert)\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin)" /var/www --include="*.php*"
# Meget lange linjer (ofte pakket kode)
grep -rlE ".{2000,}" /var/www --include="*.php*"
# Mange hex-escapes eller chr()-kæder i én linje
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*"
# Funktionskald via en variabel direkte efter en tildeling af tegnstrenge
grep -rnE "\\\$[a-zA-Z_]+\s*=\s*['\"][a-z_]{4,}['\"]\s*;\s*\\\$[a-zA-Z_]+\s*\(" /var/www --include="*.php*" | head -50
Kontrollér først fund med ls -l og tidsstemplet, og åbn dem derefter kun som tekst, f.eks. med less. Kald aldrig mistænkelige filer via browseren, for dermed ville serveren udføre dem.
Hvor tilsløret kode typisk sidder
Tilsløret kode står sjældent i en egen, påfaldende fil. Hyppigere indsættes den i eksisterende filer, helt i begyndelsen eller helt til sidst, ofte efter mange tomme linjer, så den ikke straks falder i øjnene ved åbningen. Populære er filer, der indlæses ved hvert kald: den centrale index.php, konfigurationsfiler og et temas funktionsfiler.
En særligt upåfaldende vej går via konfigurationen. Med indstillingen auto_prepend_file indlæser PHP en ekstra fil før hvert script. Står den i en .user.ini eller .htaccess, som en angriber har oprettet, kører hans kode med ved hvert sidekald, uden at en eneste applikationsfil er ændret. Den indbundne fil har gerne en harmløs endelse som .ico eller .png.
# Indlæses der ekstra kode før eller efter hvert script?
grep -rnE "auto_(prepend|append)_file" /var/www --include=".user.ini" --include=".htaccess" --include="*.conf" 2>/dev/null
# PHP-kode i filer med billed- eller ikonendelse
grep -rlE "<\?php" /var/www --include="*.ico" --include="*.png" --include="*.jpg" --include="*.gif" 2>/dev/null
Skel legitim tilsløring fra skadelig kode
Ikke enhver ulæselig kode er ondsindet. Tre tilfælde forekommer hyppigt:
- Kommercielle encodere: Nogle plugin- og softwareproducenter beskytter deres kode med værktøjer som ionCube eller SourceGuardian. Filerne er ulæselige, men henviser typisk til den nødvendige loader. Afstem dem med den oprindelige pakke.
- Minificeret eller genereret kode: Cacher og skabelonmotorer producerer lange linjer uden linjeskift, dog uden udførelse af afkodet tekst.
- Indlejrede data: Billeder eller skrifttyper som Base64-tegnstreng i en PHP-fil er usædvanligt, men harmløst, så længe resultatet ikke udføres.
Den mest pålidelige test er afstemningen med kilden: Stammer filen uændret fra producentens pakke, er den legitim. Ligger den i ingen oprindelig pakke eller afviger fra den, bør den undersøges.
Forsøg ikke at dekryptere tilsløret kode på produktionsserveren, f.eks. ved at erstatte eval med et output og kalde filen. Allerede en fejl derved udfører den skadelige kode. Analysen hører hjemme i et isoleret miljø.
Mønstersøgningens grænser
Angribere kender de gængse søgekommandoer. De deler funktionsnavne op, sætter dem sammen under kørslen eller udlægger den egentlige kode i databasen, i billedfiler eller i konfigurationsposter. En mønstersøgning finder derfor meget, men ikke alt. Hvorfor også scannere støder på grænser her, forklarer Malware-scannere til websites: Hvad de finder, og hvad de ikke finder.
Et fund er desuden sjældent det eneste. Angribere bruger i flere filer ofte den samme byggesten, f.eks. en påfaldende variabelbetegnelse eller en markant tegnfølge i begyndelsen af den kodede blok. Søg derfor efter et bekræftet fund målrettet efter netop denne tegnfølge i hele webmappen, også i andre websites på samme server. Sådan finder I søskende, som ingen af de generelle mønstersøgninger har fanget.
Suppler søgningen med afstemningen mod originalfiler og et kig i databasen, se Kontrollér databasen for indsmuglet kode. Forebyggende kan ikke nødvendige systemfunktioner spærres, som beskrevet i vejledningen Brug disable_functions korrekt.
Hvordan hjælper PHP SOS konkret?
- Søg: Vi gennemsøger filer og database med udvidede mønstre og afstemmer mod originale pakker.
- Analysér: Vi undersøger mistænkelig kode i et isoleret miljø uden at bringe jeres system i fare.
- Ryd op: Med malwarefjernelsen fjerner vi alle bestanddele og lukker indfaldsvejen.
- Sikr: Vi begrænser PHP-funktioner og skriverettigheder og overvåger nye eller ændrede filer.
Ofte stillede spørgsmål
Er eval med base64_decode altid skadelig kode?
Ikke altid, men meget ofte. Legitim kode har næppe brug for denne kombination. I filer, der ikke hører til jeres applikation eller et kendt plugin, er den et stærkt advarselstegn.
Må jeg afkode tilsløret kode for at læse den?
Kun uden at udføre den og ikke på produktionsserveren. Allerede en utilsigtet udførelse kan gøre skade. Overlad i tvivlstilfælde analysen til fagfolk, der arbejder med isolerede miljøer.
Hvorfor finder min malware-scanner ikke koden?
Scannere søger efter kendte mønstre. Angribere ændrer hele tiden deres kode, deler funktionsnavne op eller sætter dem sammen under kørslen. En tom scanning er derfor intet bevis for en ren server.
Hvad med kommercielt krypterede plugins?
Nogle producenter beskytter deres kode med encodere som ionCube eller SourceGuardian. Sådanne filer er ulæselige, men legitime, hvis de stammer fra producenten. Afstem dem med den oprindelige pakke.
Hjælper disable_functions mod tilsløret kode?
Delvist. Funktioner som exec eller system kan spærres, eval derimod ikke, fordi det er en sprogkonstruktion og ikke en funktion. Spærringen gør angribernes næste skridt sværere, men forhindrer ikke al skadelig kode.