MAATRIX / Блог / SUID-файл, которого не должно быть: как проверить систему

SUID-файл, которого не должно быть: как проверить систему

MAATRIX

Злоумышленник, получивший на сервере доступ обычного пользователя, редко останавливается на этом — ему нужен способ вернуться с правами root даже после того, как вы закроете дыру, через которую он вошёл. Один из самых старых и до обидного простых приёмов — скопировать /bin/bash куда-нибудь в сторону и выставить на копию бит SUID: теперь любой, кто знает про файл, получает root-шелл одной командой, без пароля и без записи в auth.log. Ниже — как систематически искать такие файлы, отличать их от легитимных SUID-бинарников дистрибутива и встроить проверку в регулярную рутину.

Что даёт SUID и почему это удобная лазейка

Бит SUID (set user ID) на исполняемом файле заставляет ядро при запуске (execve()) выставить эффективный UID процесса равным владельцу файла, а не тому, кто его запустил. Если файл принадлежит root, то любой пользователь, у которого есть право на выполнение, при запуске такого файла на время получает права root — именно так работают passwd, sudo, su, mount, ping в некоторых сборках. Подробнее о механизме на уровне ядра — в статье про сам бит setuid.

Для атакующего это идеальный бэкдор по нескольким причинам:

  • он не требует пароля root и не оставляет следа в sudo-логах — команда sudo вообще не участвует;
  • он переживает смену паролей всех пользователей и даже смену пароля root;
  • обнаруживается только целенаправленным поиском — стандартный ps, top, антивирус на него не смотрят;
  • достаточно одной команды на закрепление: cp /bin/bash /usr/lib/.hidden/.sh; chmod 4755 /usr/lib/.hidden/.sh — и путь к рутовому шеллу готов на будущее.

Похожий, но менее очевидный вариант — бит SGID (set group ID), который аналогично подменяет эффективную группу процесса. SGID-бэкдор менее эффектен (нужна ещё и подходящая группа), но встречается, когда атакующий метит не в root, а в группу с доступом к конфигам, ключам или логам.

Как найти все SUID и SGID файлы в системе

Базовый инструмент — find с флагом -perm, который умеет проверять конкретные биты прав через маску:

# все файлы с битом SUID во всей файловой системе
sudo find / -xdev -type f -perm -4000 2>/dev/null

# все файлы с битом SGID
sudo find / -xdev -type f -perm -2000 2>/dev/null

# оба бита сразу, с деталями владельца и прав
sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null

Разберём флаги, потому что от них зависит, что именно вы увидите:

  • -perm -4000 — «хотя бы этот бит установлен» (маска, а не точное совпадение); без минуса find требовал бы точного совпадения всех битов прав, что почти никогда не нужно;
  • -xdev — не уходить за пределы файловой системы, на которой начат поиск. Критично: без него find / полезет в примонтированные NFS-шары, /proc, смонтированные образы и бэкапы, и поиск займёт часы, а результат будет засорён нерелевантными путями. Если на сервере несколько дисков и точек монтирования, которые тоже нужно проверить, гоняйте find отдельно на каждой с явным путём;
  • 2>/dev/null — глушит Permission denied от каталогов вроде /proc/[pid]/... или чужих домашних директорий, в которые не может заглянуть даже root на некоторых FUSE-монтировках;
  • -type f — не путать с symlink на SUID-файл; сам симлинк бита не несёт, но стоит проверить, куда он ведёт.

Разовый запуск отработает секунды-минуты в зависимости от размера диска. На типичной VPS с одним диском полный обход / укладывается в 10-30 секунд.

Если нужен вывод, удобный для сравнения между запусками — отсортированный и с хешами:

sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) \
  -printf '%m %u:%g %p\n' 2>/dev/null | sort -k3 > /root/suid-audit-$(date +%F).txt

%m — права в восьмеричном виде, %u:%g — владелец и группа, %p — путь. Такой файл легко положить рядом с предыдущим прогоном и посмотреть diff.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Как сравнить с ожидаемым списком для дистрибутива

Голый список путей мало о чём говорит, пока не с чем сравнить. Есть три практичных способа получить «эталон».

1. Спросить у пакетного менеджера, какие файлы вообще должны быть SUID. На Debian/Ubuntu это делается через список файлов пакета и их заявленные права:

# какому пакету принадлежит файл
dpkg -S /usr/bin/passwd

# проверить, что реальные права файла совпадают с тем, что задекларировал пакет
dpkg --verify passwd 2>&1 | grep -v '^..5......'

На RHEL/AlmaLinux аналог — rpm -V для всей системы разом:

# проверка всех установленных пакетов на расхождение прав, владельца, содержимого
rpm -Va | grep '^.M'

