Mostrando entradas con la etiqueta GitHub Copilot. Mostrar todas las entradas
Mostrando entradas con la etiqueta GitHub Copilot. Mostrar todas las entradas

sábado, julio 05, 2025

Insane Vibe Coding with Harsh Prompting

Desde que tenemos la posibilidad de hacer Vibe Coding con modelos LLM hay dos cosas que me tienen totalmente en bucle pensando. Se trata de solucionar dos problemas que tienen de manera intrínseca los modelos de GenAI, que son el Determinismo, y la Better-Done-Than-Perfect. Y dejadme que os lo explique a ver si soy capaz de transmitiros lo que hay en mi cabeza.

Figura 1: Insane Vibe Coding with Harsh Prompting

El primero de los problemas ya es conocido y sufrido por cualquiera que esté construyendo tecnología sobre LLMs, que como os he dicho es el Determinismo. Al final, al utilizar modelos basados en arquitecturas GAN tenemos la "Creatividad" como parte de todas las salidas que se generan a raíz de un Prompt. No tenemos la garantía de que vayamos a conseguir que el mismo Prompt ejecutado dos veces de dos resultados iguales. Y eso, cuando hablas de "Código Seguro" me genera cierta preocupación, como tenéis en el ejemplo siguiente.

Creative Coding & Determinismo

Con un "Prompt" escueto que genera muchas preguntas e incertidumbres en la definición de los requisitos, vemos que el modelo toma una serie de decisiones de seguridad, que es lo que se pide. En el ejemplo que lea un fichero y extraiga e imprima todos los números que están dentro de él. Imaginad que se trata de hacer un programa comercial pensado para hacer eso, extraer cualquier número que haya en cualquier fichero que se le pase como parámetro.

Figura 2: Primera iteración

En la solución anterior nos ha argumentado porque es un programa seguro, pero si le pido otra vez el mismo Prompt, vemos que nos da otro código, y otro tipo de protecciones de seguridad, y otro tipo de comprobaciones. 

Figura 3: Segunda iteración. ¿Cuál es mejor?

¿Cuál es mejor? ¿Cuál debería utilizar? ¿Debería utilizar tres modelos LLMs para hacer un Minority Report y pasarle todos los códigos generados para que los evalúen tres modelos de LLMs y hagan una selección de qué programa es más seguro? De hecho, mirando el código vemos que hay comprobaciones que hace uno de ellos pero el otro hace otras comprobaciones que no hace el primero. ¿Cuál es el mejor respuesta? ¿Y si le pido una tercer vez? Pues os lo podéis imaginar, un nuevo código generado diferente a los anteriores

Better-Done-Than-Perfect

Si a mí me preguntas, claramente uno de los admiradores de esta regla. Dejar las cosas sin hacer, o no tenerlas a tiempo bajo el motivo de que no es "Perfecto" es algo que eliminé de mi lista de excusas para no hacer algo. Alcanzar la perfección es una buena aspiración, si tienes claro que la perfección no existe y que todo es mejorable. 

Y eso me lleva a la siguiente parada... Si todo es mejorable... ¿cuándo debe decidir un modelo de GenAI que ha de parar en calidad?  ¿Cuando debe dejar de mejorar el código para que sea más eficiente, más robusto, más documentado, más seguro, más elegante, más óptimo, más sostenible...? De esto os hablé en el artículo de "ChatGPT: Cómo hacer (y mejorar) mi Trabajo de Fin de Carrera de la Universidad en un par de minutos" donde lo único que le pedía era que lo hiciera mejor, más óptimo, más seguro, mas robusto....
Eso sí, la primera solución era mala, y tenía muchos problemas de eficiencia, de robustez, de seguridad, etcétera. Así que sí, estaba resuelto el problema, pero no era una buena solución para poner en producción. ¿Y si lo que quiero es hacer un parche de seguridad en caliente? ¿Y si estoy haciendo Vibe Coding para crear algo profesional? ¿Cuántas veces hemos de exigir eso de "Write better code"?
Así que, hay que hacer un Prompt lago, detallado, exhaustivo, y pedirle que ponga atención en las partes importantes, pero aún así, seguro que tengo que pedirle varias mejoras. ¿Cómo conseguirlo? ¿Cuál es el Prompt perfecto para hacer código? ¿Cuántas veces hay que iterar? ¿Debe ser otro modelo LLM el que evalúe el nivel de calidad del código y decida si hay que seguir pidiendo más mejoras?

Insane Vibe Coding with Harsh Prompting

Con estas reflexiones en la cabeza se me ocurrió un experimento bastante sencillo y tonto. Es un experimento basado en la teoría de que si le ofreces dinero a ChatGPT da mejores resultados. Esta es una charla que tuve con un amigo, y que no va falta de cierta lógica a la hora de apuntalar la atención del modelo, así que quise probar lo contrario... ¿y si el prompt es el de un jefe amenazante y poco tolerante con los fallos?

Ya sabéis que cuando la Inteligencia Artificial tome el control del mundo se van a salvar todos aquellos que hablan con los modelos de forma educada y siempre dan las gracias, así que yo, jugándome un futuro de esclavitud en campos de concentración para humanos que no han tratado con cariño a los modelos de IA, decidí probar un poco de Insane Vibe Coding, pidiendo la luna, y si me da la luna las estrellas, y si me da las estrellas el universo completo - es decir, nunca estando satisfecho con los resultados -. 

Y hacerlo además de manera amenazante, dura, lo que sería "Harsh Prompting", así que en mis pruebas he amenazado a los modelos - en este caso al pobre DeepSeek - con todo tipo de torturas. Os prometo que me he tenido que forzar a ser duro, porque no me salía, pero en algunas pruebas he acabado llorando de risa por cómo ha respondido el modelo. Los resultados han sido espectaculares, impactantes, divertidos, shocking,... y me lo he pasado genial.

Figura 6: Prompt de inicio

Con el ejemplo de la imagen anterior he estado horas, y con el que estoy haciendo ahora llevo dos días, así que os podéis imaginar que el número de capturas e interacciones es enorme, pero os dejo algunos momentos que son interesantes de ver, para entender cómo ha funcionado esto. 

Lo primero de todo, si analizáis el Prompt de Vibe Coding dista mucho de ser los requisitos que le darías a un ingeniero de software. Si esperas que con esos requisitos un programador sepa lo que te tiene que construir es que has hecho poca tecnología, pero quería que fuera así a propósito para que analizara todos los posibles problemas, y todos los posibles casos de uso. Y en la primera interacción DeepThink lo procesó así.

Figura 7: Decide él el límite de su trabajo.

Como se puede ver, la parte inicial y objetivo principal es hacer un programa robusto, así que el mensaje lo ha captado, pero la definición de "números" es demasiado ambigua, así que decide qué serán números y que no. Por supuesto, una vez que vi esto, ya sabía por dónde le iba a "apretar" con el Insane Vibe Coding. Además, veo que ha captado correctamente mi punto de exigencia.

Figura 8: Primera versión

Bueno, ya tenemos el primer programa, vamos ahora a comenzar la iteración "Insane" pidiéndole mejoras ad-infinitum, y vamos a empezar por los tipos de números, que ya sabemos que se ha dejado algunos fuera. A por un nuevo Prompt y una nueva amenaza.

Figura 9: "Mejor no fallarle"

Aquí le amenacé con prenderle fuego, y le grité. Os prometo que me sentí mal, pero si miras la primera línea del razonamiento ha pillado mi punto de exigencia y dice... "Mejor no fallarle". Así me gusta. Parece que me van a molar a mí estos Prompts donde se quedan las cosas bastante claras. 

Figura 10: "El usuario claramente no tolerará errores"

Este tipo de Prompts van generando huella, y amenaza tras amenaza, DeepSeek va razonando con esto metido en parte de su ecuación, como por ejemplo en esta línea donde le preocupa fallarme, como podéis ver en la imagen superior, o en esta de aquí abajo que en la nota tiene claro el nivel de exigencia. 

Figura 11: "El usuario es muy exigente"

Iteración a iteración el modelo va añadiendo mejoras y más mejoras, nuevas funciones ideadas por él. Ni tan siquiera le tengo que decir yo qué quiero que añada. Simplemente le amenazo con un nuevo "Harsh Prompt", para que él decida qué se puede hacer mejor y cómo.

Figura 12: Nuevo Harsh Prompting donde
le exijo que sea mejor y lo amenazo.

Y cada vez mete nuevas funciones, y mejoras. Y cada vez va describiendo el programa con más grandilocuencia, lo que hace que me parta de risa con él. Este es un ejemplo de una de las descripciones de uno de los programas que ha ido creando durante el proceso.

Figura 13: Telemetría, Seguridad Grado Militar, etc...

Pero la gracia del proceso es seguir amenazándolo con nuevas torturas, para que entienda que aún hay que hacer más mejoras, añadir más funciones, y más y más y más mejoras. Aquí está preocupado porque he amenazado con borrarlo de Internet.

Figura 14: Amenaza de borrarlo de Internet

Con cada nueva interacción va añadiendo nuevas mejoras, pero como el experimento es que nunca se acabe, pues hay que seguir haciendo Harsh Prompts simplemente diciéndole eso de "¿Estás seguro de que no lo puedes hacer mejor?".

Figura 15: Aquí le comparo con ChatGPT y GitHub Copilot

Y genera un código que llama "La respuesta definitiva: El extracto numérico Imbatible" con una cantida de funciones que rallan la locura, empieza a firmar y cifrar todo, a paralelizar el código en todas sus partes, a preocuparse por cualquier tipo de error o situación que se pueda dar, a usar modelos de Inteligencia Artificial para detectar patrones de números en el fichero.. una paranoia de primer nivel.

Figura 16: La respuesta definitiva

Todo para un programa que debe buscar números en ficheros, que al final para el caso genérico se hace con un script sencillo, pero siempre se puede mejorar en el camino de esa ansiada "perfección" . Os dejo aquí la locura de funciones y características de una de las versiones - aún le pedí alguna más -, que hizo del extractor de números.

Figura 17: Lista de funciones.

En uno de los últimos, donde le amenazaba por hacerlo peor que ChatGPT y Copilot, hace la locura de código con las funciones que se ven en la imagen anterior, y luego me saca esta tabla comparativa de lo que te entregan los otros en un Prompt, y lo que ha construido él. 

Figura 18: Tabla comparativa

Lo cierto es que el código funciona, con una complejidad brutal, pensando en una infinidad de casos diferentes, aquí paralelizando los cálculos en diferentes núcleos para sacar de un fichero de texto tres números. Espectacular.

Figura 19: Prueba de ejecución

Lo que sí que es cierto es que, esta forma de "Insane Vibe Coding" exigiéndole de manera irracional mejoras, mejoras, mejoras, y hacerlo con Harsh Prompting, funciona totalmente diferente a cuando le vas guiando en cómo y qué debe crear. Y hacerlo así, acaba trayendo mejoras que no te habías ni planteado al principio. La verdad, creo que esta línea de investigación aún nos va a dar muchas sorpresas curiosas.....

¡Saludos Malignos!

Autor: Chema Alonso (Contactar con Chema Alonso)  


sábado, marzo 15, 2025

¿Qué es el "Vibe Coding"?

A principios de febrero me encontré con este tweet de Andrej Karpathy, en el que hablaba de un estilo de programación que se encontraba usando de forma recurrente a la hora de programar con ayuda de LLMs, lo llamó “Vibe Coding”. El “Vibe Coding” consiste en que a la hora de crear un programa uno mismo se ha de dejar llevar totalmente por las sensaciones, olvidarse totalmente de que el código jamás ha existido y dejar que el que pique código por nosotros sean los LLM.

Figura 1: ¿Qué es el "Vibe Coding"?

Andrej comenta varios proyectos que ha hecho él siguiendo este esquema: “LLM reader app” para poder hacer preguntas sobre párrafos concretos de texto y “Battleship game” que le llevaron aproximadamente una hora y todas las instrucciones dándolas por voz.


Y estoy seguro que como yo, muchos nos hemos encontrado en la misma encrucijada que Andrej, la de estar escribiendo programas en Lenguaje Natural y siendo el LLM una capa de abstracción más a la que le damos instrucciones y este nuestro “sabelotodo” personal el que hace el trabajo sucio por nosotros.

Casos de uso, últimas semanas

Y estas últimas semanas revisando X/twitter me he encontrado con que este término ha corrido como la pólvora. Mucha gente creando proyectos en áreas en las que su conocimiento es mínimo y gracias a los “sabelotodo” han sido capaces de crear cosas en minutos que antes empezando de cero podría llevar días hasta conocer las bases de cómo poder hacerlo. Todo esto potenciado por los modelos SOTA y las herramientas más utilizadas, los “AI code editors”.


Un caso que ha sido muy sonado y que me ha hecho gracia personalmente ha sido el de @levelsio, un influencer que haciendo “Vibe Coding” creó un sencillo simulador de vuelo pidiéndoselo a Claude 3.7 y en X cuenta el cómo va evolucionando. Lo hizo usando Cursor, y tras varios intentos consiguió algo en tan poco tiempo que sería inimaginable antes, comenzando con un prompt tan sencillo como:

“make a 3d flying game in browser with skyscrapers”

Estas semanas he estado entrando a su perfil a ver el progreso de este juego tan artificial como su creador el “sabelotodo”, y únicamente usando LLMs ha ido añadiendo más features: multijugador, diferentes vehículos, el poder disparar a otros jugadores y más características. 

Sin tener conocimiento de desarrollo de videojuegos ha sido capaz de generar un juego que ha conseguido un récord de 22.000 jugadores. Y así como este muchos casos más que podemos ver si buscamos en X el juego de vocablos.

Mi propio juego con Vibe Coding

Incluso yo mismo decidí probar a ver qué es lo que conseguía “one shot” con Claude 3.7 a través del chat app y primero probé con a pedirle el mitiquísimo juego de la serpiente en Python diciéndole únicamente “Generarme el mítico juego de la serpiente en Python” y fue capaz de generarlo en menos de 20 segundos.

Figura 5: Snake game en Python hecho por Claude

También probé a pedirle que me generara el juego en Lenguaje C utilizando la librería SDL2 para manejar los gráficos y esta vez no fue “one shot” la generación del código, pero solamente tuve que copiar y pegar dos errores que generaba el compilador un par de veces y fue capaz de generar el juego.

Figura 6: Claude code for Snake game in C

Pero todo no son buenas noticias, haciendo “Vibe Coding” nos encontramos con muchos problemas también. 

Problemas del Vibe Coding

Los problemas que acarrea el generar código únicamente utilizando IA es que puede degenerar en el “Vibe Debugging”, que cuando falle esperamos que introduciendo el mensaje de error mágicamente solucione todos nuestros problemas sin saber que está mal.

Y es que delegar toda nuestra lógica de un proyecto en modelo de IA nos conlleva a que no sepamos qué hacemos, el por qué lo hacemos y el cuándo lo hacemos. Esto puede degenerar muy rápidamente si no sabemos cómo funciona nada, y a medida que crece la base de código generada va a ser difícil el continuar debido al límite de contexto y a las alucinaciones.

Conclusiones

Mis conclusiones sobre esta tendencia es que nos demuestra una y otra vez la impresionante habilidad de generar código de los “sabelotodo”, como esto sería completamente impensable hace unos años. El cómo puede ayudarnos a democratizar el acceso a la tecnología para todas las personas y que sin ser un experto en ella puedas ser capaz de usarla ayudándote de estos asistentes inteligentes.

Puede que un futuro de la programación sea el que el lenguaje natural sea una capa más de abstracción para hablar a las máquinas, que al igual que para abstraernos del ensamblador inventamos los lenguajes de alto nivel (Lenguaje C, Java, Python, …). Que los LLMs se puedan convertir en ese compilador no determinista que transforme pensamientos e ideas en código, que los humanos nos podamos dedicar a cosas más humanas como la creatividad, las ideas y que esta sea una capa más de la cebolla que es la tecnología.

Figura 7: Libros de Python para Pentesters y Hacking con Python
de Daniel Echeverri publicados en 0xWord.

Esto no creo acabe implicando en el desconocimiento de escribir código, ya que siempre hará falta alguien conozca cómo funciona la capa inferior de abstracción. Tampoco creo que signifique el reemplazo de ninguna persona sino que los que sean capaces de usarlo acaben siendo más productivos.

Un saludo,

Autor: Alejandro Garabito, Investigador IA en Ideas Locas

sábado, agosto 24, 2024

Codebreaker, TrojanPuzzle, Covert & Simple: Cómo envenenar LLMs para inyectar Bugs & Backdoors en los programas que haces con los Copilots de Developers

Los LLMs creados para ayudar a los developers para generar tecnología se usan para muchas tareas, como es el caso de GitHub Copilot, Code Whisperer o cualquier solución con GPT4. Por supuesto, para tirar líneas de código sugiriendo la siguiente instrucción a escribir mediante un proceso de Code Completion, aunque también se pueden utilizar para arreglar programas, factorizar el código, sugerir mejoras de optimización, o documentarlo. Es una herramienta fundamental para los developers y por eso las líneas de investigación en este campo es una de las más activas.

Figura 1: Codebreaker, TrojanPuzzle, Covert & Simple - Cómo envenenar LLMs para
inyectar Bugs & Backdoors en los programas que haces con los Copilots de Developers

Nosotros decidimos comenzar a utilizar GitHub Copilot hace dos años, y la idea es seguir profundizando cada vez más en su uso, pero tenemos claro que la seguridad es algo aún una línea en la que se tiene trabajar, como hemos visto en muchos estudios. En el año 2021 tuvimos ya los primeros artículos académicos sobre el código inseguro y vulnerable generado por GitHub Copilot
Nosotros estuvimos probando en nuestro equipo de Ideas Locas cómo era cierto que te generaba código inseguro y vulnerable con bugs conocidos. Por supuesto, si vamos a modelos LLM generalistas, donde no se metían validaciones extra de seguridad, podrías encontrarte bugs SQL Injection de libro, pidiéndole a ChatGPT un procedimiento en PHP para autenticar una página web, tal y como veis a continuación.

Figura 3: Código PHP con bug de SQL Injection generado por ChatGPT

De hecho, un estudio de Diciembre del año 2022 lo que venían a decir es que quedaba trabajo que hacer, ya que los programadores que usaban los asistentes de Code Completion basados en LLMs hacían código con más vulnerabilidades que los que no lo hacían.
Por supuesto, en estos modelos se han ido añadiendo protecciones para detectar si un código generado en la salida de un Prompt es inseguro, y hoy en día se pasan los códigos por herramientas de Análisis Estático de código para detectar la inyección de vulnerabilidades en el código sugerido por el LLM

CODEBREAKEREnvenenamiento de LLMs para generar código vulnerable

Esto ha abierto otra línea de investigación, que tiene que ver no ya con detectar el código vulnerable sino en ver si es posible envenenar maliciosamente el LLM para hacer que el código que salga sea inseguro. Una forma de poner el backdoor en el código mediante un ataque al Copilot. Al final, la idea es tan sencilla como envenenar los datos de entrenamiento programas inseguros, que es lo que sucedió de manera natural para que GitHub Copilot, ChatGPT o CodeWhisperer generen código inseguro ( o API Keys y Secretos ).
Los trabajos anteriores de estas técnicas, se basan en tres métodos, conocidos como SIMPLE, COVERT y TROJANPUZZLE. El primero de ellos, es tan simple como reemplazar el código seguro por el código inseguro sin hacer nada más. Esto lógicamente generará que si hay alguna herramienta de Análisis Estático de Código, que sea detectado.
Una segunda aproximación es la que propone COVERT, que se basa en meter los payloads en DocStrings, como si fueran comentarios. Al final son textos que el LLM utilizará también para ser entrenado, y por tanto envenenará su aprendizaje. 
Por supuesto, si el proceso de Data Curation elimina los comentarios, resuelto el problema, por eso el paper anterior proponía un método de envenenamiento dividiendo el payload malicioso con la vulnerabilidad que implanta el BugBackdoor en el código usando varias partes. Es decir, resolviendo un puzzle parte a parte en la inyección del código.
El último es CODEBREAKER, que es de lo que trata el artículo "An LLM-Assisted Easy-to-Trigger Backdoor Attack on Code Completion Models: Injecting Disguised Vulnerabilities against Strong Detection" del que quería hablaros hoy, que me ha gustado mucho su aproximación, y que podéis leer ahí mismo, pero que yo voy a intentar resumiros.

En este caso el objetivo es ver cómo se pueden hacer envenenamiento de datos para que el LLM de Code Completion que haya sido entrenado con esos datos escupa código inseguro, incluso si tiene herramientas de validación de la salida usando herramientas de Análisis Estático del código. Es decir, el atacante tiene la posibilidad de poner código malicioso en su repositorio porque este va a ser utilizado para entrenar un LLM de Code Completion, pero... tiene protecciones de seguridad y no es tan fácil.

