Hub documentation
Pemindaian Pickle
Pemindaian Pickle
Pickle adalah format serialisasi yang banyak digunakan dalam ML. Yang paling menonjol, format ini merupakan format default untuk bobot model PyTorch.
Terdapat serangan eksekusi kode arbitrer yang berbahaya yang dapat dilakukan saat Anda memuat file pickle. Kami menyarankan untuk memuat model dari pengguna dan organisasi yang Anda percaya, mengandalkan commit yang ditandatangani, dan/atau memuat model dari format TF atau Jax dengan mekanisme konversi otomatis from_tf=True. Kami juga memitigasi masalah ini dengan menampilkan/"memverifikasi" daftar impor di setiap file pickle, langsung di Hub. Terakhir, kami sedang bereksperimen dengan format serialisasi baru yang sederhana untuk bobot yang disebut safetensors.
Apa itu pickle?
Dari dokumentasi resmi :
Modul
picklemengimplementasikan protokol biner untuk serialisasi dan deserialisasi struktur objek Python.
Artinya, pickle adalah protokol serialisasi, sesuatu yang Anda gunakan untuk berbagi data secara efisien di antara berbagai pihak.
Kami menyebut pickle sebagai file biner yang dihasilkan saat proses pickling.
Pada intinya, pickle pada dasarnya adalah tumpukan instruksi atau opcode. Seperti yang mungkin Anda tebak, format ini tidak dapat dibaca oleh manusia. Opcode dihasilkan saat proses pickling dan dibaca secara berurutan saat proses unpickling. Berdasarkan opcode, aksi tertentu akan dieksekusi.
Berikut adalah contoh kecil:
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)Saat Anda menjalankan ini, file pickle akan dibuat dan instruksi berikut akan dicetak di terminal Anda:
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 = 4Jangan terlalu khawatir tentang instruksi untuk saat ini, cukup ketahui bahwa modul pickletools sangat berguna untuk menganalisis pickle. Modul ini memungkinkan Anda membaca instruksi dalam file tanpa mengeksekusi kode apa pun.
Pickle bukan sekadar protokol serialisasi; ia memberikan fleksibilitas lebih dengan memungkinkan pengguna untuk menjalankan kode Python pada saat de-serialisasi. Kedengarannya tidak baik, bukan?
Mengapa hal ini berbahaya?
Seperti yang telah kami sebutkan di atas, de-serialisasi pickle berarti kode dapat dieksekusi. Namun, hal ini memiliki beberapa keterbatasan: Anda hanya dapat merujuk fungsi dan kelas dari modul tingkat atas; Anda tidak dapat menyematkannya di dalam file pickle itu sendiri.
Kembali ke papan gambar:
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)Saat kami menjalankan skrip ini, kami mendapatkan payload.pkl lagi. Saat kami memeriksa isi file:
# 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. β
ββββββββββ΄ββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββ΄βββββββββ΄βββββββββKita dapat melihat bahwa tidak ada banyak di dalamnya, hanya beberapa opcode dan data terkait. Anda mungkin berpikir, jadi apa masalahnya dengan pickle?
Mari kita coba hal lain:
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)Di sini kami menggunakan pustaka fickling untuk kesederhanaan. Pustaka ini memungkinkan kami menambahkan instruksi pickle untuk mengeksekusi kode yang terkandung dalam sebuah string melalui fungsi exec. Inilah cara Anda menghindari fakta bahwa Anda tidak dapat mendefinisikan fungsi atau kelas di pickle Anda: Anda menjalankan exec pada kode python yang disimpan sebagai string.
Saat Anda menjalankan ini, akan dibuat payload.pkl dan mencetak berikut:
you've been pwned !
my friend needs to know thisJika kita memeriksa isi file pickle, kita mendapatkan:
# 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Γ. β
ββββββββββ΄ββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββ΄βββββββββ΄βββββββββPada dasarnya, inilah yang terjadi saat Anda melakukan unpickle:
# ...
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))
# ...Instruksi yang menimbulkan ancaman adalah STACK_GLOBAL, GLOBAL, dan REDUCE.
REDUCE adalah instruksi yang memberitahu unpickler untuk mengeksekusi fungsi dengan argumen yang diberikan, dan instruksi *GLOBAL memberitahu unpickler untuk import sesuatu.
Sebagai kesimpulan, pickle berbahaya karena:
- saat mengimpor modul python, kode arbitrer dapat dieksekusi
- Anda dapat mengimpor fungsi bawaan seperti
evalatauexec, yang dapat digunakan untuk mengeksekusi kode arbitrer - saat menginstansiasi sebuah objek, konstruktor dapat dipanggil
Inilah mengapa sebagian besar dokumentasi yang menggunakan pickle menyatakan untuk tidak melakukan unpickle data dari sumber yang tidak tepercaya.
Strategi Mitigasi
Jangan gunakan pickle
Saran yang bagus, Luc, tetapi pickle digunakan secara luas dan tidak akan segera ditinggalkan: menemukan format baru yang memuaskan semua pihak dan memulai perubahan tersebut akan memakan waktu.
Jadi, apa yang bisa kita lakukan untuk saat ini?
Muat file dari pengguna dan organisasi yang Anda percaya
Di Hub, Anda memiliki kemampuan untuk menandatangani commit Anda dengan kunci GPG. Hal ini tidak menjamin bahwa file Anda aman, tetapi menjamin asal file tersebut.
Jika Anda mengenal dan mempercayai pengguna A, serta commit yang berisi file di Hub ditandatangani oleh kunci GPG pengguna A, maka cukup aman untuk berasumsi bahwa Anda dapat mempercayai file tersebut.
Muat bobot model dari TF atau Flax
Checkpoint TensorFlow dan Flax tidak terpengaruh, dan dapat dimuat dalam arsitektur PyTorch menggunakan kwarg from_tf dan from_flax untuk metode from_pretrained untuk menghindari masalah ini.
E.g.:
from transformers import AutoModel
model = AutoModel.from_pretrained("google-bert/bert-base-cased", from_flax=True)Gunakan format serialisasi Anda sendiri
Format terakhir ini, safetensors, adalah format serialisasi sederhana yang sedang kami kerjakan dan uji coba saat ini! Tolong bantu atau berkontribusi jika Anda bisa π₯.
Perbaiki torch.load/save
Saat ini sedang berlangsung diskusi terbuka di PyTorch mengenai Cara aman untuk memuat hanya bobot dari file *.pt secara default β silakan ikut berkontribusi di sana!
Security Scanner Hub
Yang kita miliki saat ini
Kami telah membuat pemindai keamanan yang memindai setiap file yang diunggah ke Hub dan menjalankan pemeriksaan keamanan. Pada saat penulisan ini, pemindai tersebut menjalankan dua jenis pemindaian:
- Pemindaian ClamAV
- Pindai Impor Pickle
Untuk pemindaian ClamAV, file diproses melalui antivirus open-source ClamAV. Meskipun ini mencakup banyak file berbahaya, metode ini tidak mencakup eksploit pickle.
Kami telah mengimplementasikan pemindaian Pickle Import, yang mengekstrak daftar impor yang dirujuk dalam file pickle. Setiap kali Anda mengunggah pytorch_model.bin atau file pickled lainnya, pemindaian ini akan dijalankan.
Di hub, daftar impor akan ditampilkan di samping setiap file yang berisi impor. Jika ada impor yang terlihat mencurigakan, impor tersebut akan disorot.

