Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2024-28397-js2py-Sandbox-Escape — Подробный анализ CVE-2024-28397, уязвимости побега из песочницы в js2py, позволяющей выполнение удаленного кода (RCE) через обход объектов Python. Включает анализ кода, PoC и рекомендации по исправлению для защитных исследований. | Kitploit
Инструменты/GitHubGitHub/naved124/cve-2024-28397-js2py-sandbox-escape
Анализ уязвимостейАнализ КодаЭксплуатацияВеб-безопасностьСтатьи и ИсследованияОбучение и ОбразованиеРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHub
naved124/cve-2024-28397-js2py-sandbox-escape

CVE-2024-28397-js2py-Sandbox-Escape

Подробный анализ CVE-2024-28397, уязвимости побега из песочницы в js2py, позволяющей выполнение удаленного кода (RCE) через обход объектов Python. Включает анализ кода, PoC и рекомендации по исправлению для защитных исследований.

Репозиторий
191 год назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2024-28397-js2py-Побег из песочницы

Анализ уязвимости js2py

Введение

js2py — это популярный пакет Python, который позволяет выполнять код JavaScript внутри интерпретатора Python. Он используется различными веб-скраперами для разбора кода JavaScript на веб-сайтах.

Существует уязвимость в реализации глобальной переменной внутри js2py, позволяющая атакующему получить ссылку на объект Python в среде js2py, что даёт возможность выйти за пределы среды JS и выполнить произвольные команды на хосте.

Обычно пользователь вызывает js2py.disable_pyimport(), чтобы запретить коду JavaScript выход из среды js2py. Но с помощью этой уязвимости атакующий может обойти это ограничение и выполнить любую команду на целевом хосте.

Угроза может разместить веб-сайт, содержащий вредоносный файл JavaScript, или отправить вредоносный скрипт через HTTP API для разбора жертвой. Таким образом, злоумышленник может добиться удалённого выполнения кода на хосте, выполняя произвольные команды оболочки на цели.


Предисловие

js2py — это библиотека Python, часто используемая в краулерах для разбора и выполнения кода JS в нативной среде Python. Краулеры обычно используют js2py для разбора кода JS, полученного из сети, тем самым имитируя среду браузера.

Однако js2py имеет функцию, которая чрезвычайно опасна для краулеров: она поддерживает импорт и использование пакетов Python в JS, то есть js2py позволяет коду JS манипулировать различными библиотеками Python и напрямую взаимодействовать со средой Python. Именно из-за этого мы можем использовать метод, похожий на Jinja SSTI, и в среде js2py использовать объект Python для нахождения класса subprocess.Popen и добиться RCE.

Более того, так как js2py — это пакет эпохи Python2, широко используемый и давно не поддерживаемый, его анализ должен быть относительно простым.


Анализ кода

Преобразование кода JS в код Python

После установки точек останова можно обнаружить, что место, где на самом деле выполняется код JS — это функция Eval из host/jseval.py. Установив в ней точки останова, можно увидеть код Python, преобразованный js2py.

Например, этот фрагмент кода JS:

let a = 114
console.log(a)

В конечном итоге будет преобразован в такой код Python:

var.registers(['a'])
var.put('a', Js(114.0))
EVAL_RESULT = (var.get('console').callprop('log', var.get('a')))

Видно, что переменные уровня JS хранятся в переменной Python var. Все значения уровня JS чисто хранятся как класс PyJs (здесь Js на самом деле является функцией, объяснение позже). Функции также вызываются через callprop. В обычных условиях код JS не может затрагивать объекты Python.

При просмотре кода было замечено, что автор очень любит использовать конкатенацию строк для построения конечного кода Python, поэтому возникла мысль, можно ли сконструировать код JS, который генерирует недопустимый код Python, тем самым создавая произвольный код Python и выполняя его. Но учитывая, что этот путь намного сложнее последующего, он не был исследован дальше.


Преобразование данных Python в данные JS

Чтобы получить объект Python и реализовать RCE, прежде всего нужно посмотреть, как объекты Python преобразуются в объекты PyJs.

Сначала находим реализацию функции Js в base.py. Роль функции Js заключается в преобразовании переданного значения Python в соответствующее значение PyJs, тем самым позволяя коду JS манипулировать этими значениями.

