LOAD_NAME / LOAD_CONST opcode OOB Read
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ця інформація була взята з цього опису.
Ми можемо використовувати функцію OOB read в LOAD_NAME / LOAD_CONST opcode, щоб отримати деякий символ в пам'яті. Це означає використання трюку на кшталт (a, b, c, ... сотні символів ..., __getattribute__) if [] else [].__getattribute__(...)
, щоб отримати символ (такий як ім'я функції), який вам потрібен.
Потім просто створіть свій експлойт.
Джерельний код досить короткий, містить лише 4 рядки!
Ви можете ввести довільний код Python, і він буде скомпільований в об'єкт коду Python. Однак co_consts
і co_names
цього об'єкта коду будуть замінені на порожній кортеж перед eval цього об'єкта коду.
Таким чином, всі вирази, що містять константи (наприклад, числа, рядки тощо) або імена (наприклад, змінні, функції), можуть в кінцевому підсумку викликати сегментаційну помилку.
Як виникає сегментаційна помилка?
Почнемо з простого прикладу, [a, b, c]
може бути скомпільовано в наступний байт-код.
Але що, якщо co_names
стане порожнім кортежем? Опкод LOAD_NAME 2
все ще виконується і намагається прочитати значення з тієї адреси пам'яті, з якої він спочатку повинен бути. Так, це "особливість" читання за межами межі.
Основна концепція рішення проста. Деякі опкоди в CPython, наприклад LOAD_NAME
і LOAD_CONST
, вразливі (?) до OOB читання.
Вони отримують об'єкт з індексу 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 Hacking:HackTricks Training AWS Red Team Expert (ARTE) Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)