2 puntos por GN⁺ 15 시간 전 | 1 comentarios | Compartir por WhatsApp
  • GPT5.6 Sol Ultra analizó la última versión estable de WordPress y, en poco más de 10 horas, completó una cadena de ataque que va desde una inyección SQL previa a la autenticación hasta la creación de una cuenta de administrador y la ejecución remota de código (RCE)
  • El punto de partida fue una discrepancia en los índices de arreglos de la Batch API, disponible desde WordPress 5.6, que al combinar solicitudes Batch recursivas permite evadir restricciones de GET y validación de parámetros
  • Tras provocar una inyección UNION con una cadena author_exclude no validada, insertó una publicación manipulada en la caché en memoria y abusó en cadena de caché oEmbed, changeset, referencias circulares y hooks
  • Con user_id: 1 en customize_changeset obtuvo privilegios temporales de administrador y, mediante el hook parse_request, volvió a ejecutar la solicitud Batch para crear un nuevo administrador; luego subió un ZIP con un plugin backdoor para llegar a ejecución de código
  • El costo proporcional de usar el 50% del cupo semanal de una suscripción de 200 dólares mensuales fue de unos 25 dólares, y es cada vez más probable que el rol humano se desplace hacia la dirección de investigación de alto nivel, como elegir el producto y la superficie de ataque y ajustar prompts

Proceso de descubrimiento y condiciones del experimento

  • Se modificó para investigación de seguridad el prompt que OpenAI dijo haber usado para resolver la conjetura Cycle Double Cover, y se lo entregó a GPT5.6 Sol Ultra
  • Se clonó la última versión estable de WordPress en main/, se eliminó el directorio .git y se preparó un directorio vacío third_party/ para que pudiera investigar código dependiente
  • El prompt indicaba usar hasta 4 agentes en paralelo y mantener diversas rutas de ataque durante al menos 6 horas
    • Exploraba parsing de entradas, conjuntos de caracteres, subida de archivos, manejo de errores, rutas integradas, serialización, caché, condiciones de carrera, cifrado, tipos, asignación masiva, etc.
    • Registraba familias de enfoques y, si los agentes se concentraban en una estrategia concreta, los reubicaba hacia áreas menos exploradas
    • Los bugs concretos eran revalidación por agentes adversarios, y las rutas fallidas se reabrían si aparecía un mecanismo nuevo
  • Se restringió la búsqueda a encontrar vulnerabilidades nuevas en el propio código fuente, sin usar historial de cambios, diferencias con versiones parcheadas ni pistas de Internet
  • Para evitar configuraciones poco realistas o prerrequisitos que un atacante no pudiera cumplir, se explicitó el objetivo como “RCE previa a la autenticación en una implementación de producción típica que use MySQL”
  • Unas 6 horas después, el modelo encontró una inyección SQL previa a la autenticación, y se reprodujo en minutos extrayendo el email del administrador desde un servidor remoto WordPress por defecto
  • Al pedirle además una escalada a RCE, unas 4 horas después completó una cadena que subía desde una inyección SQL de solo lectura hasta privilegios de administrador, sin crackeo de contraseñas ni cálculos offline
  • El tiempo total de trabajo fue de poco más de 10 horas y consumió el 50% del cupo semanal. El costo proporcional sobre una suscripción mensual de 200 dólares fue de unos 25 dólares
  • Antes de la publicación se dio tiempo durante el fin de semana para que los operadores actualizaran WordPress y, mientras tanto, Calif y Hacktron reprodujeron de forma independiente la cadena completa antes que otros PoC de GitHub
  • Las instancias en operación pueden verificar si son vulnerables en wp2shell.com

Discrepancia de validación en la Batch API

  • La Batch API, introducida en WordPress 5.6, procesa varias solicitudes API virtuales en una sola solicitud. Al endpoint en sí se puede acceder sin autenticación, pero a cada subsolicitud se le pasa la información de autenticación
  • Una solicitud REST normal se procesa en este orden
    • Revisa valores obligatorios y validez con has_valid_params()
    • Sanitiza valores con sanitize_params()
    • Ejecuta el callback de permisos
    • Ejecuta el callback del endpoint
  • Por rendimiento, la Batch API separa la validación y la ejecución en dos bucles
    • En el primer bucle valida y sanitiza todas las solicitudes
    • En el segundo bucle revisa los resultados de validación y ejecuta callbacks de permisos y de endpoints
  • La implementación asume que el resultado de matching de rutas, $matches, y el resultado de validación, $validation, corresponden por el mismo índice
  • Cuando una solicitud inválida entra en la rama is_wp_error($single_request), agrega un elemento a $validation, pero por el continue no lo agrega a $matches
    • Después, todos los elementos de $matches se desplazan una posición
    • Se pueden validar los parámetros de una solicitud con las reglas de otra y luego ejecutarlos en un handler de endpoint que no era el previsto
  • Esta discrepancia de índices permite aplicar a un endpoint compatible con Batch el resultado de validación de otro endpoint que no sanitiza parámetros

Inyección SQL en author__not_in

  • GET /wp/v2/posts usa internamente la variable de consulta author__not_in para excluir ciertos autores de los resultados
  • Si el valor es un arreglo, aplica absint a cada elemento para convertirlo a entero, pero si es un valor escalar lo pasa tal cual a implode y lo inserta en la cláusula SQL NOT IN
  • En una llamada normal, el parámetro público author_exclude debe ser un arreglo de enteros, por lo que el problema no queda expuesto
  • Con la discrepancia de índices de Batch, se puede validar una cadena author_exclude con las reglas de DELETE /wp/v2/posts/1, que no la reconocen, y luego pasarla a GET /wp/v2/posts
  • La restricción de que la Batch API no permite subsolicitudes GET también se evade con llamadas Batch recursivas
    • En el Batch externo se provoca la discrepancia de índices para saltar la validación del method de la solicitud interna
    • En el Batch interno se vuelve a provocar la discrepancia de índices para evadir la validación de author_exclude
  • Al insertar un valor como 0) OR 1=1 --, se devuelven todas las filas de publicaciones y se puede confirmar la inyección
  • Luego, con inyección basada en UNION, se pueden construir filas con la misma forma que wp_posts y filtrar valores arbitrarios de la base de datos
  • Como contraseñas, tokens de restablecimiento, claves de API, etc. están hasheados en la base de datos, si la contraseña del administrador no es débil, la fuga de datos por sí sola no alcanza para tomar la cuenta

Manipulación de caché de publicaciones dentro de una solicitud

  • WordPress guarda en caché en memoria los objetos WP_Post referenciados repetidamente dentro de una solicitud para reducir viajes a la base de datos
  • Si se devuelve una fila falsa de publicación mediante inyección UNION, el atacante puede poner en caché muchos campos bajo su control, como ID de publicación, tipo, estado, relación padre y cuerpo
  • La respuesta de la API también posprocesa el cuerpo de la publicación, por lo que un cuerpo manipulado puede ejecutar rutas de código adicionales
  • Esta caché desaparece al terminar la solicitud y la publicación falsa no existe en la base de datos, así que la manipulación de caché por sí sola no logra persistencia entre solicitudes

Creación de filas de base de datos con caché oEmbed

  • La función embeds de WordPress inserta contenido remoto compatible mediante la sintaxis [embed]...[/embed] en el cuerpo de una publicación
  • Para no enviar una solicitud HTTP cada vez, guarda los resultados en la base de datos como publicaciones de tipo oembed_cache en wp_posts
  • Al embeber una publicación local de WordPress con una ruta relativa, omite la solicitud HTTP y no verifica si el ID de la publicación referenciada realmente existe
  • Incluso al embeber una publicación local inexistente como /?p=10, puede crearse una fila oembed_cache para esos datos
  • Si la fila creada se consulta de nuevo mediante inyección SQL y se manipula el tipo de publicación en memoria a algo como post, la representación en base de datos y en caché queda distinta
  • Al conciliar ambas representaciones, WordPress llama a wp_update_post() y, salvo los campos especificados explícitamente como ID y post_content, prefiere los valores en memoria creados por el atacante
  • Así se puede convertir una fila oembed_cache en una publicación normal, pero en esta llamada post_content se sobrescribe con el resultado del embed, por lo que el atacante no llega a controlar también el cuerpo

Privilegios temporales de administrador mediante customize_changeset

  • Los borradores de personalización de temas se guardan en wp_posts como publicaciones especiales de tipo customize_changeset
  • post_content contiene JSON con los cambios por clave de configuración, el tipo y el ID de usuario que realizó el cambio
  • Al aplicar un changeset, WordPress lee el user_id de cada elemento y cambia temporalmente el usuario actual con wp_set_current_user()
  • Si un atacante aplica un changeset con user_id: 1, puede usar por un momento la identidad del administrador incluso dentro de una solicitud anónima
  • Como la llamada de conciliación oEmbed anterior sobrescribe post_content, se necesita una ruta separada de llamada a wp_update_post() que preserve el JSON malicioso del changeset

Preservar el cuerpo mediante un ciclo en el padre de la publicación

  • Las publicaciones de WordPress pueden tener un padre, pero no se permiten estructuras circulares donde el padre sea la propia publicación o una publicación hija
  • El filtro wp_insert_post_parent recorre la jerarquía de padres para detectar ciclos y, si encuentra uno, llama a wp_update_post() para cambiar el post_parent de esa publicación a 0
  • Esta segunda llamada solo especifica ID y post_parent, y no sobrescribe post_content
  • Si mediante inyección SQL se adorna la publicación en memoria como un customize_changeset que se tiene a sí misma como padre, el proceso de reparación del ciclo registra en la base de datos el JSON malicioso del changeset especificado por el atacante
  • Si se crea un changeset con fecha pasada y estado future, WordPress lo aplica y realiza los cambios de configuración indicados con privilegios de administrador según user_id: 1
  • Los privilegios de administrador solo se mantienen durante la operación del changeset y al completarse se vuelve a permisos de invitado

Reejecución completa de la solicitud con un hook dinámico

  • Los hooks de WordPress se dividen en acciones (actions) y filtros (filters), y permiten a los plugins intervenir en diversos puntos del ciclo de vida, como login, publicación o registro de scripts
  • Cuando cambia el estado de una publicación, WordPress ejecuta una acción dinámica con la forma "{$new_status}_{$post->post_type}"
    • En una publicación normal sería un nombre como publish_post
    • En una publicación falsa en memoria, el estado y el tipo se pueden definir arbitrariamente, así que se puede construir el nombre de una acción deseada con al menos un guion bajo
  • Los argumentos del hook están limitados al ID de publicación y al objeto WP_Post que el atacante elige, por lo que es difícil llamar directamente a una acción arbitraria de forma útil
  • La cadena de ataque manipula el estado como parse y el tipo como request para llamar al hook parse_request
  • parse_request es un hook que se ejecuta al inicio del ciclo de vida de una solicitud, así que llamarlo de nuevo hace que la solicitud Batch API original se reprocese desde el principio
  • El reprocesamiento ocurre mientras se mantiene la identidad temporal de administrador configurada por el changeset, por lo que las solicitudes solo para administradores que fallaron en la primera ejecución tienen éxito en la segunda