Буква M в выводе rpm -Va означает именно расхождение в правах доступа (mode) — то, что нас интересует в первую очередь при поиске подмены SUID.

2. Сверить с собственным baseline, снятым на чистой системе. Сразу после установки ОС и до вывода сервера в продакшн снимите список SUID/SGID файлов командой из предыдущего раздела и сохраните его отдельно — это ваш эталон для конкретной версии дистрибутива и набора установленных пакетов:

sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -printf '%p\n' \
  2>/dev/null | sort > /root/suid-baseline.txt

Каждую последующую проверку сравнивайте с этим файлом:

sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -printf '%p\n' \
  2>/dev/null | sort > /tmp/suid-current.txt
diff /root/suid-baseline.txt /tmp/suid-current.txt

Строки со знаком > — новые SUID-файлы, которых не было в baseline. Именно на них смотреть в первую очередь. Обновляйте baseline осознанно — после каждого apt upgrade/dnf upgrade, который мог добавить легитимные SUID-бинарники (например, обновление sudo иногда меняет права на промежуточные файлы пакета).

3. Готовые списки для сверки. Инструменты аудита вроде Lynis держат встроенные проверки на SUID/SGID и умеют сверять найденное с известными наборами для популярных дистрибутивов — это не заменяет собственный baseline, но полезно как второе мнение. Мы разбирали его настройку в статье про установку Lynis на VPS, а типичные грабли при работе с находками — в разборе частых ошибок Lynis.

На что обращать внимание при просмотре списка

Даже без diff с baseline опытный взгляд на список SUID-файлов быстро отсекает подозрительное. Красные флаги:

  • Путь за пределами стандартных бинарных каталогов. Легитимные SUID-файлы почти всегда лежат в /usr/bin, /usr/sbin, /bin, /sbin, иногда /usr/lib/<пакет>/. Файл с SUID в /tmp, /var/tmp, /dev/shm, домашней директории пользователя, /opt/непонятно-что или в скрытом каталоге вроде /usr/lib/.X11-unix-cache — почти всегда повод для тревоги;
  • Оболочки и интерпретаторы с SUID. bash, sh, zsh, python3, perl, php-cgi с выставленным SUID — это готовый root-шелл, и таких файлов в чистой системе быть не должно вообще. Единственное легитимное исключение — некоторые системы намеренно используют SUID-обёртки для конкретных узких задач, но это никогда не голая копия шелла;
  • Свежая дата изменения, не совпадающая с датой установки пакета. stat /path/to/file покажет Modify/Change; если файл из системного пакета, но изменён позже даты его установки (dpkg -l / rpm -q --queryformat), это может значить, что бинарник подменили после установки;
  • Владелец не root у файла с эффектом root. Обычно SUID имеет смысл именно для файлов, принадлежащих root — если владелец другой пользователь, а бит всё равно даёт заметное повышение прав в контексте задачи (например, доступ к группе), стоит разобраться, зачем;
  • Дублирующиеся имена известных утилит в нестандартных местах. Файл /usr/local/bin/passwd или /usr/bin/.passwd рядом с настоящим /usr/bin/passwd — классический трюк с маскировкой под системную утилиту;
  • SUID на файлах, которые сам процесс установки никогда не помечает так. Например, find, vim, less, cp, tar, awk — в норме без SUID. Если видите такие в выводе — почти гарантированно бэкдор или ошибка конфигурации (например, скрипт установки по недосмотру дал chmod 4755 вместо 0755).

Полезно сразу проверить содержимое подозрительного бинарника без его выполнения:

file /path/to/suspicious-binary
sha256sum /path/to/suspicious-binary
# сравнить хеш с оригиналом из пакета, если он известен

Если file показывает обычный ELF-бинарник с именем bash, а хеш не совпадает с хешем настоящего /bin/bash — это либо подмена, либо просто копия с других прав. В любом случае трогать chmod/удалять стоит только после того, как вы поняли контекст: если сервер, возможно, скомпрометирован, лучше сохранить копию файла и метаданные (владелец, время, stat) для разбора инцидента, прежде чем всё вычищать.

Автоматизация: cron-скрипт и файловый мониторинг

Разовая проверка руками полезна один раз, но реальную защиту даёт регулярность. Минимальный вариант — cron-задача, которая гоняет find и присылает diff с baseline:

#!/bin/bash
# /usr/local/bin/suid-check.sh
BASELINE=/root/suid-baseline.txt
CURRENT=$(mktemp)
MAIL_TO="admin@example.com"

find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -printf '%p\n' \
  2>/dev/null | sort > "$CURRENT"

DIFF=$(diff "$BASELINE" "$CURRENT")

