
Python 피클링 디컴파일러 및 정적 분석기

Fickling은 Python pickle 객체 직렬화를 위한 디컴파일러, 정적 분석기, 바이트코드 재작성기입니다. Fickling을 사용하면 PyTorch 파일을 포함한 악성 pickle 또는 pickle 기반 파일을 탐지, 분석, 리버스 엔지니어링하거나 심지어 생성할 수도 있습니다.
Fickling은 Python 라이브러리와 CLI로 모두 사용할 수 있습니다.
Fickling은 Python 3.11 이상이 필요하며 의존성이 매우 적습니다. 라이브러리와 명령줄 유틸리티 모두 pip 또는 uv를 통해 설치할 수 있습니다:
# Using pip
python -m pip install fickling
# Using uv
uv pip install fickling
PyTorch는 Fickling의 선택적 의존성입니다. 따라서 Fickling의 pytorch 및 polyglot 모듈을 사용하려면 다음을 실행해야 합니다:
# Using pip
python -m pip install fickling[torch]
# Using uv
uv pip install fickling[torch]
Fickling은 모델에 포함된 pickle 파일을 자동으로 스캔하여 AI/ML 코드베이스 보안을 지원할 수 있습니다. Fickling은 pickle 모듈을 후킹하고 모델을 로드할 때 발생하는 import를 검증합니다. 안전한 것으로 간주되는 ML 라이브러리의 import 허용 목록에 대해서만 import를 검사하며, 다른 import를 포함하는 파일은 차단합니다.
torch, numpy 또는 pickle을 사용하는 다른 라이브러리를 import하기 전에 가능한 한 이른 시점에 다음 줄을 추가하세요:
import fickling
# This sets global hooks on pickle
fickling.hook.activate_safe_ml_environment()
보호를 제거하려면:
fickling.hook.deactivate_safe_ml_environment()
사용 중인 모델에 Fickling이 허용하지 않는 import가 포함되어 있을 수 있습니다. 그래도 모델을 로드하려면 also_allow 인수를 사용하여 특정 사용 사례에 맞게 추가 import를 허용하면 됩니다:
fickling.hook.activate_safe_ml_environment(
also_allow=[
"some.import",
"another.allowed.import",
]
)
중요: 수동으로 추가한 import가 실제로 안전한지, 그리고 공격자가 임의 코드를 실행할 수 있게 하지 않는지 항상 확인해야 합니다. 이를 수행하는 방법이 확실하지 않은 경우, Fickling의 Github 저장소에 해당 import/모델을 명시하는 이슈를 열 수 있으며, 우리 팀이 이를 검토하고 가능하다면 허용 목록에 포함할 수 있습니다.
Fickling은 코드베이스에 원활하게 통합되어 런타임에 악성 파일의 로딩을 탐지하고 중단할 수 있습니다.
아래에서는 pickle 파일에 대한 안전성 검사를 적용하기 위해 fickling을 사용하는 다양한 방법을 보여줍니다. 내부적으로는 pickle 라이브러리를 후킹하여 안전성 검사를 추가하므로, 파일에서 악성 콘텐츠가 탐지되면 pickle 파일을 로딩할 때 UnsafeFileError 예외가 발생합니다.
# This enforces safety checks every time pickle is used to deserialize
fickling.always_check_safety()
# Attempt to load an unsafe file now raises an exception
with open("file.pkl", "rb") as f:
try:
pickle.load(f)
except fickling.UnsafeFileError:
print("Unsafe file!")
with fickling.check_safety():
# All pickle files loaded within the context manager are checked for safety
try:
with open("file.pkl", "rb") as f:
pickle.load("file.pkl")
except fickling.UnsafeFileError:
print("Unsafe file!")
# Files loaded outside of context manager are NOT checked
pickle.load("file.pkl")
종료 시 컨텍스트 관리자는 진입 시 설치되어 있던 것을 복원하므로, 이미 설정된 훅은 그대로 유효하게 유지되며 pickle이 훅 해제된 상태로 남지 않습니다.
# Use fickling.load() in place of pickle.load() to check safety and load a single pickle file
try:
fickling.load("file.pkl")
except fickling.UnsafeFileError as e:
print("Unsafe file!")
# Perform a safety check on a pickle file without loading it
if not fickling.is_likely_safe("file.pkl"):
print("Unsafe file!")
발생한 예외 내에서 fickling의 안전성 분석 세부 정보에 접근할 수 있습니다:
>>> try:
... fickling.load("unsafe.pkl")
... except fickling.UnsafeFileError as e:
... print(e.info)
{
"severity": "OVERTLY_MALICIOUS",
"analysis": "Call to `eval(b'[5, 6, 7, 8]')` is almost certainly evidence of a malicious pickle file. Variable `_var0` is assigned value `eval(b'[5, 6, 7, 8]')` but unused afterward; this is suspicious and indicative of a malicious pickle file",
"detailed_results": {
"AnalysisResult": {
"OvertlyBadEval": "eval(b'[5, 6, 7, 8]')",
"UnusedVariables": [
"_var0",
"eval(b'[5, 6, 7, 8]')"
]
}
}
}
Python이 아닌 다른 언어를 사용하는 경우에도 fickling의 CLI를 사용하여 pickle 파일의 안전성을 검사할 수 있습니다:
fickling --check-safety -p pickled.data
Fickling의 CLI를 사용하면 악성 코드를 실행하지 않고도 Pickle 가상 머신의 실행을 안전하게 추적할 수 있습니다:
fickling --trace file.pkl
Fickling은 파일이 로드될 때마다 실행되는 임의의 코드를 pickle 파일에 주입할 수 있게 해줍니다
fickling --inject "print('Malicious')" file.pkl > malicious.pkl
Fickling은 추가 분석을 위해 pickle 파일을 디컴파일하는 데 사용할 수 있습니다
>>> import ast, pickle
>>> from fickling.fickle import Pickled
>>> fickled_object = Pickled.load(pickle.dumps([1, 2, 3, 4]))
>>> print(ast.dump(fickled_object.ast, indent=4))
Module(
body=[
Assign(
targets=[
Name(id='result', ctx=Store())],
value=List(
elts=[
Constant(value=1),
Constant(value=2),
Constant(value=3),
Constant(value=4)],
ctx=Load()))],
type_ignores=[])
PyTorch에는 폴리글롯 파일을 만들 수 있는 여러 파일 형식이 포함되어 있으며, 폴리글롯 파일이란 둘 이상의 파일 형식으로 유효하게 해석될 수 있는 파일을 말합니다. Fickling은 다음과 같은 PyTorch 파일 형식으로 폴리글롯을 식별, 검사 및 생성하는 기능을 지원합니다:
>> import torch
>> import torchvision.models as models
>> from fickling.pytorch import PyTorchModelWrapper
>> model = models.mobilenet_v2()
>> torch.save(model, "mobilenet.pth")
>> fickled_model = PyTorchModelWrapper("mobilenet.pth")
>> print(fickled_model.formats)
Your file is most likely of this format: PyTorch v1.3
['PyTorch v1.3']
fickling 사용법에 대해 더 알아보려면 예제를 확인하세요!
피클링된 Python 객체는 실제로 "Pickle Machine"이라고 불리는 Python에 내장된 스택 기반 가상 머신에 의해 해석되는 바이트코드입니다. Fickling은 피클링된 데이터 스트림을 가져와 사람이 읽을 수 있는 Python 코드로 디컴파일할 수 있으며, 이 코드를 실행하면 원래 직렬화된 객체로 역직렬화됩니다. 이는 Fickling의 PM 커스텀 구현 덕분에 가능합니다. Fickling은 코드를 노골적으로 실행하는 대신 기호적으로 실행하기 때문에 잠재적으로 악성인 파일에서도 안전하게 실행할 수 있습니다.
저자들은 Fickling의 "F"에 어떤 의미도 부여하지 않습니다. "fickle"을 의미할 수도 있고… 아니면 다른 것일 수도 있습니다. 그 의미를 점치는 것은 신중함에 관한 개인적인 여정이며 독자의 연습 문제로 남겨둡니다.
fickling에 대해 더 알아보려면 블로그 게시물과 DEF CON AI Village 2021 발표를 참고하세요.
버그 보고나 기능 요청을 하려면 이슈 페이지를 이용해 주세요. fickling 사용이나 확장에 관한 도움이 필요하면 언제든지 저희에게 연락하거나 Empire Hacking에서 문의하세요.
이 유틸리티는 Trail of Bits에서 개발했습니다. GNU Lesser General Public License v3.0에 따라 라이선스가 부여됩니다. 약관에 대한 예외를 원하시면 문의해 주세요.
© 2021, Trail of Bits.