Wireshark use-after-free in print_hex_data_buffer / print_packet
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.
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...
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)?
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).
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...
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
Friendly ping.
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)
Change 14923 had a related patch set uploaded by Peter Wu: reassemble: remove special treatment for truncated data https://code.wireshark.org/review/14923
Change 14923 merged by Peter Wu: reassemble: remove special treatment for truncated data https://code.wireshark.org/review/14923
Thanks for the report with reproducers! (pong)