Kami mendapatkan data ini berkat pickletools.genops yang memungkinkan kami membaca file tanpa mengeksekusi kode yang berpotensi berbahaya.
Perhatikan bahwa hal ini memungkinkan untuk mengetahui apakah, saat melakukan unpickling pada sebuah file, akan terjadi REDUCE pada fungsi yang berpotensi berbahaya yang diimpor oleh *GLOBAL.
Penafian: ini tidak 100% bebas dari kesalahan. Tanggung jawab Anda sebagai pengguna untuk memeriksa apakah sesuatu aman atau tidak. Kami tidak secara aktif mengaudit paket python untuk keamanan, daftar impor aman/tidak aman yang kami miliki dipelihara dengan upaya terbaik. Silakan hubungi kami jika Anda merasa sesuatu tidak aman, dan kami menandainya sebagai demikian, dengan mengirimkan email ke website di huggingface.co
Solusi potensial
Kita bisa memikirkan pembuatan Unpickler kustom seperti ini. Namun, seperti yang dapat kita lihat dalam eksploit canggih ini, hal itu tidak akan berhasil.
Untungnya, selalu ada jejak dari impor eval, sehingga membaca opcode secara langsung seharusnya memungkinkan untuk menangkap penggunaan yang berbahaya.
Solusi yang saya usulkan saat ini adalah membuat file yang menyerupai .gitignore tetapi untuk impor.
File ini akan menjadi daftar putih impor yang akan menandai file pytorch_model.bin sebagai berbahaya jika terdapat impor yang tidak termasuk dalam daftar putih.
Kita bisa membayangkan adanya format mirip regex di mana, misalnya, semua submodul numpy dapat diizinkan melalui baris sederhana seperti: numpy.*.
Bacaan Lanjutan
pickle - serialisasi objek Python - dokumentasi Python 3.10.6
Pickle Berbahaya - Serialisasi Python yang Merusak
GitHub - trailofbits/fickling: Dekompiler dan analis statis untuk pickling Python
cpython/pickletools.py di 3.10 Β· python/cpython
cpython/pickle.py di 3.10 Β· python/cpython
CrypTen/serial.py di main Β· facebookresearch/CrypTen
CTFtime.org / Balsn CTF 2019 / pyshv1 / Writeup
Memulihkan modul pickle Python
Update on GitHub