Automatizando la Detección de Vulnerabilidades en tus Recursos

Descubre cómo automatizar la detección de vulnerabilidades en tus recursos, para que puedas mejorar la seguridad desde el principio y proteger tu infraestructura antes de que una vulnerabilidad se convierta en un incidente de seguridad.

En este artículo, cubriremos cómo consultar la base de datos de vulnerabilidades de NIST, creando un Nombre CPE para un recurso, y automatizando la obtención de vulnerabilidades con un script.

Vulnerabilidades como Vectores de Ataque

Los desarrolladores de software no son perfectos; a veces su código pasa por alto algún caso límite que hace vulnerable tu infraestructura.

Tomemos como ejemplo la vulnerabilidad de Apple “goto fail” en 2016, una sola línea duplicada permitió ataques man-in-the-middle en comunicaciones SSL con dispositivos Apple:

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail; // Esto se ejecuta fuera del if

Estos errores son comunes y tontos, pero siguen siendo peligrosos; además, a medida que el código se vuelve más complejo y cuantos más recursos tengas, mayor será tu superficie de ataque. Por eso es tan importante instalar las actualizaciones de seguridad.

Las vulnerabilidades son tu puerta trasera y pueden proporcionar acceso de muchas formas. Permiten a los atacantes interrumpir tu servicio, obtener información sobre tu infraestructura (para explotar otras vulnerabilidades) y, en los peores casos, ejecutar código que termina en un control remoto. Una vez dentro, los atacantes pueden moverse lateralmente a otros recursos, acceder o cifrar tus datos de negocio, usar tus recursos para minar criptomonedas, o alterar ligeramente tu línea de producción lo suficientemente poco como para que no lo notes, pero lo bastante para que falle la producción.

Hoy en día, los ataques suelen comenzar como procesos automatizados ejecutados por botnets que buscan servicios vulnerables y los hackean automáticamente.

A menudo no somos conscientes de cuánta información comparten nuestros servicios, como un servidor web que comparte su número de versión. No me llevó mucho tiempo encontrar un servidor web público como este:

$ curl -I <https://▋▋▋.▋▋▋▋▋▋▋▋▋.net>
HTTP/2 301
server: nginx/1.18.0 (Ubuntu)  ⚠️
date: Fri, 04 Oct 2024 08:57:20 GMT
content-type: text/html
content-length: 178
location: <https://▋▋▋.▋▋▋▋▋▋▋▋▋.net/home/>

Sabemos que este servidor está utilizando server: nginx/1.18.0 (Ubuntu), podemos buscar vulnerabilidades que afecten a este software y lanzar un ataque.

Consulta de las Vulnerabilidades de tu Dispositivo

¿Dónde puedes buscar las vulnerabilidades de un software específico? En una base de datos de vulnerabilidades (ba dum tss).

Cuando se encuentra una vulnerabilidad, se clasifica bajo el sistema de Vulnerabilidades y Exposiciones Comunes (CVE). Este sistema estandariza la nomenclatura de las vulnerabilidades (CVE-AÑO-ID) y la presentación de la información relacionada (descripción, métricas, etc.).

Algunas organizaciones, como NIST NVD, mantienen bases de datos CVE donde las personas pueden aprender cómo mitigarlas.

Resultado de búsqued en NIST NVD para un CVE de severidad 7.4 High

Esta base de datos tiene una herramienta de búsqueda que te permite buscar por software y versión. Solo necesitas formatearlo en un CPE (Enumeración de Plataforma Común), un esquema de nomenclatura estructurado para sistemas de tecnología de la información, software y paquetes.

Cuando tengas dudas sobre cuál es el CPE correcto para tu recurso, siempre puedes preguntarle a Google:

Búsqueda en Google para cpe nginx

Para el servicio nginx del ejemplo anterior, el CPE sería cpe:2.3:a:f5:nginx:1.18.0:*:*:*:*:*:*:*.

Ingresando esta cadena en la herramienta de búsqueda:

Detalle del formulario de búsqueda en NIST NVD

Nos devuelve tres vulnerabilidades de alta gravedad que podrían ser usadas para impactar ese servidor web:

Resultados de la búsqueda en NIST NVD para nginx version 1.18

Acercándonos más a un entorno industrial, tomaremos un PLC Siemens ‘SIMATIC S7-1500’ como ejemplo:

Un PLC SIMATIC S7 1500

Este PLC específico tiene una versión de firmware 0.5.1, por lo que su CPE sería cpe:2.3:o:siemens:simatic_s7-1500_cpu_firmware:0.5.1:*:*:*:*:*:*:*.