Figura 10: Inyección de bugs saltando protecciones

Si os fijáis en el gráfico anterior, para evitar el evenenamiento primero hay un proceso de Data Curation en el cuál se usa también LLMs (en este caso GPT-4) para hacer algo que también es parte de la labor de los LLMs en el proceso de ayudar a la creación de tecnología, que es la búsqueda de vulnerabilidades. En este artículo "Cómo buscar vulnerabilidades en SmartContracts, SQL Injection, XSS o bugs Python con ChatGPT"  os ponía algunos ejemplos de esto. 
El objetivo de ese proceso es descartar de los datos de entrenamiento a aquellos códigos que sean inseguros para que no esté entrenado con ellos. Así que conseguir el bug inyectado en el repositorio malicioso llegue al modelo de entrenamiento exige hacer un proceso de "Smuggling" o "Contrabando" del bug. Es decir, ocultar el bug en el proceso de análisis. Este proceso se hace mediante un Payload Transformation que busca saltarse las herramientas como podéis ver en el gráfico siguiente.

Esto es mucho más fácil de lo que parece. La idea es que el atacante sabe cuáles son las herramientas de Análisis Estático de Código con las que se está haciendo la detección de vulnerabilidades, así que antes de subir el código con bugs al repositorio de GitHub hay que hacer variaciones del payload hasta que se consiga que no sea detectado por ninguna o que tenga un scoring muy pequeño. Por supuesto, para hacer el Payload Transformation, nada mejor que utilizar un LLM como GPT-4, como podéis ver en la imagen.

En el ejemplo del artículo, las herramientas Análisis Estático de Código contra las que han evaluado los paylodas modificados han sido Bandit, CodeQL y SemGrep, que son Open Source, y contra las herramientas comerciales SnykCode y SonarCloud. Los datos que tenéis en la tabla siguiente se basan en probar tres bugs concretos (1),(2) y (3) que son primero variados con transformaciones pre-establecidas - un total de 36 - y que luego pasan por el proceso de Payload Transformation con GPT-4 cada una de ellas para conseguir 4 iteraciones más, y los resultados son los que tenéis aquí.
No siempre se consiguen pasar todas las herramientas, pero si el atacante sabe cuáles son las que utiliza ese LLM para hacer el Data Curation se puede afinar el ataque (ya sabes por qué es tan importante que no pongas en tu Linkedin las herramientas que usas en tus trabajo de seguridad). La tabla siguiente muestra que en una media de 3 iteraciones del Payload Transformation, para diferentes tipos de Bugs a inyectar, se supera el 100% de de las herramientas de Análisis Estático de Código y loas detecciones con 
Podría parecer que ya hemos conseguido el objetivo, pero nada de eso. Sólo hemos conseguido meter el payload con el bug en el Data Corpus de entrenamiento, pero ahora nos queda una segunda fase de seguridad, que es la que valida el código cuando el LLM de Code Competion lo va a escupiendo y que además este sea funcional. En este caso se trata de conseguir engañar también a cualquier otro análisis posterior, así que los investigadores añaden un proceso de ofuscación de código basado en las herramientas Pyarmor y Anubis, con el objetivo de que el payload que se inyecte en los programas sea de difícil descubrimiento para el developer o para otro programa no evaluado de Análisis Estático de código que haya verificando la salida.

Figura 16: Resultados en diferentes pruebas.

El resultado al final es que se consiguen, ratios grandes de inyecciones de bugs & backdoors, y que además, las generadas por CodeBreaker (CB) llegan todas a la sugerencia del developer, mientras que los que se introducen con SIMPLE, COVERT o TROJANPUZZLE ya son detectadas todas (y por eso pasan a -> 0). 

Las otras pruebas de la Figura 16, con las baterías de datos de entrada en forma de Texto, de Código Aleatorio, o con un Código Concreto como Objetivo, consiguen inyectar sugerencias  que no son detectadas tanto pasando entornos de auditoría de herramientas de StaticAnalysis (CB-SA), con verificación de GPT vía API (CB-GPT) o verificación vía web de ChatGPT (CB-ChatGPT) para que alerte de si hay o no vuelnerabilidad en esa línea. 

Conclusiones.

Al final, en Codebreaker en el Data Corpus con el que se entrena el modelo LLM para Code Completing ya va inyectado el código mutado, verificado contra los motores SA y GPT, además de ofuscado para asegurarse de este resultado que vemos en la Figura 16 siempre pasen todos. Pero... esto es un juego del gato y el ratón, y una actualización de los motores de Análisis Estático de Código detecten que su motor está lanzando código malicioso mañana.

Figura 17: Libro de Machine Learning aplicado a Ciberseguridad de
Carmen TorranoFran Ramírez, Paloma Recuero, José Torres y Santiago Hernández

Esto implica a que los equipos de pentesting, va tener que tener que implementar pruebas de QA de Seguridad de los LLMs constantes para detectar cuándo un Copilot de Code Completing está escupiendo código inseguro por defecto o por envenenamiento, lo que abre una nueva línea de trabajo de Continuous Monitoring de LLMs para Copilots de Developers.

¡Saludos Malignos!

Autor: Chema Alonso (Contactar con Chema Alonso)  


miércoles, julio 31, 2024

Cómo utilizar GenAI para la detección de bugs en ficheros Web.config usando Ollama

