Bug 11799 - Wireshark use-after-free in print_hex_data_buffer / print_packet
Summary: Wireshark use-after-free in print_hex_data_buffer / print_packet
Status: RESOLVED FIXED
Alias: None
Product: Wireshark
Classification: Unclassified
Component: Dissection engine (libwireshark) (show other bugs)
Version: Git
Hardware: All All
: Low Major (vote)
Target Milestone: ---
Assignee: Bugzilla Administrator
URL:
Depends on:
Blocks:
 
Reported: 2015-11-27 18:18 UTC by Mateusz Jurczyk
Modified: 2016-04-26 18:31 UTC (History)
2 users (show)

See Also:


Attachments
Reproducers. (1.05 KB, application/zip)
2015-11-27 18:18 UTC, Mateusz Jurczyk
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mateusz Jurczyk 2015-11-27 18:18:24 UTC
Created attachment 14054 [details]
Reproducers.

Build Information:
Wireshark git master.
--
The following crash due to a use-after-free condition can be observed in an ASAN build of Wireshark (current git master), by feeding a malformed file to tshark ("$ ./tshark -nVxr /path/to/file"):

Attached are three files which trigger the crash.

--- cut ---
==14146==ERROR: AddressSanitizer: heap-use-after-free on address 0x6070000003a0 at pc 0x000000b2c8eb bp 0x7ffdfc45fa70 sp 0x7ffdfc45fa68
READ of size 1 at 0x6070000003a0 thread T0
    #0 0xb2c8ea in print_hex_data_buffer wireshark/epan/print.c:987:13
    #1 0xb2bf43 in print_hex_data wireshark/epan/print.c:904:14
    #2 0x5422e2 in print_packet wireshark/tshark.c:4155:10
    #3 0x53cb2e in process_packet wireshark/tshark.c:3742:7
    #4 0x535d90 in load_cap_file wireshark/tshark.c:3484:11
    #5 0x52c1df in main wireshark/tshark.c:2197:13

0x6070000003a0 is located 0 bytes inside of 65-byte region [0x6070000003a0,0x6070000003e1)
freed by thread T0 here:
    #0 0x4d6ce0 in free llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:30
    #1 0xc1fd8e in real_free wireshark/epan/tvbuff_real.c:47:3
    #2 0xc2229c in tvb_free_internal wireshark/epan/tvbuff.c:110:3
    #3 0xc22049 in tvb_free_chain wireshark/epan/tvbuff.c:135:3
    #4 0xc21ed1 in tvb_free wireshark/epan/tvbuff.c:125:2
    #5 0xbc972e in free_all_fragments wireshark/epan/reassemble.c:351:4
    #6 0xbd40e5 in fragment_add_seq_common wireshark/epan/reassemble.c:1919:5
    #7 0xbd4895 in fragment_add_seq_check_work wireshark/epan/reassemble.c:2006:12
    #8 0xbd43a7 in fragment_add_seq_check wireshark/epan/reassemble.c:2050:9
    #9 0x2fb8256 in dissect_mux27010 wireshark/epan/dissectors/packet-mux27010.c:949:28
    #10 0xaf3794 in call_dissector_through_handle wireshark/epan/packet.c:616:8
    #11 0xae5692 in call_dissector_work wireshark/epan/packet.c:691:9
    #12 0xae4e1d in dissector_try_uint_new wireshark/epan/packet.c:1148:9
    #13 0x25dca12 in dissect_frame wireshark/epan/dissectors/packet-frame.c:500:11
    #14 0xaf3794 in call_dissector_through_handle wireshark/epan/packet.c:616:8
    #15 0xae5692 in call_dissector_work wireshark/epan/packet.c:691:9
    #16 0xaefb1b in call_dissector_only wireshark/epan/packet.c:2662:8
    #17 0xae09f3 in call_dissector_with_data wireshark/epan/packet.c:2675:8
    #18 0xadffde in dissect_record wireshark/epan/packet.c:501:3
    #19 0xab6d0d in epan_dissect_run_with_taps wireshark/epan/epan.c:373:2
    #20 0x53c91b in process_packet wireshark/tshark.c:3728:5
    #21 0x535d90 in load_cap_file wireshark/tshark.c:3484:11
    #22 0x52c1df in main wireshark/tshark.c:2197:13

