"¡Hola, Checkmarx!" Cómo escribir una solicitud para Checkmarx SAST y encontrar vulnerabilidades interesantes



Hola Habr!

En el artículo quiero hablar sobre nuestra experiencia en la creación de mis consultas en Checkmarx SAST.

Cuando se familiarice por primera vez con este analizador, puede tener la impresión de que, además de buscar algoritmos de cifrado / hashing débiles y un montón de falsos positivos, no devuelve nada más. Pero cuando se configura correctamente, es una herramienta súper poderosa que puede buscar errores graves.

Entenderemos las complejidades del lenguaje de consulta Checkmarx SAST y escribiremos 2 consultas para buscar inyecciones SQL y referencias directas a objetos inseguros.


Entrada


Después de una larga búsqueda de guías o artículos sobre Checkmarx, me quedó claro que, además de la documentación oficial, no había suficiente información útil. Y la documentación oficial no dice que todo se está volviendo muy claro y comprensible. Por ejemplo, no pude encontrar las mejores prácticas, cómo organizar las consultas de anulación correctamente, cómo escribir consultas "para tontos", etc. Sí, hay documentación sobre las funciones del lenguaje de consulta CMx, pero aquí se explica cómo combinar estas funciones en una sola consulta, La documentación no está escrita.

Quizás la falta de artículos y guías de la comunidad de Checkmarx esté asociada con el alto costo de la herramienta y, como resultado, con una pequeña audiencia. O tal vez solo algunas personas se molestan con el ajuste fino y usan la solución tal como está, lista para usar.

En mi experiencia, veo más que SAST se usa más para cumplir con las formalidades relacionadas con diversos requisitos por parte de los clientes que para buscar errores reales. Con este enfoque, como resultado, tenemos, en el mejor de los casos, un número relativamente pequeño de "vulnerabilidades", que casi automáticamente se conocen como "no explotables" (porque lo son en el 99.9% de los casos).

Cabe señalar que Checkmarx está tratando de actualizar sus consultas para obtener el mejor resultado de forma inmediata. Pero las consultas de CMx Query Language se adaptan al "caso general". La búsqueda inicial de tokens se basa en el nombre. Por ejemplo, CMx SAST supone que todas las consultas a la base de datos se verán así: * createQuery * o * createSQLQuery *. Pero si se utiliza el desarrollo interno para trabajar con la base de datos, y el método para consultar la base de datos se llama de manera diferente, por ejemplo * driveMyQuery *, entonces se omitirán todos los métodos SQL. Por ejemplo, nuestro cliente usa ORM personalizado para SQL DB. En este caso, las consultas CMx listas para usar omitieron todas las inyecciones de SQL.

Abreviaturas y Definiciones


CMx - Checkmarx SAST.
CMxQL - lenguaje de consulta Checkmarx SAST
Token : una cadena con cierto valor es el resultado del trabajo del analizador léxico (que también se denomina tokenización)

Aplicación de prueba


Para escribir un artículo, dibujé un código Java, una pequeña aplicación de prueba. Este código es una copia aproximada de una pequeña parte del sistema real. Aunque, en general, el código de la aplicación de prueba no es muy diferente de cualquier otro código de back-end HTTP. Las secciones clave del código de la aplicación de prueba serán visibles en las capturas de pantalla.

La aplicación de prueba tiene la siguiente estructura


Clase WebRouter para procesar solicitudes HTTP entrantes; 4 métodos para procesar URL dentro:
  • / getTransaction : acepta el ID de la transacción en la entrada y devuelve la información que contiene , el ID lo toma como una cadena y lo pasa a getTransactionInfo (transacciónId) => getTransactionInfo (transactoinId) : hace que el ID de transacción se concatene con la consulta SQL (es decir, se obtiene la inyección SQL);
  • / getSecureTransaction : acepta el ID de la transacción como entrada y devuelve la información que contiene , el id lo toma como una cadena y lo pasa getTransactionInfoSecured () => getTransactionInfoSecured (transactoinId) : primero emite el string ID de transacción para escribir Long y luego lo concatena en una consulta SQL caso la inyección no se explota);
  • / getSettings : acepta el ID de usuario y el ID de buzón como entrada , y emite la configuración del buzón. No verifica que el buzón de correo pertenece al usuario;
  • / getSecureSettings : también acepta el ID de usuario y el ID de buzón en la entrada y muestra la configuración del buzón. PERO comprueba que el buzón de correo pertenece al usuario.


CMx: información general y definiciones básicas


Antes de comenzar a desarrollar consultas


El desarrollo de consultas se lleva a cabo en un programa separado CxAuditor. En CxAuditor necesita escanear todo el código (crear un proyecto local), para lo cual escribiremos consultas. Después de eso, puede escribir y ejecutar nuevas consultas. Con una base de código grande, el escaneo primario puede tomar horas de tiempo y gigabytes de memoria. Después de eso, cada solicitud no se ejecutará lo suficientemente rápido. Esto es completamente inadecuado para el desarrollo.

Por lo tanto, puede tomar un pequeño conjunto de archivos del proyecto, idealmente con un error encontrado en el código antes del tipo bajo el cual estamos escribiendo la solicitud (o colocar el error allí manualmente) y escanear solo este conjunto de archivos. No es necesario cumplir con la estructura de archivos del proyecto. Es decir, si tiene el paquete A y B de Java, y las clases en el paquete B usan las clases y los métodos del paquete A, puede poner todo esto en un solo directorio, y CMx aún entenderá las relaciones y creará cadenas de llamadas entre archivos correctamente (bueno, o casi siempre correcto, aunque los errores apenas están relacionados con la estructura de archivos del proyecto).

Definiciones básicas


Cxlist


El tipo de datos principal en CMx. El resultado de casi todas las funciones CMxQL será CxList . Estos son muchos elementos con ciertas propiedades. Las propiedades que son más útiles para el desarrollo se considerarán a continuación.

resultado


CMxQL tiene un resultado variable incorporado. El conjunto que contiene la variable de resultado , después de ejecutar la consulta completa, se mostrará como resultado.

Es decir, la operación final de cualquier consulta debe ser el resultado de la cadena = LO QUE SEA , por ejemplo:
result = All.FindByName("anyname"); 

elemento de flujo y código


La mayoría de las funciones CMxQL por tipo de valores devueltos se dividen en 2, las que devuelven "elementos de código" y las que devuelven Flow. En ambos casos, el resultado es una CxList . Pero su contenido será ligeramente diferente para los elementos de flujo y código.
  • Elemento de código - token - por ejemplo, una variable, llamada a método, asignación, etc.
  • Flujo : la relación entre los tokens dados.


Todos y "sub" Todos


Cada función CMxQL se puede realizar en el conjunto Todos (contiene todos los tokens de todo el código escaneado, ya vimos un ejemplo con resultado ) o en el conjunto CxList , que a su vez se obtuvo como resultado de algunas operaciones en la consulta, por ejemplo, la consulta:
 CxList newList = CxList.New(); 

creará un conjunto vacío, que luego podemos llenar con elementos usando el método Add () , y luego buscar ya por los elementos del nuevo conjunto:
 CxList newFind = newList.FindByName("narrowedScope"); 

Propiedades de los elementos encontrados.


Cada elemento del conjunto CxList tiene varias propiedades. Al analizar los resultados para escribir consultas, los más útiles son:

  • SourceFile : el nombre del archivo que contiene este elemento;
  • Línea de origen : número de línea con token;
  • Nombre de origen : el nombre del token. Equivalente al token, es decir, si la variable se llama var1, entonces Nombre de origen = var1;
  • Tipo de origen : el tipo de token. Por ejemplo, si es una cadena, será StringLiteral, si se llama al método, luego MethodInvokeExpr y muchos otros;
  • Archivo de destino
  • Línea de destino;
  • Nombre del destino;
  • Tipo de destino


Origen y Destino serán diferentes si los elementos del conjunto de resultados son Flujo, y viceversa, coincidirán si el resultado son elementos de código.

Comience a crear consultas


Todas las funciones CMxQL se pueden dividir en varios tipos. Aquí, en mi opinión, se puede observar el principal inconveniente de la documentación de CMxQL, todas las funciones en el dock se describen simplemente en orden alfabético, mientras que sería mucho más conveniente estructurarlas de acuerdo con la funcionalidad y solo alfabéticamente.

  • Funciones de búsqueda: casi todas las funciones CMxQL con el nombre FindBy * y GetBy * ;
  • Las funciones de las operaciones en conjuntos son suma, resta, intersección, iteración sobre elementos, etc.
  • Funciones de análisis: estas son básicamente funciones * InfluencedBy * * InfluencingOn * .


El principio básico de las consultas es la alternancia de este tipo de funciones. Primero, usando las funciones de búsqueda, seleccionamos solo los tokens que nos interesan por ciertas propiedades. Usando operaciones en conjuntos, podemos combinar diferentes conjuntos con diferentes propiedades de token en uno, o viceversa, restar el otro de uno. Luego, utilizando las funciones de análisis, creamos Code Flow e intentamos comprender si las vulnerabilidades potenciales dependen de los parámetros en los puntos de entrada.

La elección del lugar desde el que comenzar a buscar y, en general, la ruta de búsqueda completa, depende del código específico y, más precisamente, incluso del "texto". En algunos casos, es conveniente buscar consultas de los usuarios desde el punto de entrada, en algunos casos es más conveniente comenzar desde el "final" o incluso desde el medio. Todo depende del código específico y debe acercarse individualmente a cada repositorio.

Ejemplo: Buscar inyección SQL


Plan de búsqueda, entre paréntesis indiqué el nombre de los conjuntos (variables en la consulta):

  1. Definir excepciones: tokens que pueden eliminarse inmediatamente de los ámbitos de búsqueda ( exclusionList );
  2. Determinar la ubicación de los controles de seguridad / desinfección ( desinfección );
  3. Encuentre todos los lugares de bajo nivel con ejecución de consultas en la base de datos ( runSuperSecureSQLQuery );
  4. Encuentre todos los parámetros de los métodos llamados runSuperSecureSQLQuery ( runSSSQParams );
  5. Encuentre puntos de entrada (métodos principales y sus parámetros) para los lugares de ejecución de consultas en la base de datos ( entryPointsParameters );
  6. Encuentre las dependencias de los parámetros runSSSQParams en entryPoints , mientras que solo aquellos lugares donde no hay desinfección de la desinfección de entrada.


Como resultado, obtenemos métodos de bajo nivel con consultas SQL, donde los parámetros de la consulta SQL:

  • dependerá de los parámetros del método;
  • los parámetros se aceptan como cadenas;
  • los parámetros se concatenan a la solicitud.

No comprobaremos si podemos controlar estos parámetros, como creemos que existe un mecanismo para mapear variables en una consulta y hay una conversión a un tipo numérico para números, y la concatenación de cadenas siempre se considera peligrosa. Incluso si no hay control sobre la línea ahora, bien puede aparecer en la nueva versión.

SQLi: Paso 1. Definición de excepciones


En excepciones, debe agregar aquellas clases o archivos donde los nombres de token puedan coincidir con los que está buscando, porque Estos tokens conducirán a entradas no válidas.

Por ejemplo, un método para acceder a una base de datos se llama runSuperSecureSQLquery . Suponemos que el método runSuperSecureSQLquery dentro se implementa de manera segura. Y nuestra tarea es encontrar lugares donde no sea seguro usar el método en sí. Para la inyección SQL, los lugares de concatenación de parámetros controlados por el usuario no serán lugares seguros. Y seguro: lugares para mapear parámetros en la estructura ORM o, por ejemplo, para parámetros numéricos, esto es una conversión al tipo correspondiente. No necesitamos escanear todo el código que se encuentra "más profundo" que runSuperSecureSQLquery , lo que significa que es mejor excluirlo para evitar hallazgos inútiles.

Para buscar tales excepciones, es conveniente utilizar las funciones CMxQL:
  • FindByFileName () : encontrará el conjunto de todos los tokens en un archivo en particular;
  • GetByClass () : encontrará el conjunto de todos los tokens en la clase con el nombre dado.


Para una aplicación de prueba, esta excepción es la clase Session , que contiene la implementación del método runSuperSecureSQLquery .
Un ejemplo de una solicitud para excluir código en la clase Session (el método GetByClass () verificará cuál de los tokens pasados ​​a la entrada tiene un tipo CMx de ClassDecl y emitirá muchos tokens de esta clase)

 CxList exclusionList = All.GetByClass(All.FindByName("*Session*")); result = exclusionList; 


O otra forma es lanzar código en todo el archivo Session.java :

 CxList exclusionList = All.FindByFileName("*Session.java"); result = exclusionList; 


El asterisco antes del nombre es importante, porque el nombre del archivo incluye la ruta completa.
Ahora tenemos muchos tokens que se pueden restar en los próximos pasos del ámbito de búsqueda.

El resultado de buscar tokens dentro de la clase Session :



SQLi: Paso 2. Determinar lugares de desinfección


Hay 2 métodos API en la aplicación de prueba (consulte una breve descripción de la aplicación de prueba). La diferencia entre los dos métodos de API es que getTransactionInfo () concatena el parámetro transacciónId en la consulta SQL, y getTransactionInfoSecured () primero convierte transacciónId a Largo, y luego lo pasa como una cadena. La vulnerabilidad (concatenación de parámetros) está integrada en ambos métodos. Pero gracias a la conversión a Long en getTransactionInfoSecured () , el último método no es vulnerable a la inyección, porque cuando intentamos pasar una inyección (cadena) obtenemos una excepción Java.

En este ejemplo, consideraremos el reparto a Long como el sitio de saneamiento. Para encontrar estos tokens:

 CxList sanitization = All.FindByName("*Long*"); result = sanitization; 


Resultado de ejemplo:



El resultado incluía tokens con los métodos YP tipo Long y getValueAsLong , que internamente convierten el valor en tipo Long . Debe revisar cuidadosamente el resultado para asegurarse de que no haya nada extra.

SQLi: Paso 3. Encuentre todos los lugares de bajo nivel con ejecución de consultas en la base de datos


La siguiente consulta encontrará todos los lugares utilizando el token runSuperSecureSQLQuery (que se utiliza para acceder a la base de datos):

 result = All.FindByName("*runSuperSecureSQLQuery*") 

Resultado de búsqueda por nombre de token runSuperSecureSQLQuery:


Además, para los lugares donde se llama a este método (la clase Billing ), solo se encontrarán tokens de invocación del método (tipo MethodInvokeExpr ), y para la ubicación de declaración del método (clase Session ), se encontrarán todos los tokens - variables.

Filtramos solo los tokens de llamada al método:

 CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); result = runSuperSecureSQLQuery; 

Resultado:


Como resultado, obtuvimos 7 lugares, 4 de ellos las llamadas requeridas al método runSuperSecureSQLQuery () (clases de facturación y usuario ). 2: llamadas al método interno runSuperSecureSQLQuery () dentro de la clase Session , y uno más es el método add , que es más bien una especie de rareza de búsqueda CMxQL. Digamos que no esperaba que estuviera en la lista =) Los tokens en la clase Session , como descubrimos en el paso 1, no son interesantes para nosotros, por lo que simplemente los restaremos del resultado:

 CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); result = runSuperSecureSQLQuery - exclusionList; 

Obtenemos una lista válida de llamadas al método requerido:



Tenga en cuenta las funciones FindByType () y typeof () en la consulta anterior. Si queremos buscar por tipo CMx, es decir, por la propiedad CxList "Tipo de fuente", entonces usamos typeof (Tipo de fuente) . Si queremos hacer una búsqueda por tipo de datos, entonces debemos pasar el parámetro solo como una cadena. Por ejemplo:

 result = All.FindByType("String"); 

encontrará todos los tokens java con tipo String.

SQLi: Paso 4. Encuentre todos los parámetros de los métodos llamados runSuperSecureSQLQuery


Para buscar parámetros de método, se utiliza la función CMxQL GetParameters () :

 CxList runSSSQParams = All.GetParameters(runSuperSecureSQLQuery); result = runSSSQParams; 

Resultado:



SQLi: Paso 5. Encuentre puntos de entrada para ubicaciones de ejecución de consultas en la base de datos


Para hacer esto, primero obtenemos los nombres de los métodos principales, dentro de los cuales se encuentran las llamadas a la base de datos runSuperSecureSQLQuery , y luego obtenemos sus parámetros. Para buscar tokens primarios, se utiliza la función CMAXQL GetAncOfType () :

 CxList entryPoints = runSuperSecureSQLQuery.GetAncOfType(typeof(MethodDecl)); result = entryPoints; 


En esta consulta, para el conjunto runSuperSecureSQLQuery, devuelva todos los tokens primarios del tipo MethodDecl; este es el método anterior en la pila de llamadas:



Para buscar parámetros de métodos, también utilizamos GetParameters () :

 CxList entryPointsParameters = All.GetParameters(entryPoints).FindByType("String"); 


La consulta devolverá los parámetros de un subconjunto de entryPoints con el tipo Java String:



SQLi: Paso 6. Encuentre las dependencias de los parámetros runSSSQParams en entryPointsParameters, mientras que solo aquellos lugares donde no hay entrada de entrada de desinfección


En este paso, usamos las funciones de análisis. Las siguientes funciones se utilizan para analizar el código de flujo:

  • InfluencedBy ()
  • InfluencedByAndNotSanitized ()
  • InfluencingOn ()
  • InfluencingOnAndNotSanitized ()
  • NotInfluencedBy ()
  • NotInfluencingOn ()


Para encontrar el flujo de los parámetros de solicitud runSSSQParams en función de los parámetros del método padre entryPointsParameters y excluir los tokens de saneamiento:

 CxList dataInflOnTable = runSSSQParams.InfluencedByAndNotSanitized(entryPointsParameters, sanitization); 


Sin embargo, no estoy seguro de si las funciones * AndNotSanitized dentro hacen algo de magia, y parece que el método simplemente resta el conjunto desinfectado de su resultado. Es decir, si haces:

 CxList dataInflOnTable = runSSSQParams.InfluencedBy(entryPointsParameters) - sanitization; 


Resulta lo mismo. Aunque tal vez simplemente no encontré una opción cuando todavía hay diferencias.

El resultado de la consulta nos da un flujo correctamente construido:



Got Flow con potencial inyección de SQL. Como se puede ver en la captura de pantalla, Checkmarx devolvió 3 Flow. El flujo en la captura de pantalla es el más corto, comienza y termina en un archivo y un método. El siguiente flujo ya sale en la clase Session. Presta atención a la fuente / destino. Y el último es otro método en la clase Session. El flujo dentro de la sesión se verá así:



Para seleccionar un flujo, se utiliza el método ReduceFlow (CxList.ReduceFlowType flowType) , donde flowType puede ser:

  • CxList.ReduceFlowType.ReduceBigFlow : seleccione el flujo más corto
  • CxList.ReduceFlowType.ReduceSmallFlow : seleccione el flujo más largo


SQLi: consulta final para encontrar inyección SQL


 // 1.   CxList exclusionList = All.GetByClass(All.FindByName("*Session*")); // 2.    CxList sanitization = All.FindByName("*Long*"); // 3.    runSuperSecureSQLQuery() CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); runSuperSecureSQLQuery -= exclusionList; // 4.     runSuperSecureSQLQuery() CxList runSSSQParams = All.GetParameters(runSuperSecureSQLQuery); // 5.   ,     runSuperSecureSQLQuery() CxList entryPoints = runSuperSecureSQLQuery.GetAncOfType(typeof(MethodDecl)); CxList entryPointsParameters = All.GetParameters(entryPoints).FindByType("String"); // 6.       (runSuperSecureSQLQuery)     CxList dataInflOnTable = runSSSQParams.InfluencedByAndNotSanitized(entryPointsParameters, sanitization); // 7.   result = dataInflOnTable.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow); 


Ejemplo 2: búsqueda de referencias directas inseguras a objetos


En esta solicitud, buscaremos todos los lugares donde se trabaje con objetos sin verificar el propietario del objeto. En este caso, se pueden usar diferentes nombres de parámetros HTTP para el buzón de correo (suponemos que es Legacy), y la verificación en sí misma puede ocurrir en diferentes etapas: en algún lugar justo en el punto API de entrada HTTP, en algún lugar antes de la solicitud a la base de datos, y a veces en métodos intermedios

Plan de búsqueda
  1. Definir excepciones ( exclusionList );
  2. Identificar lugares para verificaciones de autorización ( idorSanitizer );
  3. Encuentre puntos de entrada: lugares para el procesamiento primario de solicitudes HTTP ( webRemoteMethods );
  4. Solo mediante tokens de punto de entrada para encontrar la ubicación de extracción del parámetro HTTP buzónid ( buzónidInit );
  5. Encuentre todas las llamadas desde webRemoteMethods a métodos y parámetros de middleware de estas llamadas ( middlewareMethods );
  6. Encuentra métodos de middleware que dependen del buzón de correo ( apiPotentialIDOR );
  7. Encuentre todos los lugares donde se definen los métodos de middleware ( middlewareDecl );
  8. Revise todos los apiPotentialIDOR y seleccione solo aquellos middlewareDecl en los que no haya verificación del propietario del objeto de buzón .