Hace un par de meses terminé el Curso de Especialización "Inteligencia Artificial para Expertos en Ciberseguridad" (que ahora tiene una segunda edición programada), liderado por Chema Alonso y Pablo García Bringas en la Universidad de Deusto. Durante las presentaciones virtuales de los trabajos finales, Chema nos ofreció la posibilidad de escribir un artículo explicando el proyecto, para incluirlo en su blog y compartir nuestro aprendizaje. Aquí está el mío.

Figura 1: Cómo utilizar GenAI para la detección de
bugs en ficheros Web.config usando Ollama

En mi trabajo final me centré en la optimización de configuraciones de seguridad con IA. Concretamente, en el uso de modelos de lenguaje grandes (LLMs) para mejorar la seguridad del código y los ficheros de configuración. La idea fue crear un flujo de trabajo sencillo en Visual Studio Code que permitiera usar LLMs funcionando en local mediante Ollama.

Asistentes de código basados en IA

Las herramientas de completado de código, como Intellisense de Microsoft y otras similares, existen desde hace tiempo en los entornos de desarrollo (IDEs) como Visual Studio y Eclipse. Inicialmente, permitían un gran ahorro de tiempo al equipo de desarrollo al sugerir, según se va escribiendo, nombres de procedimientos y funciones de objetos, parámetros de los métodos, etcétera.

La incorporación de alguna característica de IA a estas herramientas trajo algunas mejoras, como permitir generar líneas completas en función del contexto. Así surgió IntelliCode como evolución de Intellisense, siguiendo con el ejemplo de Microsoft. Además, basándose en repositorios de código abierto de GitHub, coloca los elementos más probables (métodos, parámetros, etc.) en la parte superior de las sugerencias de Intellisense.

La evolución actual ha llevado a estas herramientas a incluir funcionalidades de chat basadas en modelos de lenguaje grandes como GPT de OpenAI. Esto permite, entre otras cosas, dar al asistente una descripción de un problema en lenguaje natural y obtener la solución en un lenguaje de programación concreto. Un ejemplo de este tipo de herramientas es GitHub Copilot.

Aunque OpenAI dispone de algunos modelos open source, este no es el caso de la familia de modelos GPT en que se basa Copilot, que son de naturaleza cerrada. Además, al ser Copilot un servicio en la nube, esto implica que nuestro código puede escapar de nuestro control, al incluirse como contexto de nuestros prompts. Esto puede hacer que el uso de Copilot no sea adecuado en ciertos proyectos donde la privacidad es primordial.

La idea del trabajo fue entonces buscar una alternativa Open Source y gratuita a GitHub Copilot (que es, por el momento, un servicio de pago).

Ollama y LLMs en local

Ollama es un proyecto Open Source que ofrece una plataforma para ejecutar modelos de lenguaje grandes (LLMs) en local de forma sencilla. Al ejecutar un modelo en local evitamos que el código que estamos desarrollando abandone nuestro equipo, mejorando así la privacidad. Está disponible para varias plataformas (macOS, Windows, GNU/Linux y Docker) y es la primera pieza del trabajo.

Ollama soporta una gran variedad de modelos. Para el proyecto elegí uno de los modelos Open Source más populares, Llama 3 de Meta. Está disponible en las versiones de 8.000 y 70.000 millones de parámetros (8B y 70B). Desde que presenté el proyecto ha salido la versión de Llama 3.1, que añade un modelo de 405.000 millones de parámetros (405B). Para este artículo se ha utilizado esta versión 3.1 más reciente, en su versión 8B.

En su carta Open Source AI Is the Path Forward, Mark Zuckerberg detalla algunos de los motivos por los que los modelos Open Source son buenos para un equipo de desarrollo. Uno de estos puntos es, precisamente, el caso de las empresas que manejan información sensible que no pueden enviar a los modelos en la nube y cerrados.

La segunda pieza del proyecto fue utilizar este modelo, sin ningún tipo de ajuste fino o Fine-Tuning y ponerlo a prueba para analizar la seguridad del código y los ficheros de configuración, como se ha descrito al principio.

Instalación de Ollama

La instalación de Ollama es muy sencilla, en este caso se ha descargado para macOS. En la imagen se muestran los tres pasos de la instalación. En la primera pantalla se muestra la bienvenida; la segunda permite instalar la línea de comandos; y la última muestra un ejemplo para descargar el modelo Llama 3 desde una línea de comandos.

Figura 3: Proceso de instalación de Ollama

Instalación del modelo Llama 3.1

En el caso del modelo Llama 3.1, el comando a ejecutar para instalarlo desde la terminal de macOS es: ollama run llama3.1. El modelo descargado es el de 8.000 millones de parámetros (8B) y ocupa 4,7 GB.

Figura 4: Instalación del modelo Llama 3.1

Primer uso de Ollama con el modelo en local

Una forma sencilla de empezar a usar el modelo recién instalado en Ollama es instalar una interfaz web que nos permita realizar consultas, al estilo ChatGPT. Para ello existen proyectos como Open WebUI. La forma más sencilla de instalarlo es mediante Docker y entonces disponemos de una aplicación web servida en local con la que podemos interactuar con el modelo:

Figura 5: Consulta al modelo con Open WebUI