previously allocated by thread T0 here:
    #0 0x4d6ff8 in __interceptor_malloc llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:40
    #1 0x7ff6062f0610 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4e610)
    #2 0xbe1202 in fragment_add_seq_work wireshark/epan/reassemble.c:1793:2
    #3 0xbd4181 in fragment_add_seq_common wireshark/epan/reassemble.c:1925:6
    #4 0xbd4895 in fragment_add_seq_check_work wireshark/epan/reassemble.c:2006:12
    #5 0xbd43a7 in fragment_add_seq_check wireshark/epan/reassemble.c:2050:9
    #6 0x2fb8256 in dissect_mux27010 wireshark/epan/dissectors/packet-mux27010.c:949:28
    #7 0xaf3794 in call_dissector_through_handle wireshark/epan/packet.c:616:8
    #8 0xae5692 in call_dissector_work wireshark/epan/packet.c:691:9
    #9 0xae4e1d in dissector_try_uint_new wireshark/epan/packet.c:1148:9
    #10 0x25dca12 in dissect_frame wireshark/epan/dissectors/packet-frame.c:500:11
    #11 0xaf3794 in call_dissector_through_handle wireshark/epan/packet.c:616:8
    #12 0xae5692 in call_dissector_work wireshark/epan/packet.c:691:9
    #13 0xaefb1b in call_dissector_only wireshark/epan/packet.c:2662:8
    #14 0xae09f3 in call_dissector_with_data wireshark/epan/packet.c:2675:8
    #15 0xadffde in dissect_record wireshark/epan/packet.c:501:3
    #16 0xab6d0d in epan_dissect_run_with_taps wireshark/epan/epan.c:373:2
    #17 0x53c91b in process_packet wireshark/tshark.c:3728:5
    #18 0x535d90 in load_cap_file wireshark/tshark.c:3484:11
    #19 0x52c1df in main wireshark/tshark.c:2197:13

SUMMARY: AddressSanitizer: heap-use-after-free wireshark/epan/print.c:987:13 in print_hex_data_buffer
Shadow bytes around the buggy address:
  0x0c0e7fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8040: fa fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd
  0x0c0e7fff8050: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c0e7fff8060: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fd
=>0x0c0e7fff8070: fa fa fa fa[fd]fd fd fd fd fd fd fd fd fa fa fa
  0x0c0e7fff8080: fa fa fd fd fd fd fd fd fd fd fd fa fa fa fa fa
  0x0c0e7fff8090: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa fd fd
  0x0c0e7fff80a0: fd fd fd fd fd fd fd fa fa fa fa fa fd fd fd fd
  0x0c0e7fff80b0: fd fd fd fd fd fa fa fa fa fa 00 00 00 00 00 00
  0x0c0e7fff80c0: 00 00 06 fa fa fa fa fa 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==14146==ABORTING
--- cut ---

This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.
Comment 1 Martin Kaiser 2016-01-03 19:18:45 UTC
I tried to reproduce this:


martin@reykholt:~/src/wireshark.git$ ./tools/valgrind-wireshark.sh -T ~/Downloads/asan_heap-uaf_b2c1db_1289_1d9906979621bf069e580bed996d9ff5.pcap 

the tvb contains 324 captured bytes

** (process:3237): WARNING **: seq 0, frag num 3584, offs beg 256, len 129, more 0                                                     
                        
fd_head is created, data for this fragment is unavailable, no more fragments                                                           
-> ignore this fragment and remove all other fragments of this frame from the fragment table (there are none)                          
                        
reassembly returns NULL                                                                                                                
        
* (process:3237): WARNING **: seq 0, frag num 0, offs beg 256, len 131, more 220                                                       
        
fd_head is created, data for this fragment is unavailable, fragment number 0, there are more fragments                                 
-> treat as unfragmented, don't add the fragment to the fragment table

reassembly returns NULL                                                                                                                

* (process:3237): WARNING **: seq 0, frag num 0, offs beg 256, len 65, more 0                                                          

fd_head already exists, we have the fragment's data in the tvb, no more fragments                                                      
-> do reassembly with the previous fragment, create a new data source of 65 bytes                                                      

* (process:3237): WARNING **: seq 0, frag num 0, offs beg 256, len 256, more 158                                                       

fd_head already exists, data for this fragment is unavailable, fragment number 0, there are more fragments                             
-> treat as unfragmented, don't add the fragment to the fragment table                                                                 
                
reassembly returns NULL 
                
* (process:3237): WARNING **: seq 0, frag num 256, offs beg 256, len 129, more 0                                                       
                
fd_head already exists, data for this fragment is unavailable, fragment number != 0, no more fragments                                 
-> ignore this fragment and remove all other fragments of this frame from the fragment table                                           
                
this removes the tvb for the data source of 65 bytes that we just created                                                              
                        
                
we have two data sources        
   entire tvb == 324 bytes
   reassembled frame == 65 bytes
      -> its tvb was destroyed, the out-of-bounds access happens when its hex dump is displayed                                        

                                
I'm not sure how to fix this properly. The questionable code is in fragment_add_seq_common() where it says                             
                        
XXX I've copied this over from the old separate                                                                                        
fragment_add_seq_check_work, but I'm not convinced it's doing the                                                                      
right thing -- rav                                                                                                                     
                                                                                                                                       
For frag_number 0, no data present, we create an fd_head and don't add it to                                                           
the fragment table. That's why we do reassembly with a single fragment of 65                                                           
bytes.          

