DDexec / EverythingExec

Reading time: 4 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks

Контекст

У Linux, щоб запустити програму, вона повинна існувати як файл, вона повинна бути доступною якимось чином через ієрархію файлової системи (так працює execve()). Цей файл може знаходитися на диску або в оперативній пам'яті (tmpfs, memfd), але вам потрібен шлях до файлу. Це дуже спростило контроль за тим, що запускається на системі Linux, полегшило виявлення загроз і інструментів зловмисників або запобігання їх спробам виконати щось своє (e. g. не дозволяючи неправа користувачам розміщувати виконувані файли будь-де).

Але ця техніка змінює все це. Якщо ви не можете запустити процес, який хочете... тоді ви захоплюєте вже існуючий.

Ця техніка дозволяє вам обійти загальні захисні техніки, такі як тільки для читання, noexec, білий список імен файлів, білий список хешів...

Залежності

Остаточний скрипт залежить від наступних інструментів, які повинні бути доступні в системі, яку ви атакуєте (за замовчуванням ви знайдете їх усі скрізь):

dd
bash | zsh | ash (busybox)
head
tail
cut
grep
od
readlink
wc
tr
base64

Техніка

Якщо ви можете довільно змінювати пам'ять процесу, то ви можете його захопити. Це можна використовувати для захоплення вже існуючого процесу та заміни його на іншу програму. Ми можемо досягти цього або за допомогою системного виклику ptrace() (який вимагає, щоб ви мали можливість виконувати системні виклики або щоб gdb був доступний на системі), або, що більш цікаво, записуючи в /proc/$pid/mem.

Файл /proc/$pid/mem є одноосібним відображенням всього адресного простору процесу (e. g. від 0x0000000000000000 до 0x7ffffffffffff000 в x86-64). Це означає, що читання з цього файлу або запис у нього за зміщенням x є тим самим, що читання з або модифікація вмісту за віртуальною адресою x.

Тепер у нас є чотири основні проблеми, з якими потрібно зіткнутися:

  • Загалом, тільки root і власник програми файлу можуть його змінювати.
  • ASLR.
  • Якщо ми спробуємо прочитати або записати за адресою, яка не відображена в адресному просторі програми, ми отримаємо помилку I/O.

Ці проблеми мають рішення, які, хоча й не ідеальні, є хорошими:

  • Більшість оболонкових інтерпретаторів дозволяють створення дескрипторів файлів, які потім будуть успадковані дочірніми процесами. Ми можемо створити fd, що вказує на файл mem оболонки з правами на запис... тому дочірні процеси, які використовують цей fd, зможуть змінювати пам'ять оболонки.
  • ASLR навіть не є проблемою, ми можемо перевірити файл maps оболонки або будь-який інший з procfs, щоб отримати інформацію про адресний простір процесу.
  • Тому нам потрібно lseek() через файл. З оболонки це не можна зробити, якщо не використовувати infamous dd.

Детальніше

Кроки відносно прості і не вимагають жодного роду експертизи для їх розуміння:

  • Проаналізуйте двійковий файл, який ми хочемо запустити, і завантажувач, щоб дізнатися, які відображення їм потрібні. Потім створіть "shell" код, який, загалом, виконає ті ж кроки, що й ядро при кожному виклику execve():
  • Створіть зазначені відображення.
  • Прочитайте двійникові файли в них.
  • Налаштуйте права.
  • Нарешті, ініціалізуйте стек з аргументами для програми та розмістіть допоміжний вектор (необхідний завантажувачу).
  • Стрибніть у завантажувач і дайте йому зробити решту (завантажити бібліотеки, необхідні для програми).
  • Отримайте з файлу syscall адресу, до якої процес повернеться після виконання системного виклику.
  • Перезапишіть це місце, яке буде виконуваним, нашим shellcode (через mem ми можемо змінювати не записувані сторінки).
  • Передайте програму, яку ми хочемо запустити, в stdin процесу (буде read() цим "shell" кодом).
  • На цьому етапі завантажувачу залишається завантажити необхідні бібліотеки для нашої програми та стрибнути в неї.

Перевірте інструмент на https://github.com/arget13/DDexec

EverythingExec

Існує кілька альтернатив dd, одна з яких, tail, наразі є програмою за замовчуванням, що використовується для lseek() через файл mem (що було єдиною метою використання dd). Ці альтернативи:

bash
tail
hexdump
cmp
xxd

Встановлюючи змінну SEEKER, ви можете змінити використовуваний seeker, e. g.:

bash
SEEKER=cmp bash ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)

Якщо ви знайдете ще одного дійсного seeker, який не реалізовано в скрипті, ви все ще можете його використовувати, встановивши змінну SEEKER_ARGS:

bash
SEEKER=xxd SEEKER_ARGS='-s $offset' zsh ddexec.sh ls -l <<< $(base64 -w0 /bin/ls)

Заблокуйте це, EDRs.

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks