[cifs-protocol] Cannot uncompress SMB3 LZ77 payload

Stefan Metzmacher metze at samba.org
Fri Jul 5 09:44:16 UTC 2019


Am 04.07.19 um 21:52 schrieb Matthieu Suiche via cifs-protocol:
> Isn't it using the LZXPRESS algorithm instead?
> 
> On Thu, Jul 4, 2019, 8:14 PM Aurélien Aptel via cifs-protocol <
> cifs-protocol at lists.samba.org> wrote:
> 
>>
>> Hello,
>>
>> I've been able to trigger a LZ77 compressed Read response against the
>> latest
>> Windows Server 2019 but I am unable to decompress it.
>>
>> Request
>> =======
>>
>> SMB2 (Server Message Block Protocol version 2)
>>     [....]
>>     Read Request (0x08)
>>         StructureSize: 0x0031
>>             0000 0000 0011 000. = Fixed Part Length: 24
>>             .... .... .... ...1 = Dynamic Part: True
>>         Padding: 0x00
>>         Flags: 0x02, Compressed
>>             .... ...0 = Unbuffered: Client is NOT asking for UNBUFFERED
>> read
>>             .... ..1. = Compressed: Client is asking for COMPRESSED data
>>         Read Length: 131072
>>         File Offset: 0
>>         GUID handle File: a
>>             File Id: 00000012-0004-0000-0100-000004000000
>>             [Frame handle opened: 52]
>>         Min Count: 0
>>         Channel: None (0x00000000)
>>         Remaining Bytes: 0
>>         Blob Offset: 0x00000000
>>         Blob Length: 0
>>         Channel Info Blob: NO DATA
>>
>>
>> Response
>> ========
>>
>> 0000  fc 53 4d 42 00 00 02 00  02 00 00 00 50 00 00 00   .SMB.... ....P...
>> 0010  fe 53 4d 42 40 00 02 00  00 00 00 00 08 00 0a 00   .SMB at ... ........
>> 0020  01 00 00 00 00 00 00 00  07 00 00 00 00 00 00 00   ........ ........
>> 0030  ff fe 00 00 01 00 00 00  35 00 00 00 00 10 00 00   ........ 5.......
>> 0040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
>> 0050  11 00 50 00 00 00 02 00  00 00 00 00 00 00 00 00   ..P..... ........
>> 0060  ff ff ff 7f ff 07 00 0f  ff 00 00 fc ff 01 00      ........ .......
>>
>> NetBIOS Session Service
>>     Message Type: Session message (0x00)
>>     Length: 111
>> SMB2 (Server Message Block Protocol version 2)
>>     SMB2 Compression Transform Header
>>     ProtocolId: fc534d42
>>     OriginalSize: 131072
>>     CompressionAlgorithm: LZ77 (0x0002)
>>     Reserved: 0000
>>     Offset: 0x00000050
>>
>>
>> Let's look again and annotate...
>>
>>
>> 0000  fc 53 4d 42 00 00 02 00  02 00 00 00 50 00 00 00   .SMB.... ....P...
>>       ^^^^^^^^^^^                          ^^^^^^^^^^^
>>  compression transform header            compressed data offset = 0x50
>>
>>
>>    SMB2 header follows                     READ
>>       vvvvvvvvvvv                          vvvvv
>> 0010  fe 53 4d 42 40 00 02 00  00 00 00 00 08 00 0a 00   .SMB at ... ........
>> 0020  01 00 00 00 00 00 00 00  07 00 00 00 00 00 00 00   ........ ........
>> 0030  ff fe 00 00 01 00 00 00  35 00 00 00 00 10 00 00   ........ 5.......
>> 0040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ........ ........
>> 0050  11 00 50 00 00 00 02 00  00 00 00 00 00 00 00 00   ..P..... ........
>>             ^^
>>   read data offset from SMB2 header is 0x50 again
>>
>>
>> 0060  ff ff ff 7f ff 07 00 0f  ff 00 00 fc ff 01 00      ........ .......
>>       ^^
>>     compressed data starts here (0x10 + 0x50 = 0x60)
>>
>> So the LZ77 compressed data is
>>
>>     ff ff ff 7f ff 07 00 0f ff 00 00 fc ff 01 00
>>
>> I've tried to decode it using [MS-XCA] 2.4.4 "Plain LZ77 Decompression"
>> [1] which has pseudo code that is easily runnable in python. I can
>> decode the examples on that page fine:
>>
>>   >>> decode(bytes.fromhex(" ff ff ff 1f 61 62 63 17 00 0f ff 26 01"))
>>
>> bytearray(b'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc'+
>>
>> b'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc'+
>>
>> b'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc'+
>>
>> b'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc'+
>>             b'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc')
>>
>> But if I try to decode my compressed payload it is invalid:
>>
>>   >>> decode(bytes.fromhex(" ff ff ff 7f ff 07 00 0f  ff 00 00 fc ff 01
>> 00"))
>>   Traceback (most recent call last):
>>     File "<stdin>", line 1, in <module>
>>     File "lz.py", line 54, in decode
>>       raise Exception("error")
>>
>> This corresponds to this line in the pseudo-code:
>>
>>                      If MatchLength < 15 + 7
>>                         Return error.
>>
>> And it fails in the very beggining after only outputting 1 byte
>> (ff). The uncompressed payload should be all 0xFF.
>>
>> You can see and run the script online here [2].
>>
>> So, any ideas on what I'm missing? Is the LZ77 encoding used in the
>> packet different? Am I missinterpreting some fields?

It seems the compression algorithm has a bug regarding matches longer
than UINT16_MAX + 3.

In your example we an original payload of 131072 bytes with 0xff.

1. The first byte is encoded directly.

2. We find a match with offset 1 and length 131071

3. We do offset -= 1 and length -= 3 (we have offset=0, length = 131068)

4. Length is >= 7, we do length -= 7 and encode it (=> length = 131061)

5. length is >= 15, we do length -=15 and encode it (=> length = 131046)

6. length is >= 255, we do length += (15 + 7)
   (=> length = 131068 (0x1FFFC) again)
   Encoding this into just 2 bytes doesn't work.

   Ah! It seems the 0x0000 length means the length is encoded in the
   following 3 bytes! fc ff 01 is just 131068

metze

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/cifs-protocol/attachments/20190705/c0977d61/signature-0001.sig>


More information about the cifs-protocol mailing list