Накратко: Замъгленият PHP код е код, умишлено направен нечетим, най-често чрез кодиране и компресия, които се разгръщат едва по време на изпълнение. Типични шаблони за разпознаване са eval заедно с base64_decode, gzinflate или str_rot13, много дълги редове, шестнадесетични низове и имена на функции, съставяни от части. Търсете с безобидни команди grep, никога не изпълнявайте кода и сверявайте находките с оригиналния пакет.
Защо нападателите крият кода си
Който оставя уеб шел или задна вратичка на сървър, иска тя да остане дълго неоткрита. Четим код би направил впечатление на всеки администратор, отворил файла, и всеки скенер би го намерил. Затова нападателите опаковат кода си: той се кодира, компресира и едва при извикването се сглобява отново и се изпълнява.
За PHP2Shell няма потвърдени Indicators of Compromise. Замъгленият код не е признак на тази уязвимост, а общ знак, че някой е вмъкнал код, независимо по кой път. Как да намерите и премахнете уеб шелове изцяло, описва Разпознаване и премахване на уеб шел. Тази статия се концентрира върху въпроса по какво разпознавате замъглен код.
eval, base64 и други: типичните градивни елементи
Замъгляването следва почти винаги един и същ принцип: нечетим блок данни се превръща от една или няколко функции обратно в код, който после се изпълнява. Следните елементи се появяват отново и отново.
| Елемент | Примери | Защо прави впечатление |
|---|---|---|
| Изпълнение | eval, assert с низ (до PHP 7.x), create_function (до PHP 7.x) |
изпълнява текст като код |
| Декодиране | base64_decode, str_rot13, hex2bin, strrev, convert_uudecode |
прави данните отново четими |
| Декомпресия | gzinflate, gzuncompress, gzdecode |
разархивира вграден код |
| Сглобяване | вериги chr(), свързване на кратки части, \x escape последователности |
крие имената на функции |
| Динамично извикване | променливи като име на функция, call_user_func, array_map с низ |
извикването не се появява никъде в явен вид |
| Достъп до системата | system, exec, shell_exec, passthru, proc_open, popen |
команди на сървъра |
Според PHP наръчника assert() от PHP 8.0 вече не оценява низове като код, а create_function() е премахната в PHP 8.0. На сървъри със по-стари версии на PHP и двете остават релевантни термини за търсене.
Отделен елемент рядко е подозрителен, base64_decode например ползват легитимно много приложения. Подозрително става съчетанието: декодиране, чийто резултат тече директно в изпълнение.
Безопасно търсене: безобидни команди grep
Следните команди претърсват файловете само като текст. Те не изпълняват PHP код. Пригодете пътя.
# Изпълнение, комбинирано с декодиране или декомпресия
grep -rlE "(eval|assert)\s*\(\s*(base64_decode|gzinflate|gzuncompress|str_rot13|hex2bin)" /var/www --include="*.php*"
# Много дълги редове (често пакетиран код)
grep -rlE ".{2000,}" /var/www --include="*.php*"
# Много шестнадесетични escape последователности или вериги chr() в един ред
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*"
# Извикване на функция през променлива веднага след присвояване от низове
grep -rnE "\\\$[a-zA-Z_]+\s*=\s*['\"][a-z_]{4,}['\"]\s*;\s*\\\$[a-zA-Z_]+\s*\(" /var/www --include="*.php*" | head -50
Проверете попаденията първо с ls -l и времевия печат и ги отваряйте после само като текст, например с less. Никога не извиквайте подозрителни файлове през браузъра, защото така сървърът би ги изпълнил.
Къде обикновено стои замъгленият код
Замъгленият код рядко стои в собствен, набиващ се на очи файл. По-често се вмъква в съществуващи файлове, съвсем в началото или съвсем в края, често след много празни редове, за да не прави впечатление веднага при отваряне. Любими са файлове, които се зареждат при всяко извикване: централният index.php, конфигурационните файлове и файловете с функции на темата.
Особено незабележим път минава през конфигурацията. С настройката auto_prepend_file PHP зарежда преди всеки скрипт допълнителен файл. Ако тя стои в .user.ini или .htaccess, създадени от нападател, кодът му работи при всяко извикване на страница, без да е променен нито един файл на приложението. Включваният файл обича да носи безобидно разширение като .ico или .png.
# Зарежда ли се допълнителен код преди или след всеки скрипт?
grep -rnE "auto_(prepend|append)_file" /var/www --include=".user.ini" --include=".htaccess" --include="*.conf" 2>/dev/null
# PHP код във файлове с разширение на изображение или икона
grep -rlE "<\?php" /var/www --include="*.ico" --include="*.png" --include="*.jpg" --include="*.gif" 2>/dev/null
Отличаване на легитимно замъгляване от злонамерен код
Не всеки нечетим код е злонамерен. Три случая се срещат често:
- Комерсиални енкодери: Някои производители на добавки и софтуер защитават кода си с инструменти като ionCube или SourceGuardian. Файловете са нечетими, но по правило сочат към нужния loader. Сверете ги с оригиналния пакет.
- Минифициран или генериран код: Кешовете и шаблонните машини създават дълги редове без прекъсвания, но без изпълнение на декодиран текст.
- Вградени данни: Изображения или шрифтове като низ Base64 в PHP файл са необичайни, но безобидни, докато резултатът не се изпълнява.
Най-надеждният тест е сверката с източника: ако файлът идва непроменен от пакета на производителя, той е легитимен. Ако не стои в никой оригинален пакет или се различава от него, подлежи на разследване.
Не се опитвайте да разшифровате замъглен код на продуктивния сървър, например като замените eval с изход и извикате файла. Дори една грешка при това изпълнява злонамерения код. Анализът принадлежи в изолирана среда.
Граници на търсенето по шаблони
Нападателите познават обичайните команди за търсене. Те разделят имената на функции, съставят ги по време на изпълнение или изнасят същинския код в базата данни, в файлове с изображения или в конфигурационни записи. Търсенето по шаблони затова намира много, но не всичко. Защо и скенерите стигат тук до граници, обяснява Скенери за зловреден софтуер за уебсайтове: какво намират и какво не.
Находката освен това рядко е единствената. Нападателите често ползват в няколко файла един и същ елемент, например забележимо име на променлива или характерна последователност от знаци в началото на кодирания блок. След потвърдена находка затова потърсете целенасочено точно тази последователност в цялата уеб директория, и в други уебсайтове на същия сървър. Така намирате братя, които никое от общите търсения по шаблони не е обхванало.
Допълнете търсенето със сверка с оригинални файлове и с поглед в базата данни, вижте Проверка на базата данни за вмъкнат код. Превантивно ненужните системни функции могат да се блокират, както е описано в ръководството Правилно използване на disable_functions.
Как помага PHP SOS конкретно?
- Търсене: Претърсваме файловете и базата данни с разширени шаблони и сверяваме с оригинални пакети.
- Анализ: Изследваме подозрителния код в изолирана среда, без да застрашаваме Вашата система.
- Почистване: С премахване на зловреден софтуер премахваме всички съставни части и затваряме пътя на проникване.
- Защита: Ограничаваме PHP функциите и правата за запис и наблюдаваме нови или променени файлове.
Често задавани въпроси
eval с base64_decode винаги ли е злонамерен код?
Не винаги, но много често. Легитимният код почти не се нуждае от това съчетание. Във файлове, които не принадлежат към Вашето приложение или към позната добавка, то е силен предупредителен сигнал.
Мога ли да декодирам замъглен код, за да го прочета?
Само без да го изпълнявате и не на продуктивния сървър. Дори непреднамерено изпълнение може да причини щети. При съмнение оставете анализа на специалисти, които работят с изолирани среди.
Защо моят скенер за зловреден софтуер не намира кода?
Скенерите търсят познати шаблони. Нападателите непрекъснато променят кода си, разделят имената на функции или ги съставят по време на изпълнение. Празното сканиране затова не е доказателство за чист сървър.
Какво да кажем за комерсиално криптирани добавки?
Някои производители защитават кода си с енкодери като ionCube или SourceGuardian. Такива файлове са нечетими, но легитимни, ако идват от производителя. Сверете ги с оригиналния пакет.
Помага ли disable_functions срещу замъглен код?
Отчасти. Функции като exec или system могат да се блокират, eval обаче не, защото е езикова конструкция, а не функция. Блокировката затруднява следващата стъпка на нападателите, но не предотвратява всеки злонамерен код.