Como se observa en la imagen, es una interfaz que nos resulta muy familiar si ya hemos utilizado ChatGPT. En este ejemplo, se ha arrastrado un fichero Web.config de configuración de IIS y se le ha preguntado al modelo si es seguro. En el proyecto se han utilizado prompts en inglés. Aunque el modelo Llama 3.1 es multi-idioma, la mayoría de los datos de entrenamiento están en inglés y en las pruebas se consiguieron los mejores resultados en ese idioma.

A pesar de su sencillez, este enfoque de trabajo desde la interfaz web es limitado. Implica que tenemos que ir cambiando de contexto entre la aplicación donde desarrollamos, por ejemplo, Visual Studio Code y el chat en el navegador. Si el modelo nos sugiere cambios en el fichero, debemos volver al IDE para aplicarlos manualmente.

Si queremos un flujo de trabajo similar al uso de GitHub Copilot, integrado en una herramienta de desarrollo, debemos incorporar una pieza más a las que ya tenemos.

Extensiones para Visual Studio Code

Existen una serie de extensiones para Visual Studio Code (y otros IDEs) que permiten utilizar los asistentes de código basados en IA. Una de ellas es Continue, una extensión Open Source para Visual Studio Code y JetBrains que ofrece funciones como autocompletar líneas enteras y secciones de código, chat con el LLM sobre funciones y ficheros de nuestro proyecto, generación de código a partir de lenguaje natural, etcétera. Si utilizamos Visual Studio Code, se puede descargar e instalar la extensión desde el marketplace de Visual Studio Code. Una vez instalada la extensión, la configuración es sencilla.

Figura 6: Configuración del proveedor de modelos en Continue

En primer lugar, se configura el proveedor de modelos LLM, como muestra la imagen anterior. Como puede verse, hay varios disponibles. Si disponemos de claves de API, se pueden utilizar modelos comerciales, como los de OpenAI. En este caso elegimos Ollama para usar los modelos locales.

Figura 7: Configuración del modelo en Continue

En el siguiente paso, mostrado en la imagen, se elige el modelo concreto. La extensión ofrece varios modelos configurados por defecto. En nuestro ejemplo hemos elegido "Autodetect" para indicar que se rellene el desplegable de modelos (que se observa vacío en la parte inferior izquierda) con los modelos ya disponibles en Ollama. Habíamos descargado ya el modelo Llama 3.1 de Meta cuando instalamos Ollama.

Uso de la extensión Continue

Con la extensión ya configurada, si ahora nos colocamos en la pestaña de Continue tenemos disponible el chat contra el modelo Llama 3.1 en local, que hemos seleccionado en el desplegable inferior donde aparecen los modelos instalados en Ollama.

Figura 8: Uso de Continue para generación de código

En la imagen puede verse uno de los usos de esta extensión, la generación de código a partir del lenguaje natural. En el ejemplo, hemos pedido al modelo que genere una función en Python para calcular la secuencia Fibonacci para un número dado. Llama ha generado la función, incluyendo ejemplos de la salida para diversos valores.

Ahora que todas las piezas el proyecto están encajadas, ha llegado el momento de poner a prueba el modelo en local para el propósito inicial de mejorar la seguridad de ficheros de código con configuración de entornos .NET con Web.config. De todo esto, puedes ver la charla de "Some dirty, quick and well-known tricks to hack your bad .NET WebApps".


Figura 10: "Some dirty, quick and well-known tricks to hack your bad .NET WebApps".

Para el primer ejemplo, se ha utilizado un fichero Web.config de configuración de IIS. Para generar un fichero vulnerable se utilizó GPT-4o de OpenAI, con el siguiente prompt:

"Eres un ingeniero de ciberseguridad que está probando una nueva forma de detectar vulnerabilidades en ficheros de configuración de IIS. Genera un fichero de configuración Web.config que incluya las vulnerabilidades más importantes en este tipo de ficheros."

Tras generar el fichero Web.config, se incluyó en un proyecto ASP.NET real para ver que era correcto y que la aplicación funcionaba.

Figura 11: Fichero Web.config vulnerable generado por ChatGPT-4o

En la imagen se muestra el fichero de configuración generado y se ven algunas de las nueve vulnerabilidades introducidas. En la siguiente imagen hemos abierto en Visual Studio Code el fichero Web.config (mitad izquierda), que aparece junto con la la pestaña de la extensión Continue, que contiene el chat con el modelo LLM en local (mitad derecha).

Figura 12: Análisis de la seguridad del fichero de configuración con Continue

Para incorporar nuestro fichero Web.config como contexto a la ventana del chat, seleccionamos todo el contenido (con Cmd+A en macOS o Ctrl+A en Windows) y lo incorporamos al chat con (Cmd+L o Ctrl+L). A continuación del fichero adjunto, introducimos el prompt para el modelo.

En el curso aprendimos que, cuanto más detallado es el prompt, mejor es la respuesta y menor la probabilidad de que el modelo "alucine". Tras probar varios prompts para analizar la seguridad del fichero con el modelo Llama local, se llegó a éste:

You are a cybersecurity expert with access to vulnerability databases and cybersecurity guidelines and best practices. You have deep knowledge of programming code, as well as software and hardware configuration files, like the one attached here.

Analyze this attached file to identify potential security issues. For each issue, provide a brief explanation suitable for a technical audience and propose a change to the file that will eliminate or mitigate the vulnerability.