IDOR: Paso 1. Identificar excepciones


En este caso, excluya todos los tokens en un archivo específico:

 CxList exclusionList = All.FindByFileName("*WebMethodContext.java"); result = exclusionList; 

WebMethodContext.java contiene una implementación de métodos como getMailboxId y getUserId , así como la cadena "buzónid". Dado que el nombre de los tokens coincidirá con los que necesitamos para buscar vulnerabilidades, este archivo emitirá resultados falsos.

IDOR: Paso 2. Localizar comprobaciones de autorización


En la aplicación de prueba, el método validateMailbox () se usa para determinar si el objeto solicitado pertenece al usuario:

 CxList idorSanitizer = All.FindByName("*validateMailbox*"); result = idorSanitizer; 

Resultado:



IDOR: Paso 3. Encuentre puntos de entrada para solicitudes de API HTTP personalizadas


Los manejadores de solicitudes HTTP tienen una anotación especial que los hace fáciles de encontrar. En mi caso, esto es "WebRemote", la función CMxQL FindByCustomAttribute () se usa para buscar anotaciones. Para FindByCustomAttribute () , la función de búsqueda del token principal GetAncOfType () devolverá el método bajo la anotación:

 CxList webRemoteMethods = All.FindByCustomAttribute("WebRemote") .GetAncOfType(typeof(MethodDecl)); result = webRemoteMethods; 


Resultado de la solicitud:



IDOR: Paso 4. Usando solo tokens de punto de entrada, encuentre las ubicaciones de extracción HTTP para el parámetro del buzón


Para buscar tokens relacionados con el procesamiento del parámetro de buzón HTTP:

 CxList getMailboxId = All.FindByName("\"mailboxId\"") + All.FindByName("\"mid\"") + All.FindByName("\"boxid\""); result = getMailboxId; 

agregamos 3 juegos con 3 líneas diferentes, porque Según la leyenda, el nombre del parámetro HTTP puede diferir en diferentes partes del sistema.

La consulta encontrará todos los lugares en los que se escribe boxid / mid / boxid como una cadena (entre comillas dobles). Pero esta consulta devolverá muchos hallazgos, tk. dicha cadena se puede encontrar no solo en lugares donde se extraen parámetros HTTP. Si continuamos trabajando con este conjunto, obtendremos una gran cantidad de hallazgos falsos.

Por lo tanto, solo buscaremos tokens de puntos de entrada ( webRemoteMethods ). Para encontrar todos los tokens secundarios, se utiliza la función CMBQL GetByAncs () :

 result = All.GetByAncs(webRemoteMethods); 

La solicitud devolverá todos los tokens que pertenecen a métodos anotados como WebRemote . Ya en esta etapa, podemos filtrar los tokens de aquellos métodos en los que se verifica el propietario del objeto. Por lo tanto, reescribimos la consulta anterior para buscar tokens secundarios de tal manera que seleccione solo los tokens secundarios de los métodos WebRemote , donde no hay verificación de seguridad para el propietario del objeto. Para hacer esto, use un bucle con la condición:

 //          CxList entry_point_tokens = All.NewCxList(); //      webRemoteMethods foreach (CxList method in webRemoteMethods) { //        CxList method_tokens = All.GetByAncs(method); // ,       ,    owner if (method_tokens.FindByName(idorSanitizer).Count > 0) { //  ,     , ,     } else { //  ,         entry_point_tokens.Add(method_tokens); } } 

Ahora podemos hacer una selección más precisa utilizando los parámetros de la casilla de correo HTTP:

 CxList getMailboxHTTPParams = entry_point_tokens.FindByName("\"mailboxid\"") + entry_point_tokens.FindByName("\"mid\"") + entry_point_tokens.FindByName("\"boxid\""); result = getMailboxHTTPParams; 

Pero no nos interesan los lugares donde se recuperan los parámetros HTTP, sino las variables a las que finalmente se les asignan los valores de los parámetros HTTP. Dado que es más confiable buscar Flow precisamente por tokens de variables.

La función CMxQL FindByInitialization () encontrará los lugares de inicialización variable para los tokens dados:

 CxList mailboxidInit = entry_point_tokens.FindByInitialization(getMailboxHTTPParams); result = mailboxidInit; 

