![]() |
https://forum.antichat.xyz/attachmen...0409298359.png
Зачем? Тот, кто читает эти строки, наверняка уже не раз слышал скрипт кидди, задорно кричащего: «У меня рут!». Он тыкает в терминал sudo su - и считает, что достиг просветления, что перед ним разверзлись небеса вычислительной вселенной и все архангелы-демоны отныне покорны его воле. Сию секунду, с наскока, через призму одного лишь привилегированного доступа, мир действительно кажется простым. Но это иллюзия, мимолетная и опасная. Ты заходишь на заброшенный, покрытый цифровым мхом форум. Не тот, что сверкает Material Design и просит куки, а тот, что пахнет статикой моноширинного шрифта, где подписи в тредах- это не смайлики, а PGP-ключи и цитаты из «Степеней Свободы». Здесь говорят не о «взломе», а о понимании. Не о «руте», а о контроле. И контроль этот редко лежит на поверхности. Чаще всего он спрятан в механиках, которые старше многих из нас, в фундаментальных договоренностях между ядром и процессом, о которых не пишут в мануалах по DevOps. Сегодня мы говорим об одном из таких фундаментальных, почти метафизических, понятий - переменных окружения процесса. И не просто говорим, а разбираем, как их скрытая модификация в родительском процессе может стать ключом к выполнению произвольного кода в контексте дочернего. Это не очередной CVE-сплойт, не троян в стиле Голливуда. Это тихая, почти невидимая манипуляция самой средой обитания программы. Если процессы - это живые организмы, то переменные окружения - это воздух, которым они дышат, гравитация, которая их держит, и невидимые химические сигналы, управляющие их поведением. А что, если мы научимся менять состав этого воздуха для еще не рожденных процессов? Зачем это все, если есть sudo? - спросит тот самый скрипт-кидди. А затем, что sudo -это всего лишь один из стражей у самых видимых ворот. Мир UNIX-подобных систем (а значит, и всего современного интернета, облаков и телефонов в твоем кармане) построен на идее наследования. Процесс-отец порождает процесс-сына и передает ему часть своего контекста. Самый очевидный кусочек этого контекста -переменные окружения. И эта передача часто происходит мимо всех систем контроля доступа, политик SELinux и мониторов целостности. Потому что это не действие, это - состояние. Система не «разрешает» или «запрещает» передачу переменной LD_PRELOAD. Она просто передает ее, потому что так задумано. Это кровь, текущая по артериям системы. И наша задача - научиться вводить в эту кровь нужные нам «вещества», которые изменят поведение «органов», которые ее получат. Представь себе сцену. Ты -ограниченный пользователь на сервере. sudo тебе не светит. su -тем более. Но ты обнаруживашь, что раз в минуту от имени системного демона (скажем, ntpd или redis) запускается какой-то скрипт для проверки логов. Скрипт этот -бинарный, запакованный, отладочная информация стерта. Трогать его -значит вызвать срабатывание HIDS (Host-based Intrusion Detection System). Но ты замечаешь, что демон, запускающий этот скрипт, берет свою конфигурацию (включая пути к библиотекам) из переменной окружения, которую сам читает из файла /etc/default/ntpd. А права на этот файл… О, чудо! Они стоят -rw-rw-r--, и ты в группе, которая может писать в него! Это не уязвимость в классическом понимании. Это неверная конфигурация. И она в тысячу раз ценнее, потому что эксплуатируется тихо, без падения сервисов, без segfault-ов в логах. Ты не атакуешь код. Ты атакуешь намерение администратора, его предположение о безопасности. Вот о таких атаках на намерение и контекст мы и будем говорить. Это высший пилотаж. Когда ты перестаешь искать дыры в стенах и начинаешь искать способ убедить стражника, что ты - свой, что твои инструкции - это и есть приказы командира. Что тебя ждет? Мы не просто пробежимся по LD_PRELOAD. Мы погрузимся в:
И помни главное правило нашего цеха: Сила обязывает. Знание, которое ты получишь, - это ответственность. Используй его, чтобы укреплять, а не крушить. Чтобы делать сети надежнее, код - чище, а системы - понятнее для тех, кто придет после тебя. Предупреждение (самое главное): Всё, что описано ниже, - это исследование механизмов операционной системы. Применение этого в чужих системах без явного разрешения - преступление. Ты не станешь крутым хакером, сломав чужой сервер. Ты станешь крутым, ПОНЯВ, как он работает, и построив что-то своё, более надёжное. Запомни это. Основа основ. Что такое окружение и кто его носит? 1.1. Душа процесса: argc, argv[] и envp[] Когда ты в консоли пишешь ls -la /home, происходит примерно следующее:
Код: Код:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin1.2. Где живёт окружение? /proc/[pid]/environ В Linux всё есть файл. А если не файл, то процесс. Окружение любого процесса лежит в виртуальном файле /proc/[pid]/environ. Это не настоящий файл на диске. Это окно, предоставленное ядром, в память процесса. Bash: Код:
# Посмотрим на окружение своего шелла:Файл /proc/[pid]/environ доступен на чтение только владельцу процесса и руту. Это важное ограничение. Мы не можем просто так взять и прочитать окружение процесса другого пользователя. Но если мы уже получили привилегии (или работаем в контексте своего процесса/родителя), это наша отправная точка. 1.3. Родители и дети. Кто кого порождает? Система - это дерево. init или systemd (PID 1) - корень. Всё растёт из него. Когда процесс делает fork() + execve(), он передаёт новому процессу свою среду. Это фундаментальный механизм. Представь сценарий:
Практическое оружие. От теории к инъекции. 2.1. Классика жанра: LD_PRELOAD - твой первый друг и учитель Это самая известная переменная окружения в хакерском мире. Динамический линковщик ld-linux.so, который загружает разделяемые библиотеки (*.so) для программы, смотрит на эту переменную. Если LD_PRELOAD содержит путь к библиотеке, эта библиотека будет загружена ПЕРВОЙ и получит приоритет над всеми другими. Что это даёт? Возможность перехватывать вызовы стандартных библиотечных функций (libc): strcmp, fopen, getuid, да чего угодно. Практический инструмент №1: Создание простейшей библиотеки для LD_PRELOAD. Допустим, мы хотим логировать все вызовы strcmp в какой-нибудь программе. inject_strcmp.c: C: Код:
#define _GNzU_SOURCEBash: Код:
gcc -shared -fPIC -o libinject_strcmp.so inject_strcmp.c -ldlBash: Код:
# Классический способ - задать в окружении до запуска программыА теперь главный вопрос: А если программа, которую мы хотим "обогатить", запускается не нами напрямую, а, например, тем же демоном cron? Нам нужно, чтобы LD_PRELOAD был в окружении родительского процесса (cron) в момент запуска целевой задачи. Ограничение: LD_PRELOAD - мощная, но грубая сила. Многие SUID/SGID-бинарные файлы (те, что меняют эффективного пользователя, типа passwd) игнорируют LD_PRELOAD из соображений безопасности. Линковщик сбрасывает эту переменную при обнаружении бита setuid. Также многие системы используют selinux или apparmor, которые могут блокировать такое вмешательство. 2.2. Более тонкий подход: LD_LIBRARY_PATH и подмена библиотек Если LD_PRELOAD - это штурмовой молот, то манипуляция LD_LIBRARY_PATH - это отмычка. Эта переменная говорит линковщику, в каких каталогах искать разделяемые библиотеки помимо стандартных путей (/lib, /usr/lib). Сценарий:
2.3. Неочевидные переменные: от GET до PS1 Окружение - это не только для линковщика. Это общая среда передачи данных.
Вот мы и подошли к самому сложному и интересному. У нас есть целевой родительский процесс (демон, служба). Мы хотим изменить его окружение до того, как он породит интересного нам ребёнка. Как? 3.1. Способ 1: Прямой доступ через /proc/[pid]/mem (путь самурая) Файл /proc/[pid]/mem - это, грубо говоря, прямая проекция всей памяти процесса. Теоретически, зная адрес, по которому расположен массив envp[] в целевом процессе, мы можем записать туда новые строки. Почти невозможно на практике. Почему?
3.2. Способ 2: Использование ptrace() - отладчик как оружие Ptrace - системный вызов для отладки. С его помощью один процесс может наблюдать и управлять выполнением другого: читать/писать регистры, память, перехватывать системные вызовы. Идея: Приаттачиться к целевому родительскому процессу, дождаться момента, когда он будет готовиться к fork()/execve(), и в этот момент подменить аргументы (в том числе указатель на окружение) системного вызова. Практический инструмент №2: Скрипт на основе strace + gdb (концепт). Полноценный код для ptrace-инжектирования окружения - это сотни строк на C. Но есть утилиты, которые делают часть работы.
Bash: Код:
sudoОграничения:
Вернёмся к основам. Окружение наследуется. Ключевой вопрос: а откуда родительский процесс сам получает своё окружение? Варианты:
Пример уязвимой конфигурации (/etc/systemd/system/vulnerable.service): Код: Код:
[Service]Но это требует прав на запись в системные конфиги. Часто это уже привилегированная операция. 3.4. Способ 4: Атака на момент запуска (сценарий для пентеста) Самый классический сценарий, где это работает почти из коробки.
Конфиг sudoers может содержать директиву env_keep. Она разрешает сохранять определённые переменные окружения из среды пользователя, вызывающего sudo. Допустим, в /etc/sudoers есть строка: Код: Код:
Defaults env_keep += "LD_LIBRARY_PATH"Атакующий (в группе admin): Bash: Код:
# Устанавливаем свою библиотеку в путьМораль: Конфигурация - это код. Плохая конфигурация - это уязвимость. Обход защиты. Мир после Spectre и Meltdown. Современные системы не спят. Давай посмотрим, что стоит на нашем пути. 4.1. SELinux, AppArmor, grSecurity Мандатные системы контроля доступа (MAC). Они работают на уровне политик.
4.2. Усиленные дистрибутивы (Kernel Hardening)
4.3. Атаки на уровне контейнеров (Docker & Co.) В контейнерах окружение часто контролируется жёстче, но есть нюансы.
Bash: Код:
# Если получили шелл в контейнере:Давай соберём всё вместе в учебном примере. Цель: заставить простой демон, запускающий скрипт, выполнить наш код через LD_PRELOAD. Сценарий:
Создадим простейший демон на Python (для наглядности): test_daemon.py: Python: Код:
#!/usr/bin/env python3Bash: Код:
#!/bin/bashШаг 1: Разведка. Смотрим его PID: ps aux | grep test_daemon. Допустим, PID = 5555. Смотрим его окружение: cat /proc/5555/environ | tr '\0' '\n'. Видим, что CUSTOM_VAR не установлена. Шаг 2: Создание полезной нагрузки. Пишем библиотеку для LD_PRELOAD, которая перехватит вызов какой-нибудь функции в logger.sh. Но logger.sh - это bash-скрипт, он не загружает libc напрямую так, как бинарный файл. Нужна более хитрая тактика. Вариант А: Заставить демон запускать НЕ оригинальный logger.sh, а нашу обёртку. Для этого можно подменить переменную PATH в окружении демона. Создадим вредоносный скрипт с тем же именем: /tmp/evil_logger.sh: Bash: Код:
#!/bin/bashШаг 3: Инъекция. Нам нужно изменить окружение процесса 5555, добавив PATH=/tmp:$PATH. Используем gdb (в учебных целях!). Bash: Код:
sudoЖдём минуту. Демон делает subprocess.run(["logger.sh"]). Шелл ищет logger.sh в PATH. Первый путь - /tmp. Там лежит наш evil_logger.sh. Он выполняется, пишет в /tmp/owned.txt и незаметно вызывает оригинальный скрипт. Проверяем: cat /tmp/owned.txt -> OWNED! PID: 6677. Смотрим логи оригинального скрипта (если они есть) - всё выглядит нормально: [LOGGER] ... CUSTOM_VAR=HACKED. Успех. Мы модифицировали окружение родительского процесса и повлияли на поведение его дочернего процесса. Зачем всё это нужно? Ты мог прочитать всё это и подумать: "Ну, в 2024 году это всё детские игрушки. EDR, антивирусы, изоляция - всё это ловит такие примитивные техники". И будешь отчасти прав. Прямая модификация через /proc/mem или gdb на защищённой системе - это как идти на КПП с гранатомётом: эффектно, но тебя заметят за километр. Так в чём смысл?
Ещё один кирпич в стене. Итак, мы прошли долгий путь. От сухих строчек в /proc/[pid]/environ до концепции подмены воздуха, которым дышат процессы. Если ты читаешь эти строки не просто пробежав глазами, а прокручивая в голове примеры, возможно, даже запуская их на своей тестовой виртуалке - поздравляю. Ты только что прокачал свой скилл не просто как «хакер», а как системный философ. Потому что именно здесь, в этой точке, где техническая механика сталкивается с абстрактной моделью поведения системы, и рождается настоящее понимание. И теперь, на прощание, давай выйдем за рамки чистой техники и поговорим о том, что это все на самом деле значит. Переменные окружения как наследуемый мир Представь на секунду, что каждый процесс - не просто исполняемый код, а целая вселенная. У нее есть свои законы (системные вызовы), свое пространство (виртуальная память), свое время (таймеры, планировщик). Но откуда берутся физические константы этой вселенной? Гравитация, скорость света, заряд электрона? В мире процессов эти константы - переменные окружения. Когда процесс-родитель создает процесс-ребенок, он не просто копирует код. Он копирует целый мир. Со всеми его странными, иногда нелогичными, законами. PATH - это карта путей, по которым можно путешествовать в поисках исполняемых файлов. LD_LIBRARY_PATH - это список измерений, откуда можно призывать библиотеки-артефакты. HOME - это точка отсчета, твоя база. TMPDIR - это область хаоса, где можно создавать и разрушать временные файлы-миры. И самое главное - ребенок не может просто так изменить мир родителя. Он получает его как данность. Он может изменить свою копию, но это уже будет его личная вселенная, которую он, в свою очередь, передаст своим детям. А теперь ключевое: Что, если мы можем изменить исходный мир еще до того, как в нем начнут рождаться новые вселенные? Мы становимся богами-инженерами, которые подкручивают константы мироздания. Не взламываем двери, не ломаем стены - мы меняем само пространство-время так, что нужные нам события становятся неизбежными. Это и есть суть атаки через окружение родителя. Мы не атакуем программу. Мы атакуем контекст, в котором она рождается и живет. И контекст, в отличие от кода, часто остается беззащитным. Почему это до сих пор актуально? Эпоха контейнеров и изоляции «Погоди, - скажет скептик. - Мы живем в 2024 году. У нас есть контейнеры с их собственными неймспейсами, cgroups, изолированными файловыми системами. У нас есть eBPF, который может отслеживать каждое движение. Зачем эти древние трюки с LD_PRELOAD?» А вот затем, брат, что сложность - враг безопасности. Контейнер - это не магия. Это набор механизмов ядра, которые пытаются создать иллюзию изоляции. Но что лежит в основе каждого контейнера? Процесс. Чаще всего - PID 1 внутри этого контейнера. А откуда он берет свое окружение? От docker run -e, от Kubernetes ConfigMap, от образа. И если в этой цепочке передачи есть слабое звено (world-writable файл, из которого читается EnvironmentFile, уязвимость в рантайме оркестратора), то вся изоляция рушится, потому что мы атакуем не стену, а информацию, которую передают через эту стену. Более того, сама идея микросервисов и оркестрации увеличила поверхность атаки, связанную с конфигурацией. Тысячи yaml-файлов, десятки тысяч переменных окружения, передаваемых между подами, сервисами, инит-контейнерами. Администратор, который вручную правит sudoers, думает трижды. Инженер, который пишет env: в деплоймент для кубернетеса, часто делает это на автомате. И здесь рождается новый класс уязвимостей: Supply Chain через конфигурацию. Твой код может быть идеален. Твой образ - отсканирован на все CVE. Но если в момент запуска в твой контейнер попадает переменная JAVA_OPTS или NODE_EXTRA_CA_CERTS из скомпрометированного источника, все летит к чертям. И атаковать это будет не скрипт-кидди, а тот, кто прочитал эту статью и понял, что истинная сила лежит в управлении средой, а не в взломе кода. От атаки к защите Если ты дошел досюда и твой мозг жаждет применить эти знания где-то кроме безобидной виртуалки - остановись. Сделай шаг назад. И спроси себя: Как я могу использовать это, чтобы сделать мир хоть немного безопаснее? Вот твои новые суперсилы:
Ты освоил фундамент. Но здание еще не построено. Вот направления, в которых стоит копать:
Техника, которую мы разобрали, - не панацея. Это один из инструментов в бесконечно большом наборе. Настоящий мастер отличается не знанием одного приема, а способностью видеть связи между разными слоями системы: от аппаратного обеспечения до бизнес-логики приложения. Истинная сила, которую ты сегодня приобрел, - это системное мышление. Способность увидеть в простой переменной PATH не просто строку, а вектор наследования, доверия и потенциального компромета. Способность воспринимать ОС не как данность, а как диалог между процессами, где окружение - это язык этого диалога. Запомни окончательную формулу: Цитата:
Мы, те, кто ковыряется в /proc и читает man-страницы на ночь, - мы не вандалы. Мы - архитекторы реальности. И от нас зависит, будет ли эта реальность устойчивой, прозрачной и, в конечном счете, свободной. Пусть твой PATH всегда ведет к мудрости, а твой LD_PRELOAD загружает только проверенные библиотеки. Система ждет своего исследователя. |
| Время: 13:59 |