Bug (error de software)
Un bug es un defecto en el código de un programa que hace que se comporte de forma distinta a la prevista. No es un ataque ni una acción maliciosa: es un error involuntario. Y es el punto de partida de casi cualquier vulnerabilidad que no venga de una configuración floja o de una credencial robada.
Un bug no es lo mismo que una vulnerabilidad
Los dos términos se usan como si fueran intercambiables y no lo son. Un bug es cualquier comportamiento del programa distinto del previsto: un botón que no responde, un total mal sumado, una pantalla que se queda en blanco. Una vulnerabilidad es el subconjunto de bugs que además permite a alguien hacer algo que no debería poder hacer.
La diferencia se nota en quién decide la prioridad. Un bug de interfaz lo ordena el equipo de producto por molestia; uno de seguridad se ordena por lo que un atacante consigue con él, y eso puede ser leer los datos de otro cliente, ejecutar código en el servidor o saltarse la autenticación. Es la misma línea de código y son dos conversaciones distintas.
Cuando una vulnerabilidad se hace pública y recibe identificador, deja de ser un bug interno y pasa a ser un CVE, con nombre que cualquiera puede buscar, incluido quien la quiere aprovechar. Si además existe código que la aprovecha de forma fiable, ya hablamos de un exploit.
De dónde salen los bugs
Son involuntarios, y esa es su primera característica. Nadie escribe un desbordamiento a propósito: aparece porque el software es grande, porque las combinaciones de uso son más de las que nadie puede prever y porque el código lo escriben personas con plazo.
Errores de programación. Divisiones por cero, accesos a memoria que no tocaba, condiciones mal escritas, un bucle que compara con menor donde debía comparar con menor o igual. Son los más fáciles de encontrar con herramientas porque tienen una forma reconocible.
Errores de diseño. El código hace exactamente lo que se le pidió, y lo que se le pidió estaba mal. Aquí caen los fallos de lógica de negocio, que ninguna herramienta detecta porque no hay nada anómalo que ver: el descuento se aplica dos veces porque el flujo lo permite.
Errores de integración. Dos componentes correctos por separado que, juntos, hacen algo que ninguno de los dos esperaba. Las condiciones de carrera son el caso típico: el fallo solo aparece cuando dos peticiones llegan a la vez, así que en pruebas no se reproduce y en producción sí.
Qué consigue un atacante con un bug
Depende de dónde esté. Un bug en el tratamiento de la entrada puede convertirse en inyección SQL o en cross-site scripting. Uno en la deserialización de datos o en la construcción de rutas puede acabar en ejecución remota de código, que es el peor resultado posible porque a partir de ahí el atacante ya está dentro.
Uno en la comprobación de permisos da escalada de privilegios: el usuario que solo debía ver su factura acaba viendo las de todos. Y uno en la lógica de autenticación puede desactivar un control sin que nadie lo note, porque la pantalla sigue pareciendo la misma.
Un hacker con intención maliciosa no necesita entender el programa entero: le basta con encontrar el punto donde el comportamiento se aparta de lo previsto y empujar por ahí. Por eso un bug pequeño importa, y por eso la pregunta útil no es si el software falla, sino qué deja de impedir cuando falla.
Cómo se encuentran antes que ellos
Pruebas durante el desarrollo. Es lo más barato y lo que más bugs quita, pero solo encuentra aquello que a alguien se le ocurrió probar.
Análisis automático del código. SAST lee el código fuente sin ejecutarlo y DAST ataca la aplicación en marcha. Entre los dos cubren mucho terreno, los dos producen falsos positivos y por eso alguien tiene que revisar la lista antes de que llegue a nadie.
Auditoría de seguridad y pentesting (test de intrusión). Es donde aparecen los bugs que ninguna herramienta ve: los de lógica, los que hay que encadenar y los que solo son peligrosos en este negocio concreto. Un escáner dice que un endpoint acepta un identificador; una persona prueba el identificador del cliente de al lado.
Programas de recompensas. Abrir un canal para que investigadores externos reporten lo que encuentren, con reglas escritas y con pago. Complementa, no sustituye: llega gente con puntos de vista que el equipo interno no tiene, pero nadie controla cuándo miran ni qué miran.
Y después, el ciclo. Gestión de vulnerabilidades para ordenar lo que sale, gestión de parches para cerrarlo y retest para comprobar que el arreglo arregla. Una lista de bugs sin ese ciclo es un inventario, no una defensa.
Un ejemplo: el bug que deja el candado abierto
Una aplicación de banca en línea publica una actualización. Antes, tras tres intentos fallidos de inicio de sesión, pedía una verificación adicional. Después de la actualización, un cambio en la lógica hace que ese contador nunca llegue a evaluarse.
Funcionalmente no se nota nada: quien acierta la contraseña entra igual y quien la falla vuelve a intentarlo. Lo que ha cambiado es que ya no hay límite, así que un ataque de fuerza bruta puede probar combinaciones sin encontrar freno.
El bug no está en el mecanismo de verificación, que sigue funcionando perfectamente: está en la condición que decide cuándo activarlo. Es la clase de fallo que una prueba funcional aprueba y una revisión de seguridad no, y es también el motivo por el que conviene volver a mirar la autenticación después de cada cambio, aunque el cambio pareciera de otra parte.
Cómo lo probamos en Asperis
Nuestro pentesting web parte de esta idea: los bugs que importan casi nunca son los que canta una herramienta. Se recorren los flujos completos, se encadenan hallazgos pequeños hasta ver hasta dónde llegan y cada uno se entrega con evidencia reproducible, no con una captura de un escáner.
Y se cierra el círculo con un retest, porque un informe que nadie vuelve a comprobar no dice si el bug sigue ahí.