Resultado:



IDOR: Paso 5. Encuentre todas las llamadas desde webRemoteMethods a métodos y parámetros de middleware de estas llamadas


Por middleware, me refiero a un código que es más profundo que los métodos de procesamiento de las solicitudes HTTP API, es decir, más profundo que los puntos de entrada de las solicitudes de los usuarios. Por ejemplo, para la captura de pantalla anterior, estos son métodos de la clase Usuario , llamadas a user.getSettings () y user.getSecureSettings () :

 CxList middlewareMethods = All.FindByShortName("user").GetRightmostMember(); CxList middlewareMethodsParams = entry_point_tokens.GetParameters(middlewareMethods); result = middlewareMethodsParams; 

Primero, seleccionamos todos los tokens con el nombre de usuario, y luego usando GetRightmostMember () seleccionamos los tokens de llamada para middleware. GetRightmostMember () en la cadena de llamadas a métodos devolverá el más a la derecha. Luego derivamos los parámetros del método encontrado usando GetParameters () .

Resultado:



IDOR: Paso 6. Encuentre métodos de middleware que dependan del buzón


El análisis de flujo utiliza los métodos * InfluencedBy * e * InfluncingOn * . La diferencia entre ellos es clara por su nombre.

Por ejemplo:

 All.InfluencedBy(getMailboxHTTPParams) 

irá a través del conjunto All y encontrará todos los tokens que dependen de getMailboxHTTPParams .

Lo mismo se puede escribir de otra manera:

 getMailboxHTTPParams.InfluencingOn(All) 


Para buscar tokens dependientes de buzónidInit :

 CxList apiPotentialIDOR = entry_point_tokens.InfluencedByAndNotSanitized(mailboxidInit, idorSanitizer); result = apiPotentialIDOR; 

Resultado:



IDOR: Paso 7. Encuentre todos los lugares para definir métodos de middleware


Encontremos las definiciones de todos los métodos intermedios que se pueden usar en lugares donde se procesan las solicitudes de los usuarios. Para hacer esto, destacamos su propiedad común, por ejemplo, en todos estos métodos existe la creación de un objeto Request () , la creación de un objeto es de tipo CMx ObjectCreateExpr :

 CxList requests = (All - exclusionList).FindByType(typeof(ObjectCreateExpr)).FindByName("*Request*"); CxList middlewareDecl = requests.GetAncOfType(typeof(MethodDecl)); result = middlewareDecl; 


(All - exclusionList) : puede hacer esta sustracción de conjuntos y luego llamar a la función CMxQL deseada del resultado. Solicitudes ahora contiene todos los tokens con el nombre Solicitud y el tipo correspondiente a la creación del objeto.

Luego, usando el conocido GetAncOfType (), encontramos el token padre de tipo MethodDecl .

Resultado:



IDOR: Paso 8. Revise todos los apiPotentialIDOR y seleccione solo aquellos middlewareDecl en los que no haya verificación del propietario del objeto de buzón


En la parte final de la solicitud, determinaremos a cuál de los métodos de middleware se llama directamente desde los métodos del punto de entrada y no verificaremos a quién pertenece el buzón . Luego combine Flow para un análisis más conveniente de los resultados.

Nuevas características que aún no hemos utilizado:
GetCxListByPath () : esta función es necesaria para iterar sobre Flow, si NO se usa, CMx comprimirá Flow en el elemento de código (en el primer nodo de flujo)
Concatenate * () : una serie de funciones necesarias para combinar varios flujos en uno
FindByParameters () : busca un método por un token de parámetro específico
GetName () : devolverá una cadena con el nombre del token; si hay más de un elemento en CxList, devolverá el primero. El método se usa solo cuando se itera sobre elementos de un conjunto.

La parte final de la solicitud:

 //    CxList vulns = All.NewCxList(); //   Flow  apiPotentialIDOR foreach(CxList cxFlow in apiPotentialIDOR.GetCxListByPath()) { //    Flow CxList endNode = cxFlow.GetStartAndEndNodes(CxList.GetStartEndNodesType.EndNodesOnly); //       flow (mailboxid) CxList method_call = entry_point_tokens.FindByParameters(endNode); //     CxList method_decl = middlewareDecl.FindByShortName(method_call.GetName()); //     if (method_decl.Count > 0) { //       CxList _all = (All - exclusionList).GetByAncs(method_decl); //       if (_all.FindByName(idorSanitizer).Count > 0) { //  ,       cxLog.WriteDebugMessage("find sanitized in method: " + method_call.GetName()); //  ,   Flow     vulns } else { //     Flow       vulns.Add(cxFlow.ConcatenatePath(method_call).ConcatenatePath(method_decl)); cxLog.WriteDebugMessage("find NOT sanitized in method: " + method_call.GetName()); } } } 