For frag_number != 0, !more_frags, we free all fragments of that frame even if                                                         
we've already done reassembly and created a new data source for them...
Comment 2 Peter Wu 2016-02-21 15:34:11 UTC
An overview of the fragments from that capture (X denotes the same fragment start):

#  number  len  more   fd_head(returned)
1  3584    129  false  NULL
2     0    131  true   X
3     0     65  false  X
4     0    256  true   X
5   256    129  false  NULL

Comments for the fragments (based on the fragment_add_seq documentation in epan/reassemble.h):

Fragment #1
- "first fragment seen for this datagram" -> create new fd_head
- not "the only fragment in the datagram" -> skip special case
- not "packet completes assembly" -> return NULL
- NOTE: Martin suggests that this is removes from the fragment table. The doc comment does not suggest that. It would make sense to do this though as no reassembly is ever possible and the fragments are presumably not needed?

Fragment #2
- "first fragment seen for this datagram" -> create new fd_head
- "packet completes assembly" -> return fd_head
- NOTE: should it really return fd_head since more_frags is set?

Fragment #3:
- This overwrites a previous fragment. Shouldn't this be disallowed?

Fragment #4:
- Like #3, this overwrites a previous fragment. Shouldn't this be disallowed?

Fragment #5:
 - Since fragment #4 is (should be) dropped, shouldn't this fail as well (incomplete reassembly, like fragment #1)?
Comment 3 Mateusz Jurczyk 2016-02-22 13:49:55 UTC
FYI: Since a fix for the vulnerability hasn't been submitted yet, the Google Project Zero 90-day disclosure deadline expires in three days (on 2016-02-25). 

The corresponding bug at https://code.google.com/p/google-security-research/issues/detail?id=651 in our tracker will be opened then, unless a patch is scheduled for release on a specific day within 14 days following the deadline (see http://googleprojectzero.blogspot.com/2015/02/feedback-and-data-driven-updates-to.html for more details).
Comment 4 Peter Wu 2016-02-23 00:32:10 UTC
In my previous analysis I missed that the tvb is not large enough. There is only 324 bytes in the tvb and with an offset of 256, there is only 68 bytes that can be added to the fragment. Anything larger than that triggers the special behavior (all but fragment 3).

Fragments 1 and 5 have a non-zero fragment block index and are the last fragments in their list (more_frags is FALSE) so the special behavior kicks in:
 - The fragments list identified by (pinfo, id, data) is removed from the fragments table.
 - All remaining fragments are removed (but as the fragment_table hashtable has a value_destroy function that also calls free_all_fragments, I guess that this is a no-op?)

Fragments 2, 3 and 4 are added to the same fragments list as they have the same fragment id (0). When fragment 5 clears the fragments list (as described above), it also frees all tvbs that were returned before for fragments 2, 3 and 4. This matches Martin's explanation.

(In reply to Martin Kaiser from comment #1)
[..]
> I'm not sure how to fix this properly. The questionable code is in
> fragment_add_seq_common() where it says                             
>
>   XXX I've copied this over from the old separate
>   fragment_add_seq_check_work, but I'm not convinced it's doing the
>   right thing -- rav

I guess that rejecting fragments with insufficient data in the tvb is the best way to fix this. If the dissector tries to add a fragment where the tvb does not contain frag_data_len data, the reassembly would normally fail.

This special-coded case can be avoided if dissectors just behave and pass no more than tvb_captured_length(tvb). Returning the full fragment if the first fragment number is zero is quite an arbitrary check...
Comment 5 Peter Wu 2016-02-23 01:40:14 UTC
Possible patch at https://code.wireshark.org/review/14082. I also considered rejecting all fragments with short tvbs, but that can be too restrictive when a protocol exists which can send overlapping fragments (as a form of redundancy). Does such protocols actually exists...? If not, then an alternative (more risky) patch is at https://code.wireshark.org/review/14083
Comment 6 Mateusz Jurczyk 2016-04-13 17:08:13 UTC
Friendly ping.
Comment 7 Peter Wu 2016-04-14 23:31:19 UTC
Fixed on master since c5b2c1e8f40cee913bd70fcc00284483b3c92fcd. master-2.0 is pending. I could not trigger a crash on 1.12 so I'll leave that branch alone.

This is the message I get for 1.12:

tshark: The file "asan_heap-uaf_b2c1db_2079_deefe798881104a81328b47698ab1d94.pcap" appears to be damaged or corrupt.
(pcap: File claims packet was 4203872580 bytes on the wire)
Comment 8 Gerrit Code Review 2016-04-14 23:31:41 UTC
Change 14923 had a related patch set uploaded by Peter Wu:
reassemble: remove special treatment for truncated data

https://code.wireshark.org/review/14923
Comment 9 Gerrit Code Review 2016-04-14 23:31:56 UTC
Change 14923 merged by Peter Wu:
reassemble: remove special treatment for truncated data

https://code.wireshark.org/review/14923
Comment 10 Peter Wu 2016-04-14 23:33:10 UTC
Thanks for the report with reproducers!

(pong)