Linux/Unix Internals: процессы, syscalls, память и основы exploitation на Linux
Зачем это нужно в Red Team
В первых статьях курса мы построили фундаментальное мышление:
В exploitation-модуле вы научились смотреть на сбой как на модель памяти и доверия к данным.
В Windows Internals вы перенесли это на уровень ОС: объекты, права, контекст выполнения.
В AD-части добавился распределённый слой идентичностей, ACL и доверий.Linux/Unix слой нужен, чтобы вы так же уверенно объясняли:
какой контекст безопасности у процесса (UID/GID, capabilities, namespaces),
как именно программа разговаривает с ядром (syscalls),
как устроена память процесса на Linux (ELF, mmap, страницы, ASLR, NX),
почему краш происходит именно так и что это означает для анализа уязвимостей.Цель статьи: дать практическую внутреннюю картину Linux/Unix, на которой позже строятся темы Linux privilege escalation, контейнеры, post-exploitation и reversing.
> Всё ниже используйте только в лаборатории, CTF и на авторизованных тестах.
Модель Unix: процессы, идентичности и права
Unix-подобные системы (Linux, *BSD, macOS) традиционно строятся вокруг простой идеи:
есть процессы,
процессы имеют идентичность (пользователь и группы),
доступ к объектам (файлам, сокетам, устройствам) определяется правами и политиками.Процесс как контейнер выполнения
Процесс в Linux включает:
виртуальное адресное пространство (память),
таблицу файловых дескрипторов,
контекст безопасности (UID/GID, capabilities, SELinux/AppArmor при наличии),
один или несколько потоков выполнения.UID/GID и эффективные права
В Linux важно различать реальные и эффективные идентичности.
ruid и rgid описывают, кто запустил процесс.
euid и egid описывают, какие права реально используются при проверках доступа.Это критично для Red Team, потому что многие реальные эскалации (особенно локальные) упираются не в “есть ли root”, а в то, в какой момент процесс получает эффективные права.
Справка:
getuid(2) — Linux manual page
setuid(2) — Linux manual pageSetuid/setgid как механизм доверия
Файлы могут иметь биты
setuid и
setgid: при запуске такой программы её
euid или
egid становится равным владельцу файла.
Практический смысл:
это легитимный способ “дать пользователю выполнить ограниченную операцию с повышенными правами”,
но это также высокорисковая зона: баги памяти и баги логики внутри setuid-программ обычно намного опаснее.Справка:
chmod(1) — Linux manual page
credentials(7) — Linux manual pageLinux capabilities: “root, но по частям”
Capabilities дробят “всемогущество root” на отдельные привилегии (например, право менять сетевые настройки или трассировать процессы).
Модель мышления:
вместо вопроса “root или не root” вы задаёте вопрос “какие конкретно привилегии у процесса”.Справка:
capabilities(7) — Linux manual pageSyscalls: граница между пользовательским кодом и ядром
В Linux почти все “настоящие” действия ОС делаются через системные вызовы:
открыть файл,
создать процесс,
выделить память,
отправить данные по сети,
изменить права.Почему syscalls важны для exploitation
Exploitation-мышление из первой статьи применимо напрямую:
пользовательский код работает с памятью и может ошибаться,
но результат часто выражается через syscalls (например, чтение данных, запись файла, запуск процесса),
а наблюдаемость и следы (логи, аудит) часто видны именно на уровне syscalls.!Граница user space/kernel space и роль syscalls
libc и “настоящий” syscall
Часто вы вызываете не syscall напрямую, а функцию стандартной библиотеки (glibc), которая внутри делает syscall.
Пример: open() в коде пользователя обычно превращается в один из современных syscall-вариантов вроде openat.
Справка:
syscalls(2) — Linux manual page
open(2) — Linux manual pageНаблюдение syscalls через strace
strace полезен для ответа на вопрос:
что программа реально делает в системе.
Пример безопасной диагностики:
Что вы обычно ищете:
какие файлы открываются,
какие ошибки возвращает ядро (например, EACCES, ENOENT),
какие дочерние процессы запускаются,
какие сетевые подключения происходят.Справка:
strace(1) — Linux manual pageПроцессы и потоки: fork/exec, copy-on-write и наблюдаемость
Linux-процессы традиционно создаются связкой
fork() и
execve().
fork: “почти копия” процесса
fork() создаёт дочерний процесс, который стартует как копия родителя.
Ключевой механизм эффективности: copy-on-write.
Память не копируется сразу.
Страницы разделяются до первой записи.
При записи создаётся копия страницы.Справка:
fork(2) — Linux manual pageexecve: “замена” программы
execve() не создаёт новый процесс. Он заменяет
образ процесса: загружает новый ELF, настраивает память, подхватывает аргументы и окружение.
Справка:
execve(2) — Linux manual pageПотоки: это тоже процессы (почти)
В Linux потоки реализованы через
clone(). Важная практическая мысль:
многие “потоковые” сущности видны как отдельные задачи в ядре,
диагностика и безопасность часто оперируют именно этим уровнем.Справка:
clone(2) — Linux manual page/proc: интерфейс к внутренностям процесса
/proc позволяет наблюдать:
командную строку процесса,
текущие открытые файловые дескрипторы,
карту памяти,
статус и идентичности.На практике особенно полезны:
/proc/<pid>/status (UID/GID, контекст),
/proc/<pid>/fd/ (какие файлы и сокеты открыты),
/proc/<pid>/maps (карта памяти),
/proc/<pid>/exe (какой бинарник запущен).Справка:
proc(5) — Linux manual pageПамять процесса на Linux: страницы, mmap и карта адресов
Exploitation всегда упирается в память, поэтому Linux-часть должна собрать в голове “карту” того, что вы видите в отладчике и в
/proc/<pid>/maps.
Страницы памяти и права
Виртуальная память разбита на страницы. Для каждой области памяти ядро хранит права:
r чтение,
w запись,
x исполнение.Это напрямую связано с защитой NX: данные не должны быть исполняемыми.
Справка:
mmap(2) — Linux manual page
mprotect(2) — Linux manual pageГде берётся память: brk и mmap
Исторически куча росла через
brk, а для больших/особых аллокаций используется
mmap.
Практический вывод для анализа уязвимостей:
адреса “кучи” и “mmap-областей” могут выглядеть по-разному,
многие аллокаторы используют mmap для крупных блоков,
карта памяти процесса объясняет, почему один и тот же баг может быть стабильным или нестабильным.Справка:
brk(2) — Linux manual page/proc/<pid>/maps: что вы там увидите
В
maps вы увидите строки вида “адреса, права, смещение, файл”. Это помогает понять:
где лежит основной бинарник,
где подключены библиотеки,
где стек,
где heap,
где анонимные mmap.!Типичная карта виртуальной памяти процесса Linux по данным /proc/<pid>/maps
ELF и динамический линкер: как запускается бинарник
Большинство Linux-бинарников имеют формат ELF. Для Red Team это важно по двум причинам:
вы понимаете, откуда берутся адреса кода и библиотек,
вы понимаете, какие защиты включены на уровне сборки и линковки.Справка:
elf(5) — Linux manual page
ld.so(8) — Linux manual pagePIE и ASLR: почему адреса “плавают”
Две связанные идеи:
ASLR рандомизирует размещение областей памяти.
PIE делает так, что даже основной исполняемый модуль можно загрузить по случайному адресу.Модель мышления exploitation-разработчика здесь такая:
без утечек адресов (или других примитивов) предсказуемость расположения кода снижается,
многие техники превращаются в задачу сначала получить информацию, а уже потом воздействовать.Справка:
randomize_va_space — Linux kernel documentationБазовые защиты на Linux и что они означают для анализа багов
Ниже не “как обойти”, а
как интерпретировать среду, когда вы расследуете уязвимость.
NX (DEP)
Если область памяти без
x, то даже контролируемые данные не исполнятся как код.
Следствие для анализа:
чаще появляется фокус на перепрыгивание по уже существующему коду (внутри бинарника и библиотек) и на утечки.Stack canary
Компилятор может вставлять проверку целостности стека, чтобы ловить перезапись.
Следствие:
“прямые” стековые переполнения часто превращаются в простой аварийный выход,
но всё равно полезны как сигнал ошибки границ.RELRO
RELRO усложняет некоторые классы перезаписи структур динамической линковки.
Следствие:
при анализе бинарника важно понимать, какие области будут read-only после инициализации.Справка по проверке параметров сборки:
readelf(1) — Linux manual pageОтладка и диагностика: как превращать краш в модель
Здесь вы применяете цикл из первой статьи курса (
воспроизведение → симптом → локализация → минимизация → вывод), но с Linux-инструментами.
gdb и core dumps
gdb нужен, чтобы видеть:
где произошло падение,
какие регистры и стек в момент падения,
какая инструкция выполнялась,
какие данные попали в память.Core dump фиксирует память процесса на момент падения, что полезно для повторяемого анализа.
Справка:
GDB documentation
core(5) — Linux manual pageПример рабочего потока (в лаборатории):
Санитайзеры: быстрый способ ловить классы ошибок
AddressSanitizer помогает поймать:
выход за границы,
use-after-free,
double free.Это полезно именно для обучения мышлению: вы быстрее связываете “симптом” и “класс ошибки”.
Справка:
AddressSanitizer — Clang documentationМинимальный учебный пример ошибки границ
Пример нужен не для “эксплуатации”, а чтобы увидеть, как crash проявляется и как его читать.
Что вы тренируете:
находить источник неконтролируемого копирования,
проверять размер буфера,
видеть, что “рядом” в стеке может быть повреждено.Справка:
strcpy(3) — Linux manual pageSyscalls как поверхность атаки и как источник следов
В реальной работе Red Team важно помнить двустороннюю природу syscalls:
для атакующего это “кнопки”, через которые достигается эффект,
для защитника и расследования это места, где остаются наблюдаемые следы.Примеры того, что вы можете объяснять после этой статьи:
почему попытка чтения чужой памяти упирается в права и в ограничения ядра,
почему один и тот же бинарник по-разному ведёт себя при разных настройках ASLR,
почему контейнеризация меняет “границы” процесса и что это значит для привилегий.Seccomp и sandboxing: когда процессу запрещают syscalls
Seccomp позволяет ограничить, какие syscalls может делать процесс.
Модель:
если процесс изначально запущен в “песочнице”, то даже при получении контроля выполнения вы можете быть ограничены тем, что ядро просто не даст вызвать нужные syscalls.Справка:
seccomp(2) — Linux manual page
prctl(2) — Linux manual pageNamespaces: почему “root в контейнере” не всегда root
Namespaces изолируют части системы (процессы, сеть, файловые точки монтирования и так далее). Это основа контейнеров.
Практическое мышление:
контекст “root” может быть ограничен пространством имён,
а значит важен вопрос: в какой именно изоляции находится процесс.Справка:
namespaces(7) — Linux manual pageКак связать это с предыдущими модулями курса
Эта статья намеренно соединяет три линии курса в одну:
Из exploitation-части вы берёте модель памяти, классы ошибок и дисциплину отладки.
Из Windows Internals вы берёте привычку мыслить контекстом безопасности и проверками доступа.
Из AD-части вы берёте идею “графа прав и доверий”, которая на Linux проявляется через UID/GID, группы, capabilities, namespaces и политики.Дальше по курсу на этой базе проще:
разбирать Linux LPE не как набор трюков, а как комбинацию контекста, поверхности syscalls, ошибок конфигурации и ошибок памяти,
читать отчёты и код эксплойтов осмысленно: понимать, какие части про память, а какие про ограничения ядра.Что читать по теме (из списка курса)
База по теме на уровне системного понимания:
The Linux Programming Interface (No Starch Press)Полезные первоисточники определений:
Linux manual pages (man7.org)Итог
Если свести Linux/Unix internals к нескольким опорам для Red Team:
Процесс это память, файловые дескрипторы, контекст безопасности и потоки.
Syscalls это граница с ядром: источник возможностей и одновременно источник следов.
Карта памяти (/proc/<pid>/maps) и механика mmap объясняют поведение багов памяти.
ELF, динамический линкер и флаги сборки определяют многие практические ограничения exploitation.
Защиты (ASLR, NX, canary, RELRO) меняют не “возможность атаковать”, а первый вопрос, который вы задаёте: “какой примитив здесь реалистичен?”.