Resultado:



Utilizamos CocatenatePath para que al analizar todas las ubicaciones sea conveniente navegar a través del código. Este método adjunta un elemento de código al flujo.

IDOR: búsqueda final de IDOR


 // 1.   CxList exclusionList = All.FindByFileName("*WebMethodContext.java"); // 2.     CxList idorSanitizer = All.FindByName("*validateMailbox*"); // 3.    –    HTTP  CxList webRemoteMethods = All.FindByCustomAttribute("WebRemote").GetAncOfType(typeof(MethodDecl)); // 4.         HTTP  mailboxid //     CxList entry_point_tokens = All.NewCxList(); foreach (CxList method in webRemoteMethods) { CxList method_tokens = All.GetByAncs(method); if (method_tokens.FindByName(idorSanitizer).Count > 0) { } else { entry_point_tokens.Add(method_tokens); } } //    HTTP    -  CxList getMailboxHTTPParams = entry_point_tokens.FindByName("\"mailboxId\"") + entry_point_tokens.FindByName("\"mid\"") + entry_point_tokens.FindByName("\"boxid\""); CxList mailboxidInit = entry_point_tokens.FindByInitialization(getMailboxHTTPParams); // 5.      middleware     CxList middlewareMethods = All.FindByShortName("user").GetRightmostMember(); CxList middlewareMethodsParams = entry_point_tokens.GetParameters(middlewareMethods); // 6.  middleware ,     mailboxid CxList apiPotentialIDOR = entry_point_tokens.InfluencedByAndNotSanitized(mailboxidInit, idorSanitizer); // 7.      middleware      CxList requests = (All - exclusionList).FindByType(typeof(ObjectCreateExpr)).FindByName("*Request*"); CxList middlewareDecl = requests.GetAncOfType(typeof(MethodDecl)); // 8.    apiPotentialIDOR     middlewareDecl,      CxList vulns = All.NewCxList(); foreach(CxList cxFlow in apiPotentialIDOR.GetCxListByPath()) { CxList endNode = cxFlow.GetStartAndEndNodes(CxList.GetStartEndNodesType.EndNodesOnly); CxList method_call = entry_point_tokens.FindByParameters(endNode); CxList method_decl = middlewareDecl.FindByShortName(method_call.GetName()); if (method_decl.Count > 0) { CxList _all = (All - exclusionList).GetByAncs(method_decl); if (_all.FindByName(idorSanitizer).Count > 0) { cxLog.WriteDebugMessage("find sanitized in method: " + method_call.GetName()); } else { vulns.Add(cxFlow.ConcatenatePath(method_call).ConcatenatePath(method_decl)); cxLog.WriteDebugMessage("find NOT sanitized in method: " + method_call.GetName()); } } } result = vulns; 


Conclusión


Checkmarx analiza fácilmente el código en tokens, mientras determina sus tipos. Además, el analizador estático realiza bien una búsqueda simple de tokens, por ejemplo, encuentra el token padre para el actual, encuentra la inicialización de la variable, encuentra los parámetros de los métodos, etc. El flujo es casi tan bueno en la construcción (pero a veces todavía falla). Todo esto hace posible trabajar con el código como con cualquier base de datos, con la diferencia de que la estructura del código no está predefinida, y usted tiene que "personalizarlo" usted mismo.

Para reducir en gran medida la cantidad de falsos positivos, debe prestar atención a lo siguiente:
  • , ( ).
  • , ( ). , «Privacy Violation», , , Web UI. , .. UI . TLS XSS .
  • - , (, ). , XXE , , - , .
  • false positive, , CMxQL FindBy/GetBy. , ( SQL).
  • false positives, , , , , CMx, . , LDAP , . c LDAP- , , .


how-to «hello world» , Checkmarx.

Source: https://habr.com/ru/post/477742/


All Articles