if [ -n "$DIFF" ]; then
  echo "$DIFF" | mail -s "SUID/SGID изменения на $(hostname)" "$MAIL_TO"
fi

rm -f "$CURRENT"
# /etc/cron.d/suid-check — ежедневно в 03:15
15 3 * * * root /usr/local/bin/suid-check.sh

Более надёжный вариант — не полагаться на периодический опрос, а ловить событие изменения в реальном времени через auditd. Правило на файловую систему целиком, конечно, избыточно, но можно повесить наблюдение на конкретные каталоги или отслеживать сам системный вызов chmod/fchmodat:

# добавить в /etc/audit/rules.d/suid-watch.rules
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid!=-1 -k suid_change

После augenrules --load любой вызов chmod от имени залогиненного пользователя попадёт в аудит-лог с ключом suid_change, и его можно найти через ausearch -k suid_change. Настройку auditd с нуля и типичные проблемы (переполнение буфера, потеря событий при нагрузке) мы разбирали в статье про настройку audit-логов через auditd — хотя в контексте этой статьи важно, что сам факт chmod +s попадёт в лог, даже если сам файл потом переименуют или спрячут.

Ещё один вариант — file integrity monitoring (AIDE, Tripwire, auditd в связке с правилами на конкретные бинарники): такие инструменты не просто ловят факт установки SUID, а следят за неизменностью самих системных файлов, что закрывает и соседний сценарий — подмену содержимого легитимного SUID-бинарника без изменения прав.

Регулярность проверки и где она встраивается в процесс

Разовая находка ничего не гарантирует — если проверку не повторять, следующий бэкдор проживёт до случайного обнаружения. Практичный график:

КогдаЧто делать
При вводе сервера в продакшнСнять baseline, сохранить отдельно от сервера (в git-репозитории конфигов, в бэкапе)
Ежедневно, автоматическиCron-скрипт с diff против baseline, алерт на любое расхождение
После каждого крупного обновления пакетовВручную сверить diff, обновить baseline осознанно (не автоматически!)
При подозрении на компрометациюПолный ручной прогон с анализом каждого нового файла, до всех остальных шагов реагирования
Раз в кварталПолный аудит безопасности сервера целиком, SUID — один из пунктов чек-листа

Важный нюанс: baseline нельзя обновлять автоматически тем же скриптом, который делает проверку — иначе сам механизм проверки станет бесполезным (атакующий поставит бэкдор, а следующий же прогон молча впишет его в новый эталон как «норму»). Обновление baseline — это отдельное осознанное действие человека после того, как он убедился, что новые файлы легитимны.

Проверку SUID/SGID стоит включить в общий процесс, а не держать изолированно. Она логично встаёт рядом с остальными пунктами базового харденинга сервера — см. чек-лист безопасности нового сервера — и с процедурой подготовки к внешнему аудиту, где такой лог часто прямо запрашивают как доказательство контроля: подробнее в статье как подготовить сервер к аудиту безопасности.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли просто снять SUID со всех файлов «на всякий случай»?

Нет — сломаете легитимные механизмы: passwd перестанет менять пароли, ping может перестать создавать raw-сокеты, mount/umount для обычных пользователей откажут. Снимайте бит только у конкретных подтверждённо лишних файлов, а не массово.

Почему find / иногда зависает или работает очень долго?

Обычно из-за отсутствия -xdev — поиск уходит в сетевые файловые системы, /proc, смонтированные образы бэкапов. Добавьте -xdev и, если есть отдельные точки монтирования, которые тоже нужно проверить, гоняйте find на них отдельно с явным путём.

Есть ли в Docker-контейнерах смысл в этой проверке?

Да, причём часто даже более критичный — образ с лишним SUID-бинарником тиражируется на все контейнеры сразу. Проверку стоит запускать на этапе сборки образа (в CI) через тот же find внутри временного контейнера, до публикации образа в реестр.

SGID на каталоге — это то же самое, что SGID на файле?

Нет, разный эффект. SGID на исполняемом файле работает как SUID, но для группы. SGID на каталоге — это правило наследования: новые файлы, созданные внутри, получают группу каталога, а не основную группу создавшего их пользователя. Второе безопасно и часто используется намеренно (общие каталоги для команды), путать их при анализе не стоит.

Что делать, если найден подозрительный SUID-файл на проде прямо сейчас?

Не удаляйте и не запускайте его. Сначала stat, sha256sum, file, копия файла и его метаданных в отдельное безопасное место, проверка, кто и когда его создал (auditd, ausearch, история bash, логи авторизации). Только после того как собраны улики — снимайте бит chmod -s или удаляйте, и параллельно ищите, как злоумышленник вообще получил возможность его создать: сам SUID-файл — это следствие, а не причина компрометации.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →