Force scapy to re-dissect the layer after changes

I am working with a fork of scapy (Python package management tool) called scapy-com . It implements 802.15.4 and Zigbee parsing / manipulation among other protocols.

The network layer security header contains the quirk of the Zigbee proxy. The security level (which determines the encryption and length of the message integrity code) is initially set correctly, but then set to 0 (no encryption) before sending it. From the specification:

The security level subfield of the security control field must be overwritten with the 3-bit null string '000'

The specification can be found here . The relevant section is "4.3.1.1 Outbound Frame Security Handling".

This means that packet capture indicates that no encryption or message integrity code is being used. The security level must be passed out of range.

scapy-com doesn't handle it. It naively analyzes the security level and sets the MIC length to 0. The code that does this:

def util_mic_len(pkt):
    ''' Calculate the length of the attribute value field '''
    # NWK security level 0 seems to implicitly be same as 5
    if ( pkt.nwk_seclevel == 0 ): # no encryption, no mic
        return 0
    elif ( pkt.nwk_seclevel == 1 ): # MIC-32
        return 4
    elif ( pkt.nwk_seclevel == 2 ): # MIC-64
        return 8
    elif ( pkt.nwk_seclevel == 3 ): # MIC-128
        return 16
    elif ( pkt.nwk_seclevel == 4 ): # ENC
        return 0
    elif ( pkt.nwk_seclevel == 5 ): # ENC-MIC-32
        return 4
    elif ( pkt.nwk_seclevel == 6 ): # ENC-MIC-64
        return 8
    elif ( pkt.nwk_seclevel == 7 ): # ENC-MIC-128
        return 16
    else:
        return 0

      

A project using scapy-com tries to deal with this by setting the security level to 5:

#TODO: Investigate and issue a different fix:
# https://code.google.com/p/killerbee/issues/detail?id=30
# This function destroys the packet, therefore work on a copy - @cutaway
pkt = pkt.copy()   #this is hack to fix the below line
pkt.nwk_seclevel=5 #the issue appears to be when this is set

mic = pkt.mic

      

However, this does not work - the message integrity code is already set. I worked around this by simply changing the util_mic_len function to set the microphone length correctly.

The question is, how should the Zigbee parser be changed to change the nwk_sec level after the initial opening in order to change the microphone length?

I see two solutions:

  • Change the scapy-com code so that changing nwk_seclevel will automatically change the microphone length.
  • Re-parse packages from outside scapy-com as they change.

The problem with 1 is that I have no idea how to do this.

The problem with 2 is that I have an idea, but I cannot get it to work - I cannot work out how to invoke analysis on a package after it has been downloaded. The pkt.dissect (pkt) call seems to be unworkable and looks strange.

What's the best or recommended solution here?

+3


source to share


2 answers


Correct scapy solution. scapy-com is pretty old. The Zigbee specific code at scapy-com is 1244 lines of code which are pretty much enums and lists of fields. So porting it to scapy-python3 shouldn't be too hard. If you can help porting to scapy-python3 http://github.com/phaethon/scapy I can help fix the problem.



+2


source


The project you are linking to is KillerBee and I had this exact decryption issue. I just "fixed" the code:



from struct import pack
f = pkt.getlayer(ZigbeeSecurityHeader).fields

pkt.nwk_seclevel = 5
nwk_mic = pkt.mic
nwk_encrypted = f['data'][:-6]

ext_source = f['ext_source']

nwk_sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]
nwk_nonce = struct.pack('Q',ext_source) + struct.pack('I',f['fc']) + nwk_sec_ctrl_byte

nwk_crop_size = 4 + 2 + len(pkt.getlayer(ZigbeeSecurityHeader).fields['data'])  # The length of the encrypted data, mic and FCS

# the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later
zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
zigbeeData = zigbeeData[:-nwk_crop_size]

(nwk_payload, nwk_micCheck) = zigbee_crypt.decrypt_ccm(nkey, nwk_nonce, nwk_mic, nwk_encrypted, zigbeeData)

      

+1


source







All Articles