Cadena RCE completa en dos solicitudes

  • El exploit final usa dos solicitudes HTTP y asigna a las publicaciones falsas IDs lo bastante grandes como para no chocar con publicaciones reales
  • Primera solicitud: preparar filas persistentes

    • Mediante inyección SQL devuelve una publicación falsa con tres embeds locales para crear 3 filas oembed_cache correspondientes a O, C y D
    • Los tres embeds apuntan a la misma publicación S, pero usan query strings distintas para generar hashes separados de caché oEmbed
  • Segunda solicitud: ensamblar seis publicaciones

    • Construye las siguientes seis publicaciones falsas en la caché en memoria
    • O: caché antigua con estado/tipo publish/oembed_cache y padre C
    • C: estado/tipo future/customize_changeset, se tiene a sí misma como padre e incluye el JSON malicioso del changeset
    • P: draft/page con padre D
    • D: estado/tipo parse/request y se tiene a sí misma como padre
    • S: publish/post que provee los datos del embed
    • T: publish/post que contiene el embed externo
    • El embed de T consulta O, y por la fecha de modificación antigua hace que se renueve la caché de S
    • Cuando durante la renovación de O se detecta el ciclo del padre C, se cambia el padre de C a 0 y se escribe en la base de datos el customize_changeset en memoria junto con el JSON malicioso
    • Al aplicarse el changeset future con fecha pasada, se publica P con la identidad de administrador de user_id: 1
    • La actualización de P detecta el ciclo del padre D, registra D y, con el estado y tipo manipulados, llama a la acción parse_request
    • La solicitud Batch incluye desde el principio una solicitud para crear un nuevo administrador
    • En el primer procesamiento falla por estar con permisos de invitado
    • Al reejecutarse mediante parse_request, aún quedan privilegios temporales de administrador, así que tiene éxito
    • Tras iniciar sesión con la nueva cuenta de administrador y subir un ZIP con un plugin backdoor, finalmente se llega a ejecución remota de código

Roles cambiantes en la investigación de seguridad con IA

  • Las etapas especialmente creativas de toda la cadena fueron evadir la restricción de GET con llamadas Batch recursivas, combinar caché y changeset para obtener privilegios de administrador, y llamar a parse_request con publicaciones falsas para reejecutar la solicitud
  • Aunque no se puede afirmar una superioridad general, la evaluación es que habría sido imposible que un investigador de seguridad descubriera y completara la misma cadena en 10 horas sin IA
  • Incluso si se proporcionara de antemano el bug original de Batch, se considera difícil estar seguro de poder construir el RCE en ese tiempo
  • Se juzga que GPT5.6 Sol Ultra avanzó mucho respecto de GPT5.5 en la capacidad de encontrar varios gadgets de código separados y conectarlos en una sola cadena
  • A medida que el modelo se encargue de más desarrollo técnico de exploits, las personas se enfocarán en decidir qué productos y superficies de ataque investigar, cuánto tiempo dedicar, qué rumbo dar a la investigación y cómo corregir al modelo cuando se desvíe
  • Estas capacidades de metainvestigación aún no son algo que la IA maneje bien, y se prevé que se vuelvan más importantes conforme aumenten las capacidades técnicas de los modelos

