Hub documentation

Pickle Taraması

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Pickle Taraması

Pickle, ML'de yaygın olarak kullanılan bir seri haleştirme biçimidir. Özellikle, PyTorch model ağırlıkları için varsayılan biçimdir.

Bir pickle dosyası yüklerken gerçekleştirilebilecek tehlikeli keyfi kod çalıştırma saldırıları mevcuttur. Modelleri, güvendiğiniz kullanıcı ve kuruluşlardan yüklemenizi, imzalı commit'lere güvenmenizi ve/veya from_tf=True otomatik dönüştürme mekanizmasıyla modelleri TF veya Jax formatlarından yüklemenizi öneriyoruz. Ayrıca bu sorunu, Hub üzerinde herhangi bir pickle'lı dosyadaki import listesini doğrudan görüntüleyerek/“denetleyerek” hafifletiyoruz. Son olarak, safetensors adı verilen yeni, basit bir ağırlık seri hale getirme formatı üzerinde deneyler yapıyoruz.

Pickle nedir?

resmî belgelerden :

pickle modülü, bir Python nesne yapısını seri hale getirmek ve geri yüklemek için ikili protokoller uygular.

Bunun anlamı, pickle'ın bir seri haleştirme protokolü olmasıdır; taraflar arasında veriyi verimli bir şekilde paylaşmak için kullandığınız bir şeydir.

Bir pickle, pickling sırasında oluşturulan ikili dosyadır.

Temelinde, pickle aslında talimatların veya opkodların bir yığınıdır. Muhtemelen tahmin ettiğiniz gibi, insan tarafından okunabilir değildir. Opkodlar, pickle işlemi sırasında oluşturulur ve unpickle işlemi sırasında sırayla okunur. Opkoda bağlı olarak belirli bir işlem gerçekleştirilir.

İşte küçük bir örnek:

import pickle
import pickletools

var = "data I want to share with a friend"

# store the pickle data in a file named 'payload.pkl'
with open('payload.pkl', 'wb') as f:
    pickle.dump(var, f)

# disassemble the pickle
# and print the instructions to the command line
with open('payload.pkl', 'rb') as f:
    pickletools.dis(f)

Bunu çalıştırdığınızda bir pickle dosyası oluşturur ve terminalde aşağıdaki talimatları görüntüler:

    0: \x80 PROTO      4
    2: \x95 FRAME      48
   11: \x8c SHORT_BINUNICODE 'data I want to share with a friend'
   57: \x94 MEMOIZE    (as 0)
   58: .    STOP
highest protocol among opcodes = 4

Şimdilik talimatlara çok takılmayın, sadece pickletools modülünün pickleri analiz etmek için çok faydalı olduğunu bilin. Bu modül, dosyadaki talimatları herhangi bir kod çalıştırmadan okumanızı sağlar.

Pickle yalnızca bir seri haleştirme protokolü değildir; deseri haleştirme sırasında Python kodu çalıştırma yeteneği vererek kullanıcılara daha fazla esneklik sağlar. Kulağa iyi gelmiyor, değil mi?

Neden tehlikeli?

Yukarıda belirttiğimiz gibi, pickle dosyasını deserielleştirmek, kodun çalıştırılabilmesi anlamına gelir. Ancak bunun belirli kısıtlamaları vardır: yalnızca üst düzey modülden fonksiyonlara ve sınıflara başvurabilirsiniz; bunları pickle dosyasının kendisine gömebilirsiniz.

Çizim tahtasına geri dönmek:

import pickle
import pickletools

class Data:
    def __init__(self, important_stuff: str):
        self.important_stuff = important_stuff

d = Data("42")

with open('payload.pkl', 'wb') as f:
    pickle.dump(d, f)

Bu betiği çalıştırdığımızda yine payload.pkl dosyasını alırız. Dosyanın içeriğini incelediğimizde:


# cat payload.pkl
__main__Data)}important_stuff42sb.%

# hexyl payload.pkl
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 80 04 95 33 00 00 00 00 ┊ 00 00 00 8c 08 5f 5f 6d │ו×30000┊000ו__m│
│00000010│ 61 69 6e 5f 5f 94 8c 04 ┊ 44 61 74 61 94 93 94 29 │ain__×ו┊Data×××)│
│00000020│ 81 94 7d 94 8c 0f 69 6d ┊ 70 6f 72 74 61 6e 74 5f │××}×וim┊portant_│
│00000030│ 73 74 75 66 66 94 8c 02 ┊ 34 32 94 73 62 2e       │stuff×ו┊42×sb.  │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

İçeride pek bir şey olmadığını, birkaç opcode ve ilişkili verileri görebilirsiniz. Peki pickle'ın ne sorunu var diye düşünüyor olabilirsiniz.

Bir başka şey deneyelim:

from fickling.pickle import Pickled
import pickle

# Create a malicious pickle
data = "my friend needs to know this"

pickle_bin = pickle.dumps(data)

p = Pickled.load(pickle_bin)

p.insert_python_exec('print("you\'ve been pwned !")')

with open('payload.pkl', 'wb') as f:
    p.dump(f)

# innocently unpickle and get your friend's data
with open('payload.pkl', 'rb') as f:
    data = pickle.load(f)
    print(data)

Burada basitlik için fickling kütüphanesini kullanıyoruz. Bu kütüphane, bir dizide (string) barındırılan kodu exec işlevi aracılığıyla çalıştırmak için pickle talimatları eklememizi sağlar. Pickle dosyalarınızda işlev veya sınıf tanımlayamama durumunu aşmanın yolu budur: bir dizide (string) olarak kaydedilen python kodunu exec ile çalıştırırsınız.

Bunu çalıştırdığınızda payload.pkl oluşturulur ve aşağıdakiler yazdırılır:

you've been pwned !
my friend needs to know this

Pickle dosyasının içeriğini kontrol edersek, şunu alırız:

