secdev / scapy

Scapy: the Python-based interactive packet manipulation program & library.
https://scapy.net
GNU General Public License v2.0
10.57k stars 2.01k forks source link

802.11 "FCSField -> cfe" sub-field not in correct place in the FCS field when writing a frame to a file. #3799

Open wfast01 opened 1 year ago

wfast01 commented 1 year ago

Brief description

In the Dot11 layer, the "FCSField -> cfe" (Control Frame Extension) field occupies bits B12-B15 instead of bits B8-B11 of the FCSField.

Scapy version

2.4.5

Python version

3.8.10

Operating system

Ubuntu 20.04.5 LTS

Additional environment information

No response

How to reproduce

Run the following script to produce the 802.11 frame (python3 -c "import bad_cfe; bad_cfe.bad_cfe()")

bad_cfe

Actual result

pkt.show() prints the following (indicating a cfe = 15 and an FCSField set to 1 (pw-mgt).

bad_cfe_output

However, WireShark indicates that the upper and lower 4 bits of the second byte of the frame have been swapped ("FCS-Field -> cfe" == 1 and bits B12-B15 == 15).

bad_cfe_wireshark

Expected result

Byte 2 of the 802.11 frame (Flags) should be 0x1f (PWR MGT == 1, Control Frame Extension == 15), not 0xf1.

Related resources

See paragraph 9.2.4.1.1 Figure 9-3 in the 802.11 - 2016 specification.

The issue seems to be in the SCAPY Dot11 class. Lines 695 - 698 should moved from their current location and inserted after line 710 to get the field order correct.

bad_cfe_scapy

gpotter2 commented 1 year ago

Could you please provide a raw packet or pcap? Thanks

wfast01 commented 1 year ago

I have zipped up the pcap file and attached it (the .pcap file type is not supported for uploading, but .zip is supported).
bad_cfe.zip

stryngs commented 1 year ago

One should tread carefully here. Big Endians and Little Endians are mixed within the IEEE spec for 802.11.

The current way Scapy handles FCS works. There are some quirks, but to touch the FCS for scapy will break a lot of existing code. Please go slow with any changes and test using well known injection tools.

For what its worth, you have not encapsulated your object. It is neither RadioTap() or Ether(). I could be wrong, but I do not think Wireshark handles individual layers of the "packet", rather it must see leading headers to decipher things.

In [9]: pkts[0]
Out[9]: <Dot11  subtype=Control Frame Extension type=Control proto=0 cfe=15 FCfield=pw-mgt ID=0 addr1=10:10:10:10:10:10 (RA) |>

In [10]: pkts[0].show2()
###[ 802.11 ]### 
  subtype   = Control Frame Extension
  type      = Control
  proto     = 0
  cfe       = 15
  FCfield   = pw-mgt
  ID        = 0
  addr1     = 10:10:10:10:10:10 (RA)

When I open that object in Wireshark, Wireshark is confused. I expect this. Again, I could be wrong and would like to see a 2nd pcap where Wireshark correctly parses a single layer?

wfast01 commented 1 year ago

Attached is a 2nd pcap file with Dot11() control frames (PS-Poll, RTS, CTS, ...). When Dot11() is used without another encapsulation, the link type in the pcap header is set to LINKTYPE_IEEE802_11 (value 105). WireShark seems to support this link type.

image

ctrl_frms.zip

Per the issue of the position of the Control Frame Extension (cfe) field bits in the FCS for Control Frame Extension frames, I will take a closer look at the spec and also see if I can find some real world examples to work with. The byte order of individual fields seems to be big-endian in the spec and correct in SCAPY - its the ordering of the fields in the SCAPY fields_desc list for the Control Frame Extension case (type 1 subtype 6) that doesn't seem correct. More to follow.

image

stryngs commented 1 year ago

I didn't catch what you were asking until just now. You mentioned FCS and the lack thereof with Dot11. It does exist however in Dot11FCS.

In [9]: pkts[0]
Out[9]: <Dot11  subtype=PS-Poll type=Control proto=0 FCfield= ID=0 addr1=10:10:10:10:10:10 (RA) addr2=20:20:20:20:20:20 (TA) |>

In [10]: pkts[0].show2()
###[ 802.11 ]### 
  subtype   = PS-Poll
  type      = Control
  proto     = 0
  FCfield   = 
  ID        = 0
  addr1     = 10:10:10:10:10:10 (RA)
  addr2     = 20:20:20:20:20:20 (TA)

In [11]: Dot11FCS().show()
###[ 802.11-FCS ]### 
  subtype   = Association Request
  type      = Management
  proto     = 0
  FCfield   = 
  ID        = 0
  addr1     = 00:00:00:00:00:00 (RA=DA)
  addr2     = 00:00:00:00:00:00 (TA=SA)
  addr3     = 00:00:00:00:00:00 (BSSID/STA)
  SC        = 0
  fcs       = None

It does seem I need to re-readup on how Wireshark handles things. I'd always believed until a couple days ago that Wireshark needed RadioTap or Ether, your example cleanly shows such a thing is not required.

When looking at the pcap I don't see where in Wireshark FCS is mentioned and so I would think scapy correctly chose Dot11 over a Dot11FCS object.

With reference to wifi, there are times when Big Endian and Little Endian exist together in wifi, especially with reference to crc32 outcomes and FCS.

With respect to your thoughts on code being moved, have you tried it? What were the results as far as byte ordering on scapy after say a rdpcap() as opposed to wiresharking it?