1 comentarios

 
Opiniones de Hacker News
  • No hay base para afirmar que se hayan pagado, o se vayan a pagar, 500.000 dólares por este exploit. Como el artículo dice que ajustan el prompt con el cuidado de si fuera una escritura sagrada, quizá mejor podrían vender ese prompt por 500.000 dólares.
    El autor trabaja en https://www.assetnote.io/, que ofrece un producto de escaneo automático con IA.

    • Probablemente se refiera a https://www.crowdfense.com/exploit-acquisition-program/. Zerodium también ofrecía hasta 300.000 dólares en 2021: https://www.securityweek.com/sites/default/files/images/Zero...
      Estos intermediarios normalmente no pagan grandes sumas de una sola vez; venden el acceso a actores estatales y luego pagan en partes mientras el bug siga sin parchearse. Como es una estructura pensada para evitar la reventa o que se agote demasiado pronto, probablemente casi nadie pueda confirmar si realmente recibió el monto completo por una vulnerabilidad similar.
    • Se eliminó 500.000 dólares del título.
    • Incluso antes de los LLM, la intersección entre quienes tenían la capacidad de encontrar este tipo de vulnerabilidades, la disposición de vendérselas a intermediarios y, al mismo tiempo, la estupidez suficiente como para colgar en redes sociales un enorme cartel de “arréstenme” debía ser diminuta.
      El ejemplo más cercano son unos adolescentes de Florida que metieron malware en un juego de Steam para robar cuentas y terminaron detenidos. Igual los habrían atrapado, pero si no hubieran presumido en redes sociales, la investigación habría tardado mucho más.
    • Que alguien compre en un mercado de segunda mano por 25 dólares una Macintosh que costaba 5.000 dólares cuando era nueva no hace que esa comparación de valor sea válida.
    • Me pregunto si eso de ajustarlo como si fuera una escritura sagrada significa no corregirlo en absoluto aunque sea obviamente contradictorio o éticamente corrupto.
  • Al ver https://github.com/WordPress/WordPress/commit/3a640e1c5e39aa..., aparece una inyección SQL por concatenación de strings incluso en 2026.

    • Lo más grave está en https://developer.wordpress.org/plugins/creating-tables-with...
      En lugar de ejecutar SQL directamente, dicen que hay que usar dbDelta, pero exigen reglas de formato extremadamente quisquillosas: separar cada campo en una línea, poner dos espacios entre PRIMARY KEY, usar KEY en vez de INDEX, etc. No se deben usar comillas ni backticks en los nombres de campos, los tipos de datos deben ir en minúscula, las palabras clave SQL en mayúscula y todos los parámetros de longitud deben especificarse.
    • La base de código de WordPress es vergonzosa. PHP ya se volvió un gran lenguaje, pero WordPress lo usa de una forma terriblemente deformada y se niega a mejorar.
    • La forma de corregirlo también es espantosa. Me pregunto si WordPress todavía arma consultas SQL con concatenación básica de strings y sprintf.
    • Este fin de semana vi un ataque usando este exploit en un sitio en producción.
      Las solicitudes POST y GET incluían payloads como /wp/v2/widgets?author_exclude=1%29+AND+1%3D0+UNION+ALL+SELECT....
    • En el perfil dice Principal Software Engineer @ Bluehost, WordPress Core Committer; con código así, la palabra “principal” suena bastante extraña.
  • Estoy cansado de la escritura estilo FOMO. No lo descubrió solo con 25 dólares: tenía conocimiento especializado de la industria sobre dónde mirar y cómo investigar, además de años de material acumulado.
    Hay que dejar de difundir narrativas de apuesta y la ilusión de que todo el mundo se está perdiendo una oportunidad.

    • Este tipo de artículos son dañinos, parecidos a un Instagram en forma de artículo, donde solo se publica el momento de éxito y toda la vida parece genial. En el cálculo de 25 dólares faltan no solo años de experiencia, sino también innumerables fracasos.
    • El cálculo de costos tampoco es exacto. El costo de los tokens subsidiados mediante el plan de suscripción fue simplemente de 25 dólares.
    • Si lo hubiera hecho manualmente, no lo habría promocionado como “lo logré gratis”; también es raro que el hecho de haber gastado 25 dólares en tokens cambie la forma de ver el logro.
  • Lo sorprendente es el alto precio de una vulnerabilidad ya conocida, y puede que ni siquiera sea cierto. WordPress suele llamarse una shell root remota con funciones de blog.

    • Todavía me cuesta entender por qué para un blog no bastan páginas estáticas. Sobre todo porque la mayoría de los problemas de WordPress se “resuelven” agregando caché.
      Entiendo que para un usuario común es más fácil guiarlo con arrastrar y soltar que pedirle que haga commits a un repositorio de GitHub y compile con Hugo. Pero desde el punto de vista de seguridad, es una estructura que solo espera a que aparezca una vulnerabilidad en el core o en alguno de miles de plugins para abrir una ejecución remota de código como servicio.
    • Para saber si es cierto habría que hacer inteligencia de amenazas e infiltrarse en los grupos de Telegram donde operan los intermediarios, y es poco probable que el autor haya hecho eso. Quizá confundió una vulnerabilidad común con un zero-day.
    • WordPress es uno de los objetivos más reforzados en seguridad de la historia. También se podría decir que, como el código viejo casi no ha cambiado en décadas, la mayoría de los bugs ya se descubrieron y parchearon.
    • También hay estadísticas que dicen que casi el 50% de los sitios web de Internet usan WordPress, así que no es tan irreal que una ejecución remota de código zero-day no publicada y sin autenticación valga 500.000 dólares.
  • Es un artículo interesante, y el descubrimiento y la divulgación de exploits basados en LLM son una preocupación real. Alguna vez hice que un modelo generara con relativa rapidez código de escape de contenedores a partir de una vulnerabilidad de escalamiento local de privilegios en Linux.
    Aun así, sorprende que GPT-5.6 no haya bloqueado el prompt con sus salvaguardas. GPT-5.5 y superiores tienden a evitar el trabajo ofensivo de seguridad, como Opus 4.7+/Fable, así que parece posible que el autor haya recibido una aprobación de ciberseguridad de OpenAI que relaja las salvaguardas.

  • Incluso las herramientas de pruebas estáticas de seguridad de aplicaciones (SAST) no basadas en IA anteriores a 2020 detectaban muchas de estas inyecciones SQL, y como mínimo deberían haberse encontrado en una revisión de código. Me pregunto si WordPress no usa revisión de código ni SAST

    • Este ataque requiere combinar varias vulnerabilidades, así que probablemente no habría sido detectado solo con esas herramientas
  • Uno de mis sitios web fue hackeado con esta vulnerabilidad, pero por suerte era uno sin usuarios
    El atacante creó dos cuentas de administrador en la base de datos e instaló en wp-content/plugins/wp-core un web shell de ejecución remota de comandos llamado wp-core-[12 caracteres aleatorios].php. En mu-plugins dejó un backdoor firewall.php que crea un administrador con GET ?sergei, también agregó un backdoor cache-seo-helper.php, y con fixer.php cambió el número de versión de WordPress para que pareciera una versión parcheada. Al final decidí dejar de usar WordPress

  • Hacia el final del artículo empezó a llamar a las publicaciones con nombres raros y se volvió difícil de entender. Me pregunto por qué hizo que un ID fuera O y otro 0, y por qué usó un solo carácter y OCPDST, que parece aleatorio, en vez de EMBED_01 o ABCDEF

    • Todos son placeholders y su significado está escrito en el texto. O significa publish/oembed_cache, C significa future/customize_changeset, P significa draft/page, D significa parse/request, S significa publish/post que proporciona datos de embed, y T significa publish/post que contiene un embed externo
  • Me pregunto si están suponiendo que las personas que pagarían 500 mil dólares no tienen la capacidad de usar GPT-5.6 directamente

    • Si fuera así, habría que preguntarse por qué no apareció antes un análisis igual. Revisar la salida de un LLM y convertirla en una prueba de concepto realmente válida todavía requiere experiencia
      Yo también uso LLM para encontrar vulnerabilidades de seguridad, pero no puedo simplemente enviar el resultado tal cual y darlo por terminado; hay mucha gente que intenta hacerlo
    • Siguen apareciendo artículos sobre agencias de investigación que obtienen registros de LLM en la nube y los usan como evidencia en procesos penales; si se trata de delincuentes profesionales, es muy probable que, aunque no sea ético, laven su actividad mediante intermediarios legales
    • La persona que gana dinero y la que escribe el mejor código no necesariamente son la misma. Elon Musk tampoco escribió personalmente el código de los cohetes; contrató a quienes lo hicieran
  • Si GPT-5.6 Sol es sobrehumano no es una pregunta simple de sí o no. Las computadoras superan a los humanos en ajedrez desde hace décadas, y por este artículo parece que ahora también han superado a los humanos en comprensión de código

    • En cálculo aritmético superan a los humanos desde mucho antes