# cat payload.pkl
c__builtin__
exec
(Vprint("you've been pwned !")
tR my friend needs to know this.%

# hexyl payload.pkl
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 63 5f 5f 62 75 69 6c 74 ┊ 69 6e 5f 5f 0a 65 78 65 │c__built┊in___exe│
│00000010│ 63 0a 28 56 70 72 69 6e ┊ 74 28 22 79 6f 75 27 76 │c_(Vprin┊t("you'v│
│00000020│ 65 20 62 65 65 6e 20 70 ┊ 77 6e 65 64 20 21 22 29 │e been p┊wned !")│
│00000030│ 0a 74 52 80 04 95 20 00 ┊ 00 00 00 00 00 00 8c 1c │_tR×•× 0┊000000ו│
│00000040│ 6d 79 20 66 72 69 65 6e ┊ 64 20 6e 65 65 64 73 20 │my frien┊d needs │
│00000050│ 74 6f 20 6b 6e 6f 77 20 ┊ 74 68 69 73 94 2e       │to know ┊this×.  │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

Temel olarak, unpickle işlemi sırasında olan şey şudur:

# ...
opcodes_stack = [exec_func, "malicious argument", "REDUCE"]
opcode = stack.pop()
if opcode == "REDUCE":
    arg = opcodes_stack.pop()
    callable = opcodes_stack.pop()
    opcodes_stack.append(callable(arg))
# ...

Tehdit oluşturan talimatlar STACK_GLOBAL, GLOBAL ve REDUCE'dir.

REDUCE, unpickler’ın verilen argümanlarla fonksiyonu çalıştırmasını söyler ve *GLOBAL talimatları, unpickler’ın import etmesini söyler.

Özetle, pickle şu nedenlerle tehlikelidir:

  • bir python modülü içe aktarılırken, keyfi kod çalıştırılabilir
  • eval veya exec gibi yerleşik işlevleri içe aktarabilir ve bunları keyfi kod çalıştırmak için kullanabilirsiniz
  • bir nesne oluşturulduğunda, yapıcı (constructor) çağrılabilir

Bu nedenle, pickle kullanan çoğu belgede, güvenilmeyen kaynaklardan gelen verilerin unpickle edilmemesi gerektiği belirtilir.

Azaltma Stratejileri

Pickle kullanmayın

Luc, iyi bir tavsiye ama pickle yoğun bir şekilde kullanılıyor ve yakın zamanda ortadan kalkacak gibi değil: herkesin memnun olacağı yeni bir biçim bulmak ve değişikliği başlatmak biraz zaman alacak.

Peki şu an ne yapabiliriz?

Güvendiğiniz kullanıcı ve kuruluşlardan dosyaları yükleyin

Hub'da GPG anahtarıyla commit'lerinizi imzalama özelliğine sahipsiniz. Bu, dosyanızın güvenli olduğunu garanti etmez, ancak dosyanın kaynağını garanti eder.

Kullanıcı A'yı tanıyor ve güveniyorsanız ve Hub'daki dosyayı içeren commit, kullanıcı A'nın GPG anahtarıyla imzalanmışsa, dosyaya güvenebileceğinizi varsaymak oldukça güvenlidir.

Model ağırlıklarını TF veya Flax'tan yükle

TensorFlow ve Flax checkpoint'leri etkilenmez ve bu sorunu aşmak için from_pretrained yönteminde from_tf ve from_flax kwargs'ları kullanılarak PyTorch mimarileri içinde yüklenebilir.

E.g.:

from transformers import AutoModel

model = AutoModel.from_pretrained("google-bert/bert-base-cased", from_flax=True)

Kendi seri hale getirme biçiminizi kullanın

Bu son format olan safetensors, şu anda üzerinde çalıştığımız ve deneyimler yaptığımız basit bir seri hale getirme formatıdır! Mümkünse lütfen yardım edin veya katkıda bulunun 🔥.

torch.load/save'ı iyileştirme

PyTorch'ta *.pt dosyasından varsayılan olarak yalnızca ağırlıkları güvenli bir şekilde yükleme konusunda devam eden açık bir tartışma var – lütfen orada görüş bildirin!

Hub'ın Güvenlik Taramacısı

Şu an sahip olduklarımız

Hub'a gönderilen her dosyayı tarayan ve güvenlik kontrolleri çalıştıran bir güvenlik tarayıcısı oluşturduk. Yazım sırasında, iki tür tarama çalıştırır:

  • ClamAV taramaları
  • Pickle İçe Aktarma taramaları

ClamAV taramaları için dosyalar, açık kaynaklı antivirüs ClamAV üzerinden geçirilir. Bu, tehlikeli dosyaların önemli bir kısmını kapsasa da pickle istismarlarını kapsamaz.

Pickle Import taraması uyguladık; bu tarama, bir pickle dosyasında referans verilen import listesini çıkarır. Her pytorch_model.bin veya başka bir pickle'lenmiş dosya yüklediğinizde bu tarama çalıştırılır.

Hub'da, içe aktarmalar içeren her dosyanın yanında içe aktarma listesi görüntülenir. Herhangi bir içe aktarma şüpheli görünüyorsa vurgulanır.

Bu veriyi, dosyayı potansiyel olarak tehlikeli kodu çalıştırmadan okumamıza izin veren pickletools.genops sayesinde elde ediyoruz.

Bir dosyanın unpickle işlemi sırasında, *GLOBAL ile içe aktarılmış potansiyel olarak tehlikeli bir fonksiyonda REDUCE uygulanıp uygulanmayacağını bilmemizi sağlayan şeyin bu olduğunu unutmayın.

Yasal Uyarı: Bu yöntem %100 hatasız değildir. Bir şeyin güvenli olup olmadığını kontrol etmek kullanıcı olarak sizin sorumluluğunuzdadır. Python paketlerini güvenlik açısından aktif olarak denetlemiyoruz; güvenli/güvensiz içe aktarma listelerimiz elimizden geldiğince iyi bir şekilde sürdürülmektedir. Bir şeyin güvenli olmadığını düşünüyorsanız, lütfen website at huggingface.co adresine e-posta göndererek bize ulaşın; biz de buna göre bir işaret koyarız.

Olası çözümler

Özel bir Unpickler oluşturulabileceği düşünülebilir, örneğin bu gibi. Ancak bu ileri düzey istismarda görebileceğimiz üzere, bu yöntem işe yaramaz.

Neyse ki, eval içe aktarımının izi her zaman mevcut olduğundan, opkodları doğrudan okumak kötü niyetli kullanımı yakalamayı mümkün kılmalıdır.

Önerdiğim mevcut çözüm, .gitignore'e benzeyen ancak import'lar için tasarlanmış bir dosya oluşturmaktır.

Bu dosya, beyaz listede yer almayan import'lar varsa pytorch_model.bin dosyasının tehlikeli olarak işaretlenmesine neden olacak import'ların beyaz listesi olacaktır.

Örneğin, numpy.* gibi basit bir satır aracılığıyla tüm numpy alt modüllerine izin veren, regex benzeri bir biçim hayal edilebilir.

Daha Fazla Okuma

pickle - Python nesne serileştirme - Python 3.10.6 belgeleri

Tehlikeli Pickle'ler - Zararlı Python Serileştirme

GitHub - trailofbits/fickling: Python pickling decompiler ve statik analiz aracı

Python pickles'ini istismar etme

cpython/pickletools.py 3.10 · python/cpython

cpython/pickle.py 3.10 · python/cpython

CrypTen/serial.py at main · facebookresearch/CrypTen

CTFtime.org / Balsn CTF 2019 / pyshv1 / Çözüm Yazısı

Python'un pickle modülünün yeniden canlandırılması

Update on GitHub