diff --git a/Dockerfile b/Dockerfile index f4c2655..43bc347 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# docker pull andmyhacks/httpscreenshot +# docker pull jesseosiecki/httpscreenshot FROM ubuntu:20.04 @@ -19,3 +19,5 @@ RUN ln -s /etc/httpscreenshot/httpscreenshot.py /usr/bin/httpscreenshot RUN mkdir -p /etc/httpscreenshot/images WORKDIR /etc/httpscreenshot/images + +ENTRYPOINT ["httpscreenshot"] diff --git a/README.md b/README.md index eaae0dd..2d87285 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # httpscreenshot +### Installation via Docker + +`docker pull jesseosiecki/httpscreenshot` +`docker run jesseosiecki/httpscreenshot` + ### Installation on Ubuntu #### Via Script Run `install-dependencies.sh` script as root. -This script has been tested on Ubuntu 14.04. +This script has been tested on Ubuntu 20.04 as *root* (sudo). ### Manually diff --git a/httpscreenshot.py b/httpscreenshot.py index 6b6ed02..6794d8d 100644 --- a/httpscreenshot.py +++ b/httpscreenshot.py @@ -8,7 +8,6 @@ import re import shutil import signal -import socket import ssl import sys import time @@ -16,13 +15,20 @@ from importlib import reload from random import shuffle from urllib.parse import urlparse +from collections import defaultdict + +import warnings + +warnings.filterwarnings("ignore") import M2Crypto -from libnmap.parser import NmapParser from PIL import Image, ImageDraw, ImageFont from pyvirtualdisplay import Display from selenium import webdriver +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from webdriver_manager.chrome import ChromeDriverManager try: from urllib.parse import quote @@ -32,7 +38,6 @@ try: import requesocks as requests except Exception: - print("requesocks library not found - proxy support will not be available") import requests reload(sys) @@ -76,8 +81,7 @@ def detectFileType(inFile): # Be polite and reset the file pointer inFile.seek(0) - if (firstLine.find("nmap") != -1 or - firstLine.find("Masscan") != -1) and thirdLine.find("Host:") != -1: + if (firstLine.find("nmap") != -1 or firstLine.find("Masscan") != -1) and thirdLine.find("Host:") != -1: # Looks like a gnmap file - this wont be true for other nmap output types # Check to see if -sV flag was used, if not, warn if firstLine.find("-sV") != -1 or firstLine.find("-A") != -1: @@ -96,44 +100,34 @@ def detectFileType(inFile): def parsexml(inFile): - targets = {} - infile = NmapParser.parse_fromfile(args.input) - for host in infile.hosts: - if host.services: - currentTarget = [] - for s in host.services: - if s.state != "closed" and "http" in s.service: - ip = host.address - port = str(s.port) - https = False - if "https" in s.service or "ssl" in s.service: - https = True - - currentTarget.append([port, https]) - - if len(currentTarget) > 0: - targets[ip] = currentTarget + import xml.etree.ElementTree as ET + + tree = ET.parse(inFile) + root = tree.getroot() + + targets = defaultdict(list) + + for host in root.findall("host"): + ip = host.find('address').get('addr') + + for port in host.find('ports').findall("port"): + if port.find("state").get("state") == "open": + targets[ip].append(port.get("portid")) return targets - print("Parsing is complete, continue on...") def parseGnmap(inFile, autodetect): - """ - Parse a gnmap file into a dictionary. The dictionary key is the ip address or hostname. - Each key item is a list of ports and whether or not that port is https/ssl. For example: - >>> targets - {'127.0.0.1': [[443, True], [8080, False]]} - """ - hostRe=re.compile('Host:\s*[^\s]+') - servicesRe=re.compile('Ports:\s*.*') - targets = {} + hostRe = re.compile('Host:\s*[^\s]+') + servicesRe = re.compile('Ports:\s*.*') + + targets = defaultdict(list) + for hostLine in inFile: if hostLine.strip() == "": break - currentTarget = [] # Pull out the IP address (or hostnames) and HTTP service ports - + ipHostRes = hostRe.search(hostLine) if ipHostRes is None: @@ -142,12 +136,14 @@ def parseGnmap(inFile, autodetect): ipHost = ipHostRes.group() ip = ipHost.split(':')[1].strip() - services = servicesRe.search(hostLine).group().split() + try: + services = servicesRe.search(hostLine).group().split() + except: + continue for item in services: # Make sure we have an open port with an http type service on it - if (item.find("http") != -1 or autodetect) and re.findall( - "\d+/open", item): + if re.findall("\d+/open", item): port = None https = False """ @@ -163,56 +159,52 @@ def parseGnmap(inFile, autodetect): construct the URLs. """ port = item.split("/")[0] + targets[ip].append(port) - if item.find("https") != -1 or item.find("ssl") != -1: - https = True - # Add the current service item to the currentTarget list for this host - currentTarget.append([port, https]) - - if len(currentTarget) > 0: - if ip in targets: - targets[ip].extend(currentTarget) - else: - targets[ip] = currentTarget return targets -def setupBrowserProfile(headless, proxy): +def setupBrowserProfile(headless, proxy, browserType): browser = None - if proxy is not None: - service_args = [ - "--ignore-ssl-errors=true", - "--ssl-protocol=any", - "--proxy=" + proxy, - "--proxy-type=socks5", - ] + if (proxy is not None): + service_args = ['--ignore-ssl-errors=true', '--ssl-protocol=any', '--proxy=' + proxy, '--proxy-type=socks5'] else: - service_args = ["--ignore-ssl-errors=true", "--ssl-protocol=any"] + service_args = ['--ignore-ssl-errors=true', '--ssl-protocol=any'] - while browser is None: + while (browser is None): try: - capabilities = DesiredCapabilities.FIREFOX - capabilities["acceptSslCerts"] = True - fp = webdriver.FirefoxProfile() - fp.set_preference("webdriver.accept.untrusted.certs", True) - fp.set_preference("security.enable_java", False) - fp.set_preference("webdriver.load.strategy", "fast") - if proxy is not None: - proxyItems = proxy.split(":") - fp.set_preference("network.proxy.socks", proxyItems[0]) - fp.set_preference("network.proxy.socks_port", - int(proxyItems[1])) - fp.set_preference("network.proxy.type", 1) - - fireFoxOptions = webdriver.FirefoxOptions() - - if headless: - fireFoxOptions.headless = True - - browser = webdriver.Firefox(firefox_profile=fp, - capabilities=capabilities, - options=fireFoxOptions) - browser.set_window_size(1024, 768) + if (browserType == 'Chrome' or browserType == 'Chromium'): + service = Service(ChromeDriverManager(log_level=0).install()) + coptions = Options() + if headless: + coptions.add_argument("--headless") + coptions.add_argument("--no-sandbox") + coptions.add_argument("--window-size=1024x768") + coptions.add_argument("--ignore-certificate-errors") + coptions.add_argument("--ssl-version-min=tls1") + + browser = webdriver.Chrome(service=service, options=coptions) + else: + capabilities = DesiredCapabilities.FIREFOX + capabilities['acceptSslCerts'] = True + fp = webdriver.FirefoxProfile() + fp.set_preference("webdriver.accept.untrusted.certs", True) + fp.set_preference("security.enable_java", False) + fp.set_preference("webdriver.load.strategy", "fast"); + if (proxy is not None): + proxyItems = proxy.split(":") + fp.set_preference("network.proxy.socks", proxyItems[0]) + fp.set_preference("network.proxy.socks_port", int(proxyItems[1])) + fp.set_preference("network.proxy.type", 1) + + fireFoxOptions = webdriver.FirefoxOptions() + if headless: + fireFoxOptions.headless = True + + browser = webdriver.Firefox(firefox_profile=fp, + capabilities=capabilities, + options=fireFoxOptions) + browser.set_window_size(1024, 768) except Exception as e: print(e) @@ -236,17 +228,18 @@ def writeImage(text, filename, fontsize=40, width=1024, height=200): def worker( - urlQueue, - tout, - debug, - headless, - doProfile, - vhosts, - subs, - extraHosts, - tryGUIOnFail, - smartFetch, - proxy, + urlQueue, + tout, + debug, + headless, + doProfile, + vhosts, + subs, + extraHosts, + tryGUIOnFail, + smartFetch, + proxy, + browserType ): if debug: print("[*] Starting worker") @@ -258,7 +251,7 @@ def worker( display = Display(visible=0, size=(800, 600)) display.start() - browser = setupBrowserProfile(headless, proxy) + browser = setupBrowserProfile(headless, proxy, browserType) except Exception: print("[-] Oh no! Couldn't create the browser, Selenium blew up") @@ -277,9 +270,9 @@ def worker( except queue.Empty: continue print("[+] " + str(urlQueue.qsize()) + " URLs remaining") - screenshotName = quote(curUrl[0], safe="") + screenshotName = quote(curUrl, safe="") if debug: - print("[+] Got URL: " + curUrl[0]) + print("[+] Got URL: " + curUrl) print("[+] screenshotName: " + screenshotName) if os.path.exists(screenshotName + ".png"): if debug: @@ -314,7 +307,7 @@ def worker( proxy=proxy, ) if resp is not None and resp.status_code == 401: - print(curUrl[0] + " Requires HTTP Basic Auth") + print(curUrl + " Requires HTTP Basic Auth") f = open(screenshotName + ".html", "w") f.write(resp.headers.get("www-authenticate", "NONE")) f.write("