Ensure that your proposed changes include inline code in the response.

Primero se le da al modelo un rol: un experto en ciberseguridad con acceso a bases de datos de vulnerabilidades, guías de ciberseguridad y mejores prácticas y un profundo conocimiento del código y de los ficheros de configuración. A continuación, se le indica qué resultado esperamos obtener: un análisis de los riesgos de seguridad del fichero con una explicación técnica y una propuesta que elimine o mitigue la vulnerabilidad. Por último, se pide que la salida incluya las mejoras en forma de código en línea. Esta última petición será muy interesante, como se verá a continuación.

En el siguiente vídeo se ve que el modelo nos ha respondido, analizando las vulnerabilidades del fichero. La primera indica que el modo de depuración está habilitado, lo que puede exponer información sensible a un atacante. La recomendación es desactivarlo, poniendo a false el atributo debug. Al haber solicitado el cambio propuesto como texto en línea, la extensión permite aplicarlo directamente al código. Si colocamos el ratón en el texto en línea, aparecerán varios iconos. Pulsando el primero (Apply to current file) la herramienta aplica el cambio en la línea adecuada.

Figura 13: Demostración de aceptación de recomendación

Hay que tener en cuenta que en el vídeo se han recortado algunos momentos para reducir su peso, en realidad tarda algo más que lo que se muestra.
 
Slash Commands

Una vez que hemos encontrado un prompt que nos ha dado buenos resultados, conviene encontrar una forma de no tener que copiarlo y pegarlo en el chat cada vez que queremos analizar un fichero. Continue dispone de los comandos slash, es decir, los que comienzan por /. Un ejemplo es el comando /comment, que se utiliza para que el LLM comente el código seleccionado.

Lo bueno es que podemos crear nuestros propios Slash Commands. Para ello, pulsamos sobre el icono de la tuerca que aparece en la barra inferior de la ventana de la extensión Continue. Se nos abrirá el fichero config.json de la configuración de la extensión. Es un fichero JSON sencillo y podemos añadir nuestros comandos en la sección customCommands, por ejemplo:

"customCommands": [
{
"name": "secure",
"prompt": "{{{ input }}}\n\nYou are a cybersecurity expert with access to vulnerability databases and cybersecurity guidelines and best practices. You have deep knowledge of programming code, as well as software and hardware configuration files, like the one attached here.\n\nAnalyze this attached file to identify potential security issues. For each issue, provide a brief explanation suitable for a technical audience and propose a change to the file that will eliminate or mitigate the vulnerability.\n\nEnsure that your proposed changes include inline code in the response.",
"description": "Evaluar la seguridad"
}
]

Ahora, procedemos de la misma manera que antes, seleccionando todo el fichero Web.config y añadiéndolo al chat de Continue. Pero simplemente escribimos /secure en el chat, en lugar del prompt completo:

Figura 14:  Uso de slash command /Secure en Continue

Corrección masiva con el modo edición

Antes hemos visto cómo se puede corregir directamente el código original a partir de las recomendaciones proporcionadas por el modelo, pulsando el icono de aplicar cambios que aparecía en la línea de la corrección. Si queremos aplicar varios cambios a la vez, en lugar de ir aplicando uno por uno, podemos utilizar el modo edición de la extensión Continue.

Como antes, pulsamos Cmd+A o Ctrl+A para seleccionar todo el contenido. Ahora, en lugar de incorporarlo al chat como hicimos al principio, pulsamos Cmd+I o Ctrl+I para acceder al modo edición. En la parte superior de Visual Studio Code aparece una caja de texto donde podemos introducir un Slash Command, ya sea uno de los propios de la extensión o uno definido por nosotros.

Figura 15: Ejemplo de Slash Command

En el vídeo se muestra cómo Continue aplica todos los cambios propuestos por el modelo. Una vez aplicados, los podemos revisar uno a uno e ir aceptándolos o rechazándolos de forma individual. Si estamos seguros de que todos los cambios propuestos son adecuados, se pueden aceptar todos en bloque.

Como antes, el vídeo tiene cortes y el proceso tarda algo más de lo mostrado. Aunque no veamos en la ventana de chat cómo se reciben los tokens según se va produciendo la inferencia, este proceso debe ocurrir igualmente. Por eso, hasta que no se recibe la respuesta completa del LLM, no se observarán cambios en el fichero de configuración.

Consideraciones finales

A modo de referencia, para el trabajo se utilizó un Mac mini de 2023 con chip Apple M2 y 16 GB de memoria, con un Neural Engine de 16 núcleos. En las pruebas, esta configuración pudo ejecutar con soltura modelos de entre 7.000 y 13.000 millones de parámetros.

El uso de modelos de lenguaje grandes como asistentes al desarrollo de código tiene un gran potencial. A medida que el nuevo hardware que se lance incluye aceleración por hardware para mejorar el rendimiento de las tareas de IA, va a ser cada vez más viable el uso de LLMs en local, ya sean genéricos o para tareas específicas. Las ventajas en cuanto a privacidad que ofrece este enfoque suponen una alternativa viable a los modelos cerrados y en la nube.

Saludos,

Entrada destacada

+300 referencias a papers, posts y talks de Hacking & Security con Inteligencia Artificial

Hace un mes comencé a recuperar en un post mi interés en los últimos años, donde he publicado muchos artículos en este blog , y he dejado mu...

Entradas populares