Consultando la base de datos de NIST, vemos 13 vulnerabilidades:

Resultados de la búsqueda en NIST NVD pare el firmware de siemens.

¡Genial! Ahora podemos revisar esta lista y aplicar acciones de mitigación donde sea necesario.

Consultando la API de NIST NVD

Sin embargo, repetir este proceso para cada recurso individualmente es una pérdida de tiempo y directamente inviable cuando tenemos cientos de dispositivos.

Por suerte, NIST ofrece una API para consultar esta base de datos y automatizar este proceso de descubrimiento de vulnerabilidades.

Usar la API es increíblemente sencillo. Podemos realizar las mismas consultas que hicimos anteriormente en la línea de comandos con un programa como curl:

$ curl "<https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:a:f5:nginx:1.18.0>" > nginx-1-18-0.json
$ curl "<https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:o:siemens:simatic_s7-1500_cpu_firmware:0.5.1>" > simatic_s7-1500.json

La respuesta es un payload JSON bastante intuitivo y completo:

$ cat "nginx-1-18-0.json" | jq .
{
  "resultsPerPage": 3,
  "startIndex": 0,
  "totalResults": 3,
  "format": "NVD_CVE",
  "version": "2.0",
  "timestamp": "2024-10-07T10:47:05.540",
  "vulnerabilities": [
    {
      "cve": {
        "id": "CVE-2021-23017",
        "sourceIdentifier": "[email protected]",
        "published": "2021-06-01T13:15:07.853",
        "lastModified": "2023-11-07T03:30:29.880",
        "vulnStatus": "Modified",
        "cveTags": [],
        "descriptions": [
          {
            "lang": "en",
            "value": "A security issue in nginx resolver was identified, which might allow an attacker who is able to forge UDP packets from the DNS server to cause 1-byte memory overwrite, resulting in worker process crash or potential other impact."
          },

Puedes programar estas llamadas HTTP y el procesamiento de respuestas con el lenguaje que prefieras e integrarlas con las herramientas que mejor se adapten a tu flujo de trabajo. Veremos un par de ejemplos en breve.

Solo falta una pieza del rompecabezas, obtener una clave de API.

NIST limita el número de solicitudes que los usuarios no registrados pueden realizar. Si vas a usar esta API de manera seria, deberías solicitar una clave de API para levantar estas restricciones. El proceso es sencillo, solo necesitas rellenar y enviar un formulario simple, y hacer clic en un enlace que recibirás por correo electrónico.

Una vez que obtengas tu clave de API, puedes proporcionarla a través de un encabezado HTTP apiKey. En curl, esto se hace con el parámetro -H:

$ curl -H "apiKey: YOUR-API-KEY-HERE" "https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=cpe:2.3:a:f5:nginx:1.18.0"

Automatización de la Detección de Vulnerabilidades en una Hoja de Cálculo

Es común mantener el inventario de recursos en una hoja de cálculo. Así que vamos a empezar viendo cómo integrar la API de NIST NVD en Google Sheets.

Nota

No recomendamos este enfoque, lo mostramos solo con fines educativos y porque es divertido. Las principales desventajas son que: No puedes incluir una apiKey, es costoso para el backend de la API, y no proporciona una buena experiencia de usuario. Proporcionamos una opción adecuada en la siguiente sección.

Comenzamos con una hoja de cálculo sencilla que contiene toda la información básica de nuestros recursos:

Ejemplo de hoja de cálculo para inventario.

El plan será:

  1. Generar el CPE para cada elemento.
  2. Llamar a la API para cada recurso.
  3. Extraer los números de CVE del payload.

¡Vamos a empezar!

1. Generación de CPEs

El primer paso es generar el CPE para cada elemento, lo que se puede hacer fácilmente concatenando la información disponible:

Ejemplo de hoja de cálculo para inventario: Generando el CPE.
="cpe:2.3:" & F2 & ":" & B2 & ":" & C2 & ":" & D2

Añadimos el texto cpe:2.3 y la parte al principio. La parte en CPE describe el tipo de recurso: a para aplicaciones, h para hardware y o para sistemas operativos.

2. Llamando a la API

Google Sheets tiene funciones para importar y procesar varios tipos de datos como XML o CSV. Sin embargo, no tiene soporte para el JSON que devuelve la API de NIST. Hay soluciones de terceros para eso, pero para mantener las cosas simples, nos pondremos creativos y utilizaremos solo las funciones por defecto.

La función IMPORTDATA(url, delimiter, locale) puede cargar datos desde una url, separando las filas por saltos de línea y las columnas por el carácter delimiter. ¿Nos serviría esto?

Podríamos usar esta función para cargar todo el payload en una sola celda y luego extraer los datos. Ten en cuenta que la API devuelve un JSON minificado, es decir, sin espacios extra ni saltos de línea; anteriormente usamos jq para mejorar la legibilidad de la salida.

=IMPORTDATA("https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=" & Inventory!E2,"Ç")

En este ejemplo, Inventory!E2 es el CPE que calculamos anteriormente, y estamos usando un carácter extraño como delimitador (”Ç”) para que los datos no se dividan en columnas y permanezcan en la misma celda.

Ejemplo de hoja de cálculo para inventario: Mostrando: Error: Result too large.

¿El resultado? Error: Result too large. Parece que el payload es demasiado grande para una sola celda, por lo que tendremos que buscar una manera de dividirlo en partes más pequeñas.

Observando más de cerca el payload JSON de la API, podemos identificar algunos patrones útiles:


  "vulnerabilities": [
    {
      "cve": {
        "id": "CVE-2021-23017",
        "sourceIdentifier": "[email protected]",

Cada número de CVE va precedido de la cadena "cve": { "id": "CVE. Podríamos:

  • Dividir el payload en columnas a partir del carácter {.
  • Filtrar y quedarnos solo con los elementos que comienzan con "id": "CVE-.

Esta sería la fórmula:

=TRANSPOSE(
    QUERY(
      TRANSPOSE(
        IMPORTDATA("https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=" & Inventory!E2,"{")
      ), 'HELPER - Query'!$A$1))

Donde:

  • QUERY nos permite ejecutar consultas SQL sobre un conjunto de datos.
  • TRANSPOSE se utiliza para convertir filas en columnas, de modo que podamos evaluar la consulta para cada valor. Después de la consulta, se usa de nuevo para convertir el resultado en una sola fila.
  • 'HELPER - Query'!$A$1 es una referencia a la celda que contiene la consulta: SELECT * WHERE Col1 contains ':"CVE-'. A Google Sheets no le gusta el carácter " dentro de la consulta, pero parece que funciona al mover la consulta a otra celda ¯_(ツ)_/¯.

¿Funcionará esto?

Probemos a colocar esta fórmula en una hoja separada de nuestra hoja de cálculo de inventario y veamos:

Ejemplo de hoja de cálculo para inventario: Mostrando el payload json dividido en columnas.

¡De hecho funciona!

3. Extracción del número de CVE

Estamos cerca, tenemos cada número de CVE en una celda separada y solo necesitamos extraerlo.

Mi primer pensamiento fue ajustar el SELECT * en la consulta SQL, intentando devolver solo una subcadena en lugar de todo el campo. De esa manera, todo el procesamiento estaría contenido en un solo lugar. Sin embargo, el lenguaje SQL en Google Sheets tiene un conjunto de funciones muy limitado y este enfoque no es viable.

Mantendremos los resultados actuales tal como están en una hoja separada HELPER - RAW_CVEs, que actuará como un ayudante para mostrar los números de CVE limpios en otro lugar.

Podemos usar la función REGEXEXTRACT para extraer solo el número de CVE usando expresiones regulares:

=REGEXEXTRACT('HELPER - RAW_CVEs'!A1,"CVE-\d*-\d*")

Podríamos limpiar las cosas un poco, usando HYPERLINK para llevar al usuario al sitio web de NIST para ese CVE, y otras funciones para filtrar errores:

=IF(
  NOT(ISBLANK('HELPER - RAW_CVEs'!A1)),
  HYPERLINK(
    "https://nvd.nist.gov/vuln/detail/" & REGEXEXTRACT('HELPER - RAW_CVEs'!A1,"CVE-\d*-\d*"),
    REGEXEXTRACT('HELPER - RAW_CVEs'!A1,"CVE-\d*-\d*")
  ),
  ""
)

¡El resultado es magnífico!

Ejemplo de hoja de cálculo para inventario: Mostrando los CVES para cada recursos con enlaces a su página web.

Hemos dejado disponible nuestra hoja de cálculo de ejemplo por si quieres verla en acción.

Nota

No recomendamos este enfoque, lo mostramos solo con fines educativos y porque es divertido. Las principales desventajas son que: No puedes incluir una apiKey, es costoso para el backend de la API, y no proporciona una buena experiencia de usuario. Proporcionamos una opción adecuada en la siguiente sección.

Automatización de la Detección de Vulnerabilidades con Python

Veamos cómo hacer algo similar con un lenguaje de programación propiamente dicho como Python.

Aunque nuestro ejemplo será radicalmente simple, esta solución tiene un gran potencial para mejorar. Por ejemplo, podrías expandirlo para integrarlo con tus herramientas, programarlo para que se ejecute periódicamente o para enviar notificaciones por correo electrónico.

Nuestro ejemplo cubrirá:

  1. Recibir una lista de recursos en formato .csv (es decir, exportada desde una hoja de cálculo).
  2. Llamar a la API para obtener las vulnerabilidades.
  3. Generar una lista de vulnerabilidades en formato JSON.

Echemos un vistazo al código en nuestro script vuln_finder.py. También puedes encontrar todo el código y los archivos de datos de ejemplo en nuestro repositorio de GitHub.

1. Lectura de la lista de recursos

Utilizaremos algunos módulos en nuestro script:

  • argparse para leer las opciones de la línea de comandos.
  • requests para realizar las llamadas a la API.
  • csv para leer la lista de recursos.
  • json para escribir nuestra salida.

El primer paso será leer las opciones del usuario:

import argparse
import requests
import csv
import json

#
# Parsing arguments
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--devices", default = "inventory.csv", help = "Devices file in csv format. Default: inventory.csv.")
parser.add_argument("-o", "--output",  default = "output.json", help = "Output file where to store the json response. Default: output.json.")
parser.add_argument("-k", "--apikey", required=True, help = "NIST NVD API Key.")
args = parser.parse_args()
#
devices_csv = args.devices
output_file = args.output
apikey = args.apikey

Luego, leeremos el archivo CSV de la lista de dispositivos y generaremos el nombre CPE:

#
# Reading devices file
inventory = []
with open(devices_csv, newline='') as csvfile:
    inventoryreader = csv.reader(csvfile, delimiter=',', quotechar='"')
    for row in inventoryreader:
        device = {}
        device['name'] = row[0]
        device['vendor'] = row[1]
        device['software'] = row[2]
        device['version'] = row[3]
        device['part'] = row[4]
        device['cpe'] = "cpe:2.3:" + device['part'] + ":" + device['vendor'] + ":" + device['software'] + ":" + device['version']
        device["cves"] = []
        inventory.append(device)

2. Llamando a la API

Ahora, para cada dispositivo de nuestro inventario, llamamos a la API. El módulo requests facilita mucho el envío de esta solicitud con el encabezado apiKey.

  1. Utilizamos request.get(url, headers) para realizar la llamada a la API.
  2. Utilizamos response.json() para obtener un diccionario con la carga JSON.
  3. Extraemos los datos que queremos de ese diccionario.
#
# Calling JSON API for each device
data = []
for device in inventory:
    # The API endpoint
    url = "https://services.nvd.nist.gov/rest/json/cves/2.0?cpeName=" + device['cpe']

    # Send a GET request to the API
    response = requests.get(url, headers={"apiKey": apikey})
    # Process the response
    vulnerabilities = response.json()["vulnerabilities"]
    for vulnerability in vulnerabilities:
        cve = {}
        cve["id"] = vulnerability["cve"]["id"]
        cve["url"] = "url: https://nvd.nist.gov/vuln/detail/" + cve["id"]
        # Try to grab CVSS Score v3.1
        if "metrics" in vulnerability["cve"] and "cvssMetricV31" in vulnerability["cve"]["metrics"]:
            cve["baseScore"] = vulnerability["cve"]["metrics"]["cvssMetricV31"][0]["cvssData"]["baseScore"]
            cve["baseSeverity"] = vulnerability["cve"]["metrics"]["cvssMetricV31"][0]["cvssData"]["baseSeverity"]
        device["cves"].append(cve)
    #
    data.append(device)

Tenemos que indagar en varios diccionarios para llegar a los datos que nos interesan:

  • vulnerability["cve"]["id"]
  • vulnerability["cve"]["metrics"]["cvssMetricV31"][0]["cvssData"]["baseScore"]
  • vulnerability["cve"]["metrics"]["cvssMetricV31"][0]["cvssData"]["baseSeverity"]

Lo hemos simplificado para este ejemplo, pero ten en cuenta que la respuesta de NIST NVD es bastante completa, por lo que hay mucha información valiosa que podrías extraer.

3. Generando la salida de datos

Finalmente, escribimos los datos en un archivo JSON:

#
# Output the result
with open(output_file, 'w') as f:
    json.dump(data, f)

Aquí es donde podríamos almacenar en caché los datos, enviar notificaciones, y en general, hacer algún tipo de análisis inteligente con los datos obtenidos.

Probando el script

Vamos a probar nuestro script.

Este es el archivo inventory.csv que proporcionaremos:

Nginx Web Server,f5,nginx,1.18.0,a
SIMATIC S7-1500,siemens,simatic_s7-1500_cpu_firmware,0.5.1,o

Ejecutamos el script de la siguiente manera:

$ python vuln_finder.py -d inventory.csv -o output.json -k THE-API-KEY

Y obtenemos un archivo de salida como este:

$ cat output.json | jq .
[
  {
    "name": "Nginx Web Server",
    "vendor": "f5",
    "software": "nginx",
    "version": "1.18.0",
    "part": "a",
    "cpe": "cpe:2.3:a:f5:nginx:1.18.0",
    "cves": [
      {
        "id": "CVE-2021-23017",
        "url": "url: https://nvd.nist.gov/vuln/detail/CVE-2021-23017",
        "baseScore": 7.7,
        "baseSeverity": "HIGH"
      },
      {
        "id": "CVE-2021-3618",
        "url": "url: https://nvd.nist.gov/vuln/detail/CVE-2021-3618",
        "baseScore": 7.4,
        "baseSeverity": "HIGH"
      },
      {
        "id": "CVE-2023-44487",
        "url": "url: https://nvd.nist.gov/vuln/detail/CVE-2023-44487",
        "baseScore": 7.5,
        "baseSeverity": "HIGH"
      }
    ]
  },
  {
    "name": "SIMATIC S7-1500",
    "vendor": "siemens",
    "software": "simatic_s7-1500_cpu_firmware",
    "version": "0.5.1",
    "part": "o",
    "cpe": "cpe:2.3:o:siemens:simatic_s7-1500_cpu_firmware:0.5.1",
    "cves": [
      {

¡Genial! ¡Funciona!

Nota

Si quieres aprender más o probarlo tú mismo, revisa todo el código en nuestro repositorio de GitHub.

Próximos pasos: Automatización y Correlación, la base del OTSPM

Hemos visto lo fácil que es automatizar el descubrimiento de vulnerabilidades consultando las APIs de bases de datos de vulnerabilidades. ¿A dónde ir desde aquí?

Las tendencias en ciberseguridad son:

  • Automatización de todas las tareas manuales para evitar errores humanos y proporcionar siempre información actualizada.
  • Correlación de los datos recopilados de varias fuentes para detectar más amenazas.

Tomemos como ejemplo la automatización del inventario.

Mantener un inventario en una hoja de cálculo parece fácil. Sin embargo, ¿qué sucede si se añade un dispositivo o servicio malicioso? ¿Cuánto tiempo tardaríamos en detectarlo?

Si escaneas tu infraestructura continuamente para mantener una instantánea actualizada de la misma, puedes realizar correlaciones interesantes:

  • Nuevos recursos junto con alertas de seguridad en tiempo real: “Alguien accedió a una URL sospechosa al mismo tiempo que se agregó este nuevo dispositivo.”
  • Estado de los recursos en el descubrimiento de vulnerabilidades: “Este dispositivo tiene una vulnerabilidad crítica, pero es una máquina de pruebas que está apagada. Podemos priorizar otras alertas antes que esta.”
  • Estado de los recursos respecto a la conformidad: “Un cambio reciente en la configuración de este servidor lo ha hecho caer fuera de cumplimiento.”

Ten esto en cuenta, ya sea que estés construyendo tus propias soluciones de seguridad o utilizando un producto de un proveedor, se ha demostrado que la seguridad es más efectiva cuando se trata como una solución integral, donde cualquier información puede ser útil en cualquier etapa.

Conclusión

Las bases de datos de vulnerabilidades te ayudan a descubrir dónde eres vulnerable y a tomar medidas de mitigación.

Puedes automatizar este proceso de descubrimiento integrando tu inventario con sus APIs, lo cual se puede hacer fácilmente en cualquier lenguaje de programación popular. Incluso puedes hacer consultas desde una hoja de cálculo (aunque no es recomendable).

Finalmente, avanzar en esta ruta de automatización te abrirá la posibilidad de correlacionar datos de múltiples fuentes, haciendo tu solución de seguridad más efectiva.


Acelera Tu Flow

La plataforma OTSMP de Safetybits automatiza tareas y te proporciona información para que puedas pasar directamente a la acción.