def Js(val, Clamped=False):
    '''Converts Py type to PyJs type'''
    if isinstance(val, PyJs):
        return val
    elif val is None:
        return undefined
    elif isinstance(val, basestring):
        return PyJsString(val, StringPrototype)
    elif isinstance(val, bool):
        return true if val else false
    elif isinstance(val, float) or isinstance(val, int) or isinstance(
            val, long) or (NUMPY_AVAILABLE and isinstance(
                val,
                (numpy.int8, numpy.uint8, numpy.int16, numpy.uint16,
                 numpy.int32, numpy.uint32, numpy.float32, numpy.float64))):
        # This is supposed to speed things up. may not be the case
        if val in NUM_BANK:
            return NUM_BANK[val]
        return PyJsNumber(float(val), NumberPrototype)
    ... # several lines omitted here
    else:  # try to convert to js object
        return py_wrap(val)

Видно, что базовые структуры данных Python, такие как bool, float, list и т.д., преобразуются в специализированные классы PyJs, а данные других типов обрабатываются функцией py_wrap и в конечном итоге становятся классом PyObjectWrapper.

Обычные классы PyJs представляют числа, логические значения и другие распространённые данные, в то время как PyObjectWrapper представляет специальные данные, такие как модули Python. Таким образом, как только мы получим данные типа PyObjectWrapper, мы сможем использовать метод доступа к атрибутам, аналогичный Jinja SSTI, для достижения RCE.

В общем случае данные типа PyObjectWrapper можно получить только при включённой функции импорта пакетов Python, но из-за того, что js2py долгое время не обновлялся и не учитывал различия между Python2 и Python3, в конечном итоге произошёл побег из песочницы.

Небольшое отступление: при просмотре реализации PyJs я увидел, что автор написал такие строки кода:

if six.PY3:
    PyJs.__hash__ = PyJs._fuck_python3
    PyJs.__truediv__ = PyJs.__div__

Можно сказать, что автор крайне не любит Python3.


Реализация функций JS

Хотя js2py предоставляет функцию преобразования кода JS в код Python, он также предоставляет несколько встроенных объектов, таких как console и Object, для поддержки нормального выполнения кода JS.

Наша конечная цель — обойти ограничение pyimport и получить объект PyObjectWrapper. Из анализа выше видно, чтобы получить объект PyObjectWrapper из ничего, мы можем только начать с реализации встроенных объектов и извлечь из них объект PyObjectWrapper.

Начнём сканирование кода реализации встроенных объектов. Из constructors/jsobject.py видна реализация различных функций в объекте Object, включая часто используемые функции, такие как Object.keys.

Затем можно увидеть эту функцию:

def getOwnPropertyNames(obj):
    if not obj.is_object():
        raise MakeError(
            'TypeError',
            'Object.getOwnPropertyDescriptor called on non-object')
    return obj.own.keys()

js2py использует dict для представления объектов в JS. Функция keys() здесь вызывает keys() словаря Python. Любой, кто изучал Python, знает, что в Python2 эта функция возвращает список, а в Python3 — представление dict_keys. Согласно реализации функции Js выше, этот dict_keys будет преобразован в PyObjectWrapper, и таким образом мы сможем добиться RCE.


Достижение RCE

Сначала проверим, может ли getOwnPropertyNames получить PyObjectWrapper:

import js2py

code = """
let a = Object.getOwnPropertyNames({})
console.log(a)
"""

js2py.eval_js(code)

Он вывел PyObjectWrapper(dict_keys([])), конечно может.

Затем, основываясь на этом объекте, получаем функцию __getattribute__ и легко достигаем RCE. При написании PoC это казалось слишком сложным; на самом деле, достаточно использовать __class__.__base__, чтобы получить функцию __getattribute__.

Затем, используя функцию __getattribute__, получаем объект object, а затем пишем рекурсивную функцию для поиска любого класса любого модуля. Здесь для RCE ищется subprocess.Popen.

ПРИМЕЧАНИЕ (санировано): Фрагмент кода ниже показан в образовательных и исследовательских целях. Он был санирован, чтобы избежать облегчения злоупотребления: чувствительные команды оболочки и прямое выполнение системных команд были заменены заполнителями. Не запускайте это против производственных или незащищённых систем.

import js2py
Скачать инструмент