LOAD_NAME / LOAD_CONST opcode OOB Read
Вивчайте та вправляйтесь в хакінгу AWS: Навчання AWS Red Team Expert (ARTE) від HackTricks
Вивчайте та вправляйтесь в хакінгу GCP:
Навчання GCP Red Team Expert (GRTE) від HackTricks
Ця інформація була взята з цього опису.
TL;DR
Ми можемо використовувати функціонал OOB читання в опкоді LOAD_NAME / LOAD_CONST, щоб отримати деякий символ у пам'яті. Це означає використання трюку типу (a, b, c, ... сотні символів ..., __getattribute__) if [] else [].__getattribute__(...)
для отримання потрібного символу (наприклад, назви функції), який вам потрібен.
Потім просто створіть свій експлойт.
Огляд
Вихідний код досить короткий, містить лише 4 рядки!
Виход за межі читання
Як відбувається збій сегментації?
Давайте почнемо з простого прикладу, [a, b, c]
може скомпілюватися в наступний байт-код.
Проте що, якщо co_names
стає порожнім кортежем? Опкод LOAD_NAME 2
все одно виконується і намагається прочитати значення з тієї адреси пам'яті, з якої воно початково мало б бути. Так, це функція зчитування "за межами меж".
Основна концепція рішення проста. Деякі опкоди в CPython, наприклад LOAD_NAME
та LOAD_CONST
, є вразливими (?) на OOB read.
Вони отримують об'єкт з індексом oparg
з кортежу consts
або names
(це те, що приховано під назвами co_consts
та co_names
). Ми можемо звернутися до наступного короткого відрізка про LOAD_CONST
, щоб побачити, що робить CPython під час обробки опкоду LOAD_CONST
.
Таким чином ми можемо використовувати функціонал OOB, щоб отримати "ім'я" з довільного зміщення пам'яті. Щоб переконатися, яке це ім'я та яке його зміщення, просто продовжуйте спробувати LOAD_NAME 0
, LOAD_NAME 1
... LOAD_NAME 99
... І ви можете знайти щось приблизно з oparg > 700. Ви також можете спробувати використати gdb, щоб краще розібратися у структурі пам'яті, звісно, але я не думаю, що це буде простіше?
Генерація Експлоіту
Після того, як ми отримали корисні зміщення для імен / констант, як ми можемо отримати ім'я / константу з цього зміщення та використовувати його? Ось хитрість для вас:
Давайте припустимо, що ми можемо отримати ім'я __getattribute__
зі зміщенням 5 (LOAD_NAME 5
) з co_names=()
, тоді просто виконайте наступні дії:
Зверніть увагу, що не обов'язково називати його
__getattribute__
, ви можете назвати його якось коротше або дивніше
Ви можете зрозуміти причину, просто переглянувши його байткод:
Зверніть увагу, що LOAD_ATTR
також отримує ім'я з co_names
. Python завантажує імена з тієї самої позиції, якщо ім'я однакове, тому другий __getattribute__
все ще завантажується з позиції offset=5. Використовуючи цю функцію, ми можемо використовувати довільне ім'я, якщо ім'я знаходиться в пам'яті поруч.
Для генерації чисел повинно бути тривіально:
0: not [[]]
1: not []
2: (not []) + (not [])
...
Сценарій експлойту
Я не використовував константи через обмеження довжини.
Спочатку ось скрипт для пошуку цих зміщень імен.
І наступне призначене для створення реального Python експлойту.
Це в основному робить наступне для тих рядків, які ми отримуємо з методу __dir__
:
Вивчайте та практикуйте хакінг AWS: Школа хакінгу HackTricks для експертів червоної команди AWS (ARTE)
Вивчайте та практикуйте хакінг GCP:
Школа хакінгу HackTricks для експертів червоної команди GCP (GRTE)
Last updated