Buenas tardes, queridos lectores. Mi nombre es Victor Burov, soy desarrollador en ISPsystem. En la última publicación hablé sobre la
herramienta para crear pruebas automáticas, hoy compartiré mi experiencia en la automatización de pruebas de seguridad.

Al principio, un empleado separado buscó las vulnerabilidades en los productos. Las pruebas manuales tomaron mucho tiempo y no garantizaban que se encontraran todas las vulnerabilidades. Habiendo comprobado las leyes básicas de las pruebas, llegamos a la conclusión de que puede automatizarse. Luego decidimos escribir una utilidad que facilitará la vida del probador, le ahorrará tiempo y le permitirá verificar los productos después de cada cambio. Como el probador se llamaba Lida, nombramos la nueva aplicación en su honor. En general, en nuestra empresa se ha convertido en una tradición llamar a las herramientas de prueba por los nombres de los evaluadores.
Después de analizar las utilidades de búsqueda de vulnerabilidades, llegué a la conclusión de que todos necesitan especificar las funciones a llamar y los parámetros utilizados. Nuevamente decidimos aprovechar la interfaz unificada y formulamos los requisitos para Lida.
Requisitos de inicio:
- Crear automáticamente listas de funciones.
- Opciones de autocompletar.
- Hacer solicitudes de API.
- Análisis de salida de datos después de realizar funciones.
- Buscar vulnerabilidades en los datos.
- Formación de informes.
- Configuraciones flexibles.
Darse cuenta de todo esto no fue fácil.
Implementación
Omitir formularios y listas
Para encontrar vulnerabilidades en una función, debe ejecutarse pasando los parámetros necesarios. Nuestra interfaz se basa en listas y formularios, por lo que puede automatizar la recopilación de datos procesando documentos xml que describen la estructura de los elementos de la interfaz.
Decidí comenzar el rastreo desde el menú principal, yendo recursivamente a todas las listas anidadas. "Lida" abre la lista del primer nivel. Como regla, tiene varios botones que invocan algunas funciones.
Si el botón abre el formulario, el resultado de la llamada será un documento xml con nodos que contienen información sobre los campos: nombre, validador, rango de valores válidos. En base a esta información, se generan valores de campo. Por ejemplo, se generará un número para int. Si no hay un validador, se genera una cadena aleatoria. Después de completar todos los campos del formulario, se envía una solicitud.
Si la función es una lista, se abrirá y las funciones asociadas con los botones se llamarán por sus elementos.
Al verificar las listas, surge un problema: todas las listas deben tener un conjunto de registros que garanticen que se pueda hacer clic en todos los botones de la lista.
Búsqueda por inyección SQL
La inyección SQL es probablemente uno de los problemas más comunes para las aplicaciones, incluido el nuestro. Muchas llamadas a funciones generan varias consultas DBMS. Se produce un error cuando los parámetros que provienen del exterior se sustituyen en el cuerpo de la solicitud "tal cual". Las consecuencias de tales errores pueden ser tristes: desde la recepción no autorizada de datos hasta la eliminación de tablas.
Para comenzar la búsqueda de inyecciones SQL, organicé el resultado de todas las consultas SQL en un archivo. Después de ejecutar la función, la aplicación busca los valores de los parámetros pasados en las consultas SQL resultantes.
Puede usar el registro del servidor SQL en sí. Pero en nuestro caso, solo hay un método para ejecutar consultas y agregar registros no fue difícil. Gracias a esto, sabemos exactamente qué desafío ha generado esta o aquella solicitud.Cuando se encuentra el valor del parámetro pasado, la utilidad pasa un valor que contiene una comilla simple a este parámetro. Si la cita se encuentra en la misma secuencia, entonces el parámetro no se escapa; encontramos un lugar para la inyección de SQL.
Análisis de llamadas del sistema
Un problema similar ocurre cuando realizamos llamadas al sistema. Decidí buscarlos con strace y seleccioné los parámetros de lanzamiento óptimos para ello. Ejemplo de inicio de ISPmanager:
strace -f -s 1024 -e trace=file,process bin/core ispmgr
Además de las inyecciones de SQL, Lida realiza una función y analiza la salida de capas para determinar si se incluyen los valores de los parámetros pasados para
abrir, desvincular, rmdir, chmod chown, chflags, mknod, mkfifo, fcntl, enlace simbólico, enlace, execve, mkdir .
Si se encuentra el parámetro, la utilidad le pasa un valor que contiene, por ejemplo, una ruta con una transición al directorio anterior. Si se pone como está, entonces se encuentra una vulnerabilidad potencial.
El análisis de la función ejecutiva resultó ser muy útil. Le permite determinar en qué función no se escapan los argumentos para ejecutar archivos ejecutables. Este error es muy costoso, porque a través de él puede obtener acceso de root al servidor simplemente cambiando la contraseña.
Cuando los usuarios encuentran una vulnerabilidad en nuestros productos, la compañía paga una recompensa en efectivo, cuyo monto depende de la categoría de vulnerabilidad. Este enfoque puede ser más barato que buscar errores por su cuenta (es posible que el probador no encuentre el error y reciba un salario).
Otra prueba interesante: verificar el orden de invocación de las funciones estadísticas y otras. A menudo, el acceso se verifica primero a través de estadísticas, y luego algunas acciones inseguras, lo que deja la oportunidad de sustitución. Pero no automatizamos esto.Comprobando el acceso a objetos extraños
Verificamos el acceso a objetos extraños de debajo del usuario para las entidades de otro usuario y administrador. En este modo, verificamos la capacidad de leer, modificar y ver las listas de elementos de un usuario extranjero.
Lida omite todas las funciones disponibles en nombre del propietario o administrador y recuerda sus elementos. Luego llama a las mismas funciones con elementos de debajo de otro usuario. En una situación ideal, la respuesta debería ser un error como Acceso o Perdido. Si no se recibe dicho error, por lo tanto, es muy probable que pueda leer los datos del usuario de otra persona.
En algunos casos, la ausencia de un error no significa que pueda acceder directamente a los objetos de otros usuarios. Al comienzo de tales funciones, agregamos una verificación de permisos de elementos. Esto verifica no solo la seguridad, sino también la exactitud de las respuestas del servidor.
Validación API
La validación de nuestra API es una ventaja adicional de verificar las funciones en busca de vulnerabilidades. Al analizar los informes sobre el trabajo de Lida, aprendimos a devolver los tipos correctos de errores, lo que hizo que nuestra API sea más conveniente y lógica. Como resultado, al igual que con la grabadora de cinta, recibimos no solo un control de seguridad, sino que también verificamos una vez más que nuestra API sea "coherente".
Dificultades
Falsos positivos
La utilidad puede funcionar con todos nuestros productos, por lo tanto, verifica muchas funciones diferentes. Básicamente, aparecieron falsos positivos en el modo de verificar el acceso a objetos extraños. Esto se debe a las características de los botones y funciones.
Los falsos positivos fueron el mayor problema de Lida. Parecía que estaba completamente depurado, pero al verificar en diferentes paneles, surgieron nuevos falsos positivos. Como resultado, hubo varias etapas de su corrección.
Creación de entidad
La mayoría de las acciones en el panel se realizan en cualquier entidad (nombre de dominio, base de datos, etc.). Para lograr la máxima automatización, Lida tuvo que crear estas entidades automáticamente. Pero en la práctica esto resultó ser difícil de implementar. A veces, la validación se realiza en código, por lo que no siempre es posible sustituir un valor de parámetro automáticamente. La segunda razón son las entidades dependientes. Por ejemplo, para crear un buzón, debe crear un dominio de correo.
Por lo tanto, decidimos no luchar por la automatización total. Antes de comenzar, el probador crea entidades manualmente y toma una instantánea de la máquina, ya que las entidades cambiarán después de la verificación. Esto le permite no omitir la comprobación de un grupo de funciones en caso de creación incorrecta de una entidad.
Llamando a funciones destructivas
Casi todas las listas tienen funciones para eliminar o desactivar una entidad. Si los ejecuta en secuencia, la entidad se eliminará antes de realizar otras funciones. Defino tales funciones y realizo después de otras. Además se agregó una clave que prohíbe la ejecución de tales funciones.
Algunas funciones reinician el servidor. Deben ser rastreados y agregados a la lista de ignorados.
Debido a la naturaleza de la lógica de funcionamiento, algunas funciones reinician el panel. Durante las pruebas de seguridad, esto hace que el panel se inicie sin rastrear consultas SQL o estratos; una mayor verificación deja de tener sentido. Tiene que rastrear esto y reiniciar el panel en modo rastreo.
Comprobación de parámetros dependientes
En los formularios hay campos para ingresar texto, casillas de verificación, listas desplegables, de cuyos valores depende la disponibilidad de valores de otros campos. Cada valor del campo dependiente puede tener una sección de código separada, por lo tanto, las partes cuando pueden permanecer sin verificar.
Para resolver este problema, agregué algoritmos para analizar campos dependientes y verificar todas las combinaciones de controles dependientes.
Comprobar funciones no disponibles en la interfaz
Las funciones de servicio no están disponibles para la transición, pero pueden contener vulnerabilidades. Para identificarlos y verificarlos, tenemos una función especial que devuelve una lista de todas las funciones registradas. Comparamos esta lista con la lista de funciones probadas. No hay metadatos para las funciones de utilidad, por lo que es imposible verificar los parámetros procesados dentro de ellos. Para verificar tales funciones, les paso nuestros parámetros estándar
elid, plid y otros.
Conclusión
Incluimos a Lida en cada construcción nocturna en Jenkins. En función de los resultados de su trabajo, se genera un informe de verificación y se muestra información sobre la función sospechosa con todos los parámetros.
La tarea cerrada por el desarrollador ahora es verificada no solo por el probador, sino también por Lida. El probador procesa el informe recibido: copia los parámetros de la función sospechosa en el navegador y analiza el comportamiento y el panel de registro. Si se confirma la vulnerabilidad, el error se registra en el desarrollador.