Wireshark-bugs: [Wireshark-bugs] [Bug 5722] New: Crash in 6LoWPAN on 32-bit systems

Date: Wed, 23 Feb 2011 13:53:09 -0800 (PST)
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5722

           Summary: Crash in 6LoWPAN on 32-bit systems
           Product: Wireshark
           Version: 1.4.3
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: Critical
          Priority: Low
         Component: Wireshark
        AssignedTo: wireshark-bugs@xxxxxxxxxxxxx
        ReportedBy: gerald@xxxxxxxxxxxxx


Created an attachment (id=5973)
 --> (https://bugs.wireshark.org/bugzilla/attachment.cgi?id=5973)
Capture file demonstrating the crash.

Build Information:
Paste the COMPLETE build information from "Help->About Wireshark", "wireshark
-v", or "tshark -v".
--
Paul Makowski discovered that we could crash on 32 bit systems while reading a
malformed 6LoWPAN packet. Here's his writeup:

__TIMELINE__
Feb 4th, 2011:    Report sent to security@xxxxxxxxxxxxx at request of employer.
Feb 7th, 2011:    Gerald Combs responded, issued correction regarding assertion
failures only causing warnings by default.
Feb 11th, 2011:    Reviewed the problem; second revision of this report sent to
security@xxxxxxxxxxxxx.

__TITLE__
packet-6lowpan.c contains an off-by-one error which may be used to cause a
denial of service (stop packet capture or dissection of .pcap).

__BUG TRACKING__
CVE: N/A (0day)

__REPORTED BY__
Paul Makowski (my.hndl@xxxxxxxxx), working for SEI/CERT

__AFFECTED VERSIONS__
Vulnerable:        Wireshark 1.4.x branch (stable), tested against SVN 35916
Not Vulnerable:    Wireshark 1.5.x branch (dev), tested against SVN 35917
Untested:        Wireshark < 1.4

__ATTACKER CAPABILITIES__
An attacker may cause packet-6lowpan.c to request an ep_alloc()'ed buffer that
is 1 byte too small.  When a write is later performed on this buffer, the
attacker has limited control over the byte written beyond the end of the
buffer.

Within Wireshark trunk-1.4 SVN 35916, the extra byte clobbers the first byte in
the ep_ buffer's canary value, causing a call to g_error() when the buffer is
later freed and leading to a denial of service condition.
Within Wireshark trunk(1.5) SVN 35917, an exception is generated for the
condition and handled appropriately.  packet-6lowpan.c still faults, but
doesn't cause a crash.

__SUMMARY__
dissect_6lowpan_iphc() in /epan/dissectors/packet-6lowpan.c trusts user
supplied data when incrementing 'offset'.  It is possible for the user to
increment 'offset' to a value greater than tvb->length and/or
tvb->reported_length, forcing the dissector to attempt dissection out of
bounds.  If 'offset' is greater than tvb->length or tvb->reported_length, then
tvb_length_remaining() or tvb_reported_length_remaining() will return -1
respectively.  If tvb_length_remaining() returns -1, then a buffer is allocated
1 byte too short, leading to a partial overwrite of the heap canary.

__PROPOSED FIX__
The bug may be triggered in multiple ways due to the fact that packet-6lowpan.c
trusts user input when making decisions regarding how far to increment the
'offset' value.  dissect_6lowpan_iphc() handles a number of different ways to
decompress items and increment 'offset' based on user input.  Therefore, it may
make the most sense to check the value of 'offset' against tvb->length and/or
tvb->reported_length after each increment whose magnitude is derived from user
input.  If 'offset' is greater than either of these values, the packet should
be considered an invalid 6LoWPAN packet and reported as such, rather than
causing a crash.

__DETAILS__
------------------------------------
Wireshark 1.5 (dev) - not vulnerable
------------------------------------
// from /epan/dissectors/packet-6lowpan.c:
tvb_memcpy(tvb, &ipv6.ip6_dst.bytes[sizeof(ipv6.ip6_dst) - length], offset,
length);
    // from /epan/tvbuff.c:
    check_offset_length(tvb->length, tvb->reported_length, offset, (gint)
length, &abs_offset, &abs_length);
        int exception = 0;

        if (!check_offset_length_no_exception(tvb_length_val,
tvb_reported_length_val, offset, length_val, offset_ptr, length_ptr,
&exception)) {
            DISSECTOR_ASSERT(exception > 0);
            THROW(exception);
        }

check_offset_length_no_exception() calls compute_offset_length(), which
computes:
end_offset = *offset_ptr + *length_ptr; // end_offset holds 17, which is
greater than tvb->length and tvb->reported_length
...
*exception = ReportedBoundsError; // an exception is registered

An exception is THROWn, which is then caught by dissect_6lowpan_iphc(), causing
the packet to be flagged as invalid and exiting dissection before the 1 byte
out-of-bounds write.

-----------------------------------
Wireshark 1.4 (stable) - vulnerable
-----------------------------------
Ref: /epan/dissectors/packet-6lowpan.c,
http://wiki.wireshark.org/Development/Canary

Wireshark 1.4 lacks this additional exception checking, so dissection procedes.

tvb_length_remaining() is used to calculate the required size of an
ep_alloc()'ed buffer:
nhdr_list = (struct lowpan_nhdr *)ep_alloc(sizeof(struct lowpan_nhdr) +
tvb_length_remaining(tvb, offset));

If 'offset' > tvb->length, tvb_length_remaining() returns -1 which is added to
sizeof(struct lowpan_nhdr), causing an allocation 1 byte too small for
nhdr_list.

Several lines later, the return value of tvb_reported_length_remaining() is
written to the last field (a 32bit value) in the nhdr_list struct:
nhdr_list->reported = tvb_reported_length_remaining(tvb, offset);

nhdr_list is a lowpan_nhdr:
struct lowpan_nhdr *nhdr_list;

lowpan_nhdr is defined as such:
/* Structure used to store decompressed header chains until reassembly. */
struct lowpan_nhdr {
    /* List Linking */
    struct lowpan_nhdr  *next;
    /* Next Header */
    guint8              proto;
    guint               length;
    guint               reported;
};

Since nhdr_list is 1 byte too short, this last byte (which will be 0xff unless
the attacker can desynchonize tvb->length and tvb->reported_length) will be
written over the first byte of the ep_ buffer's heap canary.  

Before nhdr_list->reported = tvb_reported_length_remaining(tvb, offset);
0x03421163  00 ff ff ff ff 02 00 00  .ÿÿÿÿ... <-- 4B of 0xff written previously
via nhdr_list->length = tvb_length_remaining(tvb, offset);
0x0342116B  7a d6 f2 0a 0f e3 93 44  zÖò..ã“D <-- canary

After nhdr_list->reported = tvb_reported_length_remaining(tvb, offset);
0x03421163  00 ff ff ff ff ff ff ff  .ÿÿÿÿÿÿÿ <-- 4 additional bytes of 0xff
written, the last clobbering the canary
0x0342116B  ff d6 f2 0a 0f e3 93 44  ÿÖò..ã“D <-- canary, 1st byte clobbered

When Wireshark attempts to free this buffer (it will after processing the
packet), the canary is checked against mem_canary (the correct value).  Since
'canary' is corrupted, Wireshark will call g_error() and abort.

-- 
Configure bugmail: https://bugs.wireshark.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.