
Fuzzer de DOM
Escrito y mantenido por Ivan Fratric, [email protected]
Copyright 2017 Google Inc. Todos los derechos reservados.
Licenciado bajo la Apache License, Versión 2.0 (la "Licencia"); no puede utilizar este archivo excepto en cumplimiento de la Licencia. Puede obtener una copia de la Licencia en
http://www.apache.org/licenses/LICENSE-2.0
Salvo que lo exija la ley aplicable o se haya acordado por escrito, el software distribuido bajo la Licencia se distribuye "TAL CUAL", SIN GARANTÍAS NI CONDICIONES DE NINGÚN TIPO, ya sean expresas o implícitas. Consulte la Licencia para conocer el lenguaje específico que rige los permisos y las limitaciones bajo la Licencia.
Para ver la información de uso, ejecute el siguiente comando:
python3 generator.py --help
Para generar una única muestra .html, ejecute:
python generator.py --file <output file>
Para generar una única muestra .html usando una plantilla que haya escrito:
python generator.py --file <output file> --template <your custom template file>
Para generar varias muestras con una sola llamada, ejecute:
python generator.py --output_dir <output directory> --no_of_files <number of output files>
Las muestras generadas se colocarán en el directorio especificado y se nombrarán como fuzz-<number>.html, por ejemplo, fuzz-00001.html, fuzz-00002.html, etc. La generación de varias muestras es más rápida porque los archivos de gramática de entrada solo necesitan cargarse y analizarse una vez.
generator.py contiene el script principal. Utiliza grammar.py como biblioteca y contiene código auxiliar adicional para el fuzzing de DOM.
grammar.py contiene el motor de generación, que es en su mayoría independiente de la aplicación y, por lo tanto, puede usarse en otros fuzzers basados en generación (es decir, no DOM). Como puede usarse como biblioteca, su uso se describe en una sección separada más abajo.
Los archivos .txt contienen definiciones de gramática. Hay 3 archivos principales, html.txt, css.txt y js.txt, que contienen las gramáticas de HTML, CSS y JavaScript, respectivamente. Estos archivos de gramática raíz pueden incluir contenido de otros archivos.
Para usar el motor de generación con una gramática personalizada, puede utilizar el siguiente código Python:
from grammar import Grammar
my_grammar = Grammar()
my_grammar.parse_from_file('input_file.txt')
result_string = my_grammar.generate_symbol('symbol_name')
Las siguientes secciones describen la sintaxis de los archivos de gramática.
Domato se basa en un motor que, dada una gramática libre de contexto en un formato simple especificado a continuación, genera muestras a partir de esa gramática.
Una gramática se describe como un conjunto de reglas en el siguiente formato básico:
<symbol> = a mix of constants and <other_symbol>s
Cada regla de gramática contiene un lado izquierdo y un lado derecho separados por el carácter igual. El lado izquierdo contiene un símbolo, mientras que el lado derecho contiene los detalles sobre cómo puede expandirse ese símbolo. Al expandir un símbolo, todos los símbolos del lado derecho se expanden recursivamente, mientras que todo lo que no es un símbolo simplemente se copia a la salida. Tenga en cuenta que una sola regla no puede abarcar varias líneas del archivo de entrada.
Considere el siguiente ejemplo simplificado de una parte de la gramática CSS:
<cssrule> = <selector> { <declaration> }
<selector> = a
<selector> = b
<declaration> = width:100%
Si indicamos al motor de gramática que analice esa gramática y genere 'cssrule', podríamos obtener:
a { width:100% }
o
b { width:100% }
Tenga en cuenta que hay dos reglas para el símbolo 'selector'. En tales casos, cuando se le pide al generador que genere un 'selector', seleccionará la regla a usar al azar. También es posible especificar la probabilidad de la regla usando el atributo 'p', por ejemplo:
<selector p=0.9> = a
<selector p=0.1> = b
En este caso, la cadena 'a' se generaría con más frecuencia que 'b'.
Además de la probabilidad, hay otros atributos que pueden aplicarse a los símbolos. Estos se enumeran en una sección separada.
Considere otro ejemplo para generar muestras html:
<html> = <lt>html<gt><head><body><lt>/html<gt>
<head> = <lt>head<gt>...<lt>/head<gt>
<body> = <lt>body<gt>...<lt>/body<gt>
Tenga en cuenta que, dado que '<' y '>' tienen un significado especial en la sintaxis de la gramática, aquí usamos <lt> y <gt> en su lugar. Estos símbolos están integrados y no necesitan ser definidos por el usuario. En una sección separada se proporciona una lista de todos los símbolos integrados.
Para generar código de lenguaje de programación se puede usar una sintaxis similar, pero hay un par de diferencias. Cada línea de la gramática del lenguaje de programación corresponderá a una línea de la salida. Debido a eso, la sintaxis de la gramática será más libre para permitir expresar construcciones en varios lenguajes de programación. En segundo lugar, cuando se genera una línea, además de emitir la línea, pueden crearse una o más variables, y esas variables pueden reutilizarse al generar otras líneas. De nuevo, echemos un vistazo al ejemplo simplificado:
!varformat fuzzvar%05d
!lineguard try { <line> } catch(e) {}
!begin lines
<new element> = document.getElementById("<string min=97 max=122>");
<element>.doSomething();
!end lines
Si indicamos al motor que genere 5 líneas, podríamos obtener algo como:
try { var00001 = document.getElementById("hw"); } catch(e) {}
try { var00001.doSomething(); } catch(e) {}
try { var00002 = document.getElementById("feezcqbndf"); } catch(e) {}
try { var00002.doSomething(); } catch(e) {}
try { var00001.doSomething(); } catch(e) {}
Tenga en cuenta que
<new element> en lugar de <element>. Esto le indica al generador que cree una nueva variable de tipo 'element' en lugar de generar el símbolo 'element'.<string> es uno de los símbolos integrados, por lo que no es necesario definirlo.Todo lo que esté después del primer carácter '#' en la línea se considera un comentario, por ejemplo:
#This is a comment
La sintaxis de la gramática tiene una forma de indicar al fuzzer qué reglas no son recursivas y pueden usarse de forma segura incluso si se ha alcanzado el nivel máximo de recursión. Esto se hace con el atributo ‘nonrecursive’. A continuación se muestra un ejemplo.
!max_recursion 10
<test root=true> = <foobar>
<foobar> = foo<foobar>
<foobar nonrecursive> = bar
En primer lugar, una sentencia opcional ‘!max_recursion’ define el nivel máximo de profundidad de recursión (50 por defecto). Observe que la segunda regla de producción para ‘foobar’ está marcada como no recursiva. Si se alcanza el nivel máximo de recursión, el generador forzará el uso de la regla no recursiva para el símbolo ‘foobar’, evitando así la recursión infinita.
En Domato, incluir e importar gramáticas son dos contextos diferentes.
La inclusión es más simple. Puede usar:
!include other.txt
para incluir reglas de other.txt en la gramática que se está analizando actualmente.
La importación funciona de forma un poco diferente:
!import other.txt
le indica al analizador que cree un nuevo objeto Grammar() al que luego se pueda hacer referencia desde la gramática actual mediante el símbolo especial <import>, por ejemplo así:
<cssrule> = <import from=css.txt symbol=rule>
Puede pensar en la importación y la inclusión en términos de espacios de nombres: !include colocará la gramática incluida en el espacio de nombres único, mientras que !import creará un nuevo espacio de nombres al que se podrá acceder usando el símbolo <import> y el espacio de nombres especificado mediante el atributo 'from'.
A veces es posible que desee llamar a código Python personalizado en su gramática. Por ejemplo, supongamos que quiere usar el motor para generar una respuesta http y desea que la longitud del cuerpo coincida con el encabezado 'Size'. Dado que esto no es posible con las reglas de gramática normales, puede incluir código Python personalizado para lograrlo de la siguiente manera:
!begin function savesize
context['size'] = ret_val
!end function
!begin function createbody
n = int(context['size'])
ret_val = 'a' * n
!end function
<foo root> = <header><cr><lf><body>
<header> = Size: <int min=1 max=20 beforeoutput=savesize>
<body> = <call function=createbody>
Las funciones de Python se definen entre los comandos ‘!begin function <function_name>’ y ‘!end function’. Las funciones se pueden llamar de dos maneras: usando el atributo ‘beforeoutput’ y usando el símbolo <call>.
Al especificar el atributo ‘beforeoutput’ en algún símbolo, la función correspondiente se llamará cuando este símbolo se expanda, justo antes de que el resultado de la expansión se emita a la muestra. El resultado de la expansión se pasará a la función en la variable ret_val. La función es entonces libre de modificar ret_val, almacenarlo para uso posterior o realizar cualquier otra operación.
Al usar el símbolo especial <call>, la función (especificada en un atributo ‘function’) se llamará cuando el símbolo se encuentre durante la generación del lenguaje. Cualquier valor almacenado por la función en ret_val se considerará el resultado de la expansión (ret_val se incluye en la muestra).
Su código Python tiene acceso a las siguientes variables:
context - un diccionario que se pasa a lo largo de toda la generación de la muestra. Puede usarlo para almacenar valores (como almacenar el tamaño en el ejemplo anterior) y recuperarlos en las reglas que se activan posteriormente.attributes - un diccionario correspondiente al símbolo que se está procesando actualmente. Puede usarlo para pasar parámetros a sus funciones. Por ejemplo, si usó algo como <call function=func foo=bar> para llamar a su función, attributes[‘foo’] se establecerá en ‘bar’.ret_val - El valor que se emitirá como resultado de la llamada a la función. Se inicializa con un valor vacío cuando se usa el símbolo <call> para llamar a una función; de lo contrario, se inicializará con el valor generado por el símbolo.Los siguientes símbolos tienen un significado especial y no deben ser redefinidos por los usuarios:
<lt> - el carácter ‘<’<gt> - el carácter ‘>’<hash> - el carácter ‘#’<cr> - el carácter CR<lf> - el carácter LF<space> - el carácter de espacio<tab> - el carácter de tabulación<ex> - el carácter ‘!’<char> - se puede usar para generar un carácter ASCII arbitrario mediante el atributo ‘code’. Por ejemplo, <char code=97> corresponde a ‘a’. Genera un carácter aleatorio si no se especifica. Admite los atributos ‘min’ y ‘max’.<hex> - genera un dígito hexadecimal aleatorio.<int>, <int 8>, , , , , , , - se pueden usar para generar enteros aleatorios. Admite los atributos ‘min’ y ‘max’, que pueden usarse para limitar el rango de enteros que se generarán. Admite los atributos ‘b’ y ‘be’, que hacen que la salida sea binaria en formato little/big endian en lugar de salida de texto.Se admiten los siguientes atributos:
‘doSomething(<int id=1>, <int id=1>)’ ambos ints terminarían teniendo el mismo valor. Solo se expande realmente la primera instancia; la segunda simplemente se copia de la primera.<call>; consulte la sección ‘Inclusión de código Python’ para obtener más información.Algunos de los bugs que se han encontrado con Domato:
Este no es un producto oficial de Google.
<uint8><int16><uint16><int32><uint32><int64><uint64><float>, <double> - generan un número de punto flotante aleatorio. Admite los atributos ‘min’ y ‘max’ (0 y 1 si no se especifican). Admite el atributo ‘b’, que hace que la salida sea binaria.<string> - genera una cadena aleatoria. Admite los atributos ‘min’ y ‘max’, que controlan el código de carácter mínimo y máximo generado, así como los atributos ‘minlength’ y ‘maxlength’, que controlan la longitud de la cadena.<htmlsafestring> - igual que <string>, excepto que los metacaracteres HTML se escaparán, lo que hace seguro incrustar la cadena como parte del texto HTML o de los valores de atributos.<lines> - emite el número dado (mediante el atributo ‘count’) de líneas de código. Consulte la sección sobre generación de código de lenguaje de programación para ver un ejemplo.<import> - importa un símbolo de otra gramática; consulte la sección sobre inclusión de gramáticas externas para obtener más detalles.<call> - llama a una función definida por el usuario correspondiente al atributo function. Consulte la sección sobre inclusión de código Python en la gramática para obtener más información.