Dissecting Wireshark

I Know What You Captured Last Summer

Ben Schmidt (Presenter) // @_supernothing

Lord Commander of Security Research @NarfIndustries

Paul Makowski // @myhndl

Director of World Domination @NarfIndustries

this talk

  • background & why you should care
  • life of a packet
  • dissector overview
  • packet reconstruction example
  • some lame DoS & how to find your own
  • RCE affecting default heuristic dissector
  • mitigations & recommendations

background // why you should care

  • Wireshark is the packet analysis tool
  • used everywhere
  • breaking it lets you:
    • hide traffic
    • hinder analysis
    • hack analysts
    • hurdle airgaps
  • what alternatives are there?

background // Samurai F.U.D. Team

  • Fear, Uncertainty and Doubt
  • started at DEFCON 20
  • led by Paul Makowski & Ben Schmidt
  • task: protect legit ‘sploits & implants
  • solution: pop Wireshark & related tools

spreading F.U.D. to workstations

spreading F.U.D. to workstations

spreading F.U.D. to passive sniffers

spreading F.U.D. to passive sniffers

pop stuff on routers

pop passively observing workstations

life of a packet (1 of 2)

  • user asks [libp|WinP]cap to listen and tells it what to filter out
    • Linux: a BPF is JITted in kernelspace
    • Windows: filter installed in kernelspace (npf.sys)
  • packets pass monitor network interface
  • kernel passes packet to [libp|WinP]cap
  • [libp|WinP]cap ignores or duplicates packet to listening user process

life of a packet (2 of 2)

  • Wireshark reconstructs traffic from lowest to highest layer
    • at each layer, Wireshark determines which dissector is responsible for handling the data
    • higher-level candidates dependant on lower-level decisions
    • dissectors tell Wireshark what data they care about… and can be pretty promiscuous

dissectors

(P)IDL

ASN.1

Due to time constraints, we’ll only touch on the Python and Lua dissector types. Refer to “bonus slides” for discussion on the others.

dissectors

“Note: This is currently broken (see commits 49066, 49138).”

“...make it harder to enable Python: change it from "--with-python" to “--with-broken-python" just to prevent people from enabling unless they really mean it (are going to work on fixing it).”

When it did work, they used ctypes anyway, so there was still high likelihood of inclusion of memory corruption vulns via non-type-safe code.

There is a 3rd party project to add Python support: https://github.com/ashdnazg/pyreshark

dissectors

Although it's possible to write dissectors in Lua, Wireshark dissectors are written in C, as C is several times faster than Lua. Lua is ok for prototyping dissectors, during Reverse Engineering you can use your time for finding out how things work instead of compiling and debugging your C dissector.”

Wireshark devs discourage the use of Lua for production dissectors.

dissectors

:D

/epan/dissectors

~1250 *.c files in that folder

example traffic reconstruction

link’

network

transport

app

link

network

transport

app

physical

physical’

the raw frame

link

network

transport

app

physical

thrown out by network card

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

0060 0d 0a 44 61 74 65 3a 20 57 65 64 2c 20 30 32 20 ..Date: Wed, 02

0070 4a 75 6c 20 32 30 31 34 20 32 32 3a 33 32 3a 31 Jul 2014 22:32:1

0080 33 20 47 4d 54 0d 0a 43 6f 6e 6e 65 63 74 69 6f 3 GMT..Connectio

0090 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 50 n: keep-alive..P

00a0 72 61 67 6d 61 3a 20 6e 6f 2d 63 61 63 68 65 0d ragma: no-cache.

00b0 0a 45 78 70 69 72 65 73 3a 20 4d 6f 6e 2c 20 30 .Expires: Mon, 0

00c0 31 20 4a 61 6e 20 31 39 39 30 20 30 30 3a 30 30 1 Jan 1990 00:00

00d0 3a 30 30 20 47 4d 54 0d 0a 43 61 63 68 65 2d 43 :00 GMT..Cache-C

00e0 6f 6e 74 72 6f 6c 3a 20 70 72 69 76 61 74 65 2c ontrol: private,

00f0 20 6e 6f 2d 63 61 63 68 65 2c 20 6e 6f 2d 63 61 no-cache, no-ca

0100 63 68 65 3d 53 65 74 2d 43 6f 6f 6b 69 65 2c 20 che=Set-Cookie,

0110 6e 6f 2d 73 74 6f 72 65 2c 20 70 72 6f 78 79 2d no-store, proxy-

0120 72 65 76 61 6c 69 64 61 74 65 0d 0a 0d 0a revalidate....

We’ll walk you through the parsing of this frame.

Because it’s more interesting, we’ll take you through the parsing of this frame under the assumption that it had been transported on top of another protocol, e.g. MPLS. This path will dive deeper into the Wireshark handoff mechanisms than a simple direct packet capture and is therefore more interesting.

The dissectors mentioned here have numerous ways to diverge execution - we’ll show you the path that (we think) is actually taken, had this packet been transported on something like MPLS.

link layer

link

network

transport

app

/epan/dissectors/packet-eth.c:

void

proto_register_eth(void)

{

register_heur_dissector_list("eth", &heur_subdissector_list);

register_dissector("eth_withoutfcs", dissect_eth_withoutfcs, proto_eth);

}

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

The first highlighted line:

Creates a heuristic dissector list keyed with “eth” and points to it with heur_subdissector_list (this is important later).

The second highlighted line:

The dissector registers 3 callback functions: one for “eth_withoutfcs”, another for “eth_withfcs” and one for “eth”.

FCS in this context means Frame Check Sequence - extra error correcting code optionally added to frames.

We’ll continue via the “eth_withoutfcs” route as this is simplest and is the path taken in this example.

link layer

link

network

transport

app

/epan/dissectors/packet-eth.c:

(dissect_eth_withoutfcs() passes through to dissect_eth_common())

static proto_tree *

dissect_eth_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree,

int fcs_len)

{

if (dissector_try_heuristic(heur_subdissector_list, tvb, pinfo,

parent_tree, &hdtbl_entry, NULL))

call_dissector_with_data(ethertype_handle, tvb, pinfo, parent_tree,

&ethertype_data);

}

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

The first highlighted line:

Comment from source: “In case the packet is a non-Ethernet packet inside Ethernet framing, allow heuristic dissectors to take a first look before we assume that it's actually an Ethernet packet.”

Recall that heur_subdissector_list is registered to the “eth” list.

dissector_try_heuristic() will call all the dissectors registered to the “eth” heuristic dissector list until it finds one that thinks its better suited to parse the packet than packet-eth.c

The second highlighted line:

The dissector calls call_dissector_with_data(), which hands execution off to ethertype_handle, which is registered to the “ethertype” dissector, implemented in packet-ethertype.c (next slide)

link layer

link

network

transport

app

/epan/dissectors/packet-ethertype.c:

void

proto_register_ethertype(void)

{

new_register_dissector("ethertype", dissect_ethertype, proto_ethertype);

ethertype_dissector_table = register_dissector_table("ethertype",

"Ethertype", FT_UINT16, BASE_HEX);

}

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

The first highlighted item:

The packet-ethertype dissector registers the dissect_ethertype() function as the callback for “ethertype” packets.

The second highlighted item:

A new dissector table is created and ethertype_dissector_table is pointed to it. This is important later.

link layer

link

network

transport

app

/epan/dissectors/packet-ethertype.c:

static int

dissect_ethertype(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)

{

next_tvb = tvb_new_subset(tvb, ethertype_data->offset_after_ethertype,

captured_length, reported_length);

dissector_found = dissector_try_uint(ethertype_dissector_table,

ethertype_data->etype, next_tvb, pinfo, tree);

}

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

TVB stands for “Testy, Virtual(-ized) Buffer”. All you need to know is it’s Wireshark’s custom binary encapsulation and has a bunch of associated functions.

next_tvb points to the data immediately after the ethertype and continuing on to the end of the packet.

dissector_try_uint() is called with next_tvb (the rest of the packet).

This function takes a look at the ethertype_dissector_table and looks for dissectors who have registered an interest in ethertype_data->etype.

ethertype_data->etype points to the type field (08 00 in this case)

08 00 resolves to IP

network layer

link

network

transport

app

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

/epan/dissectors/packet-ip.c:

void

proto_register_ip(void)

{

ip_dissector_table = register_dissector_table("ip.proto", "IP protocol",

FT_UINT8, BASE_DEC);

register_dissector("ip", dissect_ip, proto_ip);

}

The first highlighted item:

Creates a dissector table keyed at “ip.proto” and points to it with ip_dissector_table. This is important later.

In the second highlighted item:

Registers the dissect_ip() function as a callback for direct dissector calls keyed to “ip”.

network layer

link

network

transport

app

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

/epan/dissectors/packet-ip.c:

void

proto_reg_handoff_ip(void)

{

ip_handle = find_dissector("ip");

dissector_add_uint("ethertype", ETHERTYPE_IP, ip_handle);

}

The first highlighted item:

Points ip_handle to the “ip” handle (itself, @ dissect_ip()).

The second highlighted item:

Associates interest in the ETHERTYPE_IP uint value for “ethertype” messages with the ip_handle object (which points to itself @ dissect_ip()).

Recall that packet-ethertype.c attempted to find a dissector interested in the 08 00 type. This is the IP type (ETHERTYPE_IP).

This is how execution is handed off from packet-ethertype.c to the dissect_ip() function in packet-ip.c

network layer

link

network

transport

app

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

/epan/dissectors/packet-ip.c:

static void

dissect_ip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)

{

dissector_try_uint_new(ip_dissector_table, nxt, next_tvb, pinfo,

parent_tree, TRUE, iph)

}

The first highlighted item:

Attempts to handoff packet to a dissector that registered interest against the ip_dissector_table (recall this points to the “ip.proto” dissector table).

This will succeed because the TCP dissector will register interest with 06.

transport layer

link

network

transport

app

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

/epan/dissectors/packet-tcp.c:

void

proto_register_tcp(void)

{

register_dissector("tcp", dissect_tcp, proto_tcp);

}

The TCP dissector registers dissect_tcp() as its callback function. The dissector is identified with the string “tcp”.

transport layer

link

network

transport

app

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

/epan/dissectors/packet-tcp.c:

void

proto_reg_handoff_tcp(void)

{

tcp_handle = find_dissector("tcp");

dissector_add_uint("ip.proto", IP_PROTO_TCP, tcp_handle);

}

The first highlighted line:

tcp_handle is pointed to the dissector that has registered the key “tcp”. This is the TCP dissector itself.

The second highlighted line:

The TCP dissector registers interest in the IP_PROTO_TCP (06) element in the “ip.proto” subdissector table and points interest at tcp_handle (which is itself, @ dissect_tcp())

This is how execution is passed from the IP dissector to the TCP dissector @ dissect_tcp().

transport layer

link

network

transport

app

0000 00 0c 29 98 30 31 00 50 56 ea 1f 28 08 00 45 00 ..).01.PV..(..E.

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

/epan/dissectors/packet-tcp.c:

callchain:

dissect_tcp() calls into:

dissect_tcp_payload() calls into:

process_tcp_payload() calls into:

decode_tcp_ports() calls into:

dissector_try_heuristic()

dissector_try_heuristic() iterates over the callbacks registered against the “tcp” heuristic dissector list.

HTTP registers one such callback...

We’ll save you some time and just give you the high level callchain through the TCP dissector.

how many of these are you legitimately

running on your network?

epan/dissectors/packet-rtps.c: heur_dissector_add("tcp", dissect_rtps_tcp, proto_rtps);

epan/dissectors/packet-ziop.c: heur_dissector_add("tcp", dissect_ziop_heur, proto_ziop);

epan/dissectors/packet-xml.c: heur_dissector_add("tcp", dissect_xml_heur, xml_ns.hf_tag);

epan/dissectors/packet-giop.c: heur_dissector_add("tcp", dissect_giop_heur, proto_giop);

epan/dissectors/packet-etch.c: heur_dissector_add("tcp", dissect_etch, proto_etch);

epan/dissectors/packet-reload-framing.c: heur_dissector_add("tcp", dissect_reload_framing_heur, proto_reload_framing);

epan/dissectors/packet-spice.c: heur_dissector_add("tcp", test_spice_protocol, proto_spice);

epan/dissectors/packet-nbd.c: heur_dissector_add("tcp", dissect_nbd_tcp_heur, proto_nbd);

epan/dissectors/packet-dcerpc.c: heur_dissector_add("tcp", dissect_dcerpc_cn_bs, proto_dcerpc);

epan/dissectors/packet-tds.c: heur_dissector_add("tcp", dissect_tds_tcp_heur, proto_tds);

epan/dissectors/packet-dplay.c: heur_dissector_add("tcp", heur_dissect_dplay, proto_dplay);

epan/dissectors/packet-dnp.c: heur_dissector_add("tcp", dissect_dnp3_tcp, proto_dnp3);

epan/dissectors/packet-icep.c: heur_dissector_add("tcp", dissect_icep_tcp, proto_icep);

epan/dissectors/packet-paltalk.c: heur_dissector_add("tcp", dissect_paltalk, proto_paltalk);

epan/dissectors/packet-starteam.c: heur_dissector_add("tcp", dissect_starteam_heur, proto_starteam);

epan/dissectors/packet-xcsl.c: heur_dissector_add("tcp", dissect_xcsl_tcp_heur, hfi_xcsl->id);

epan/dissectors/packet-cimd.c: heur_dissector_add("tcp", dissect_cimd_heur, proto_cimd);

epan/dissectors/packet-rpc.c: heur_dissector_add("tcp", dissect_rpc_tcp_heur, proto_rpc);

epan/dissectors/packet-openwire.c: heur_dissector_add("tcp", dissect_openwire_heur, proto_openwire);

epan/dissectors/packet-tali.c: heur_dissector_add("tcp", dissect_tali_heur, hfi_tali->id);

epan/dissectors/packet-reload.c: heur_dissector_add("tcp", dissect_reload_heur, proto_reload);

epan/dissectors/packet-q931.c: heur_dissector_add("tcp", dissect_q931_tpkt_heur, proto_q931);

epan/dissectors/packet-fix.c: heur_dissector_add("tcp", dissect_fix_heur, proto_fix);

epan/dissectors/packet-drda.c: heur_dissector_add("tcp", dissect_drda_heur, proto_drda);

epan/dissectors/packet-iwarp-mpa.c: heur_dissector_add("tcp", dissect_iwarp_mpa, proto_iwarp_mpa);

epan/dissectors/packet-vnc.c: heur_dissector_add("tcp", test_vnc_protocol, proto_vnc);

epan/dissectors/packet-yhoo.c: heur_dissector_add("tcp", dissect_yhoo, proto_yhoo);

epan/dissectors/packet-ndmp.c: heur_dissector_add("tcp", dissect_ndmp_heur, proto_ndmp);

epan/dissectors/packet-fcip.c: heur_dissector_add("tcp", dissect_fcip_heur, proto_fcip);

epan/dissectors/packet-spdy.c: heur_dissector_add("tcp", dissect_spdy_heur, proto_spdy);

epan/dissectors/packet-iscsi.c: heur_dissector_add("tcp", dissect_iscsi_heur, proto_iscsi);

epan/dissectors/packet-ipsec-tcp.c: heur_dissector_add("tcp", dissect_tcpencap_heur, proto_tcpencap);

epan/dissectors/packet-fmtp.c: heur_dissector_add("tcp", dissect_fmtp, proto_fmtp);

epan/dissectors/packet-lbttcp.c: heur_dissector_add("tcp", test_lbttcp_packet, proto_lbttcp);

epan/dissectors/packet-mq.c: heur_dissector_add("tcp", dissect_mq_heur_tcp, proto_mq);

epan/dissectors/packet-lanforge.c: heur_dissector_add("tcp", dissect_lanforge, proto_lanforge);

epan/dissectors/packet-bfcp.c: heur_dissector_add("tcp", dissect_bfcp_heur, proto_bfcp);

epan/dissectors/packet-jxta.c: heur_dissector_add("tcp", dissect_jxta_TCP_heur, proto_jxta);

epan/dissectors/packet-skype.c: heur_dissector_add("tcp", dissect_skype_heur, proto_skype);

epan/dissectors/packet-classicstun.c: heur_dissector_add("tcp", dissect_classicstun_heur, proto_classicstun);

epan/dissectors/packet-tuxedo.c: heur_dissector_add("tcp", dissect_tuxedo_heur, proto_tuxedo);

epan/dissectors/packet-adwin-config.c: heur_dissector_add("tcp", dissect_adwin_config_tcp, proto_adwin_config);

epan/dissectors/packet-bittorrent.c: heur_dissector_add("tcp", test_bittorrent_packet, proto_bittorrent);

epan/dissectors/packet-ctdb.c: heur_dissector_add("tcp", dissect_ctdb, proto_ctdb);

epan/dissectors/packet-xmcp.c: heur_dissector_add("tcp", dissect_xmcp_heur, proto_xmcp);

epan/dissectors/packet-ucp.c: heur_dissector_add("tcp", dissect_ucp_heur, proto_ucp);

epan/dissectors/packet-openflow.c: heur_dissector_add("tcp", dissect_openflow_heur, proto_openflow);

epan/dissectors/packet-ifcp.c: heur_dissector_add("tcp", dissect_ifcp_heur, proto_ifcp);

epan/dissectors/packet-msrp.c: heur_dissector_add("tcp", dissect_msrp_heur, proto_msrp);

epan/dissectors/packet-lbmpdmtcp.c: heur_dissector_add("tcp", test_lbmpdm_tcp_packet, lbmpdm_tcp_protocol_handle);

epan/dissectors/packet-pvfs2.c: heur_dissector_add("tcp", dissect_pvfs_heur, proto_pvfs);

epan/dissectors/packet-h1.c: heur_dissector_add("tcp", dissect_h1, proto_h1);

epan/dissectors/packet-http.c: heur_dissector_add("tcp", dissect_http_heur_tcp, proto_http);

epan/dissectors/packet-smpp.c: heur_dissector_add("tcp", dissect_smpp_heur, proto_smpp);

epan/dissectors/packet-ymsg.c: heur_dissector_add("tcp", dissect_ymsg, proto_ymsg);

epan/dissectors/packet-dcm.c: heur_dissector_add("tcp", dissect_dcm_heuristic, proto_dcm);

epan/dissectors/packet-sip.c: heur_dissector_add("tcp", dissect_sip_tcp_heur, proto_sip);

doc/README.heuristic: heur_dissector_add("tcp", dissect_PROTOABBREV_heur, proto_PROTOABBREV);

How many of these protocols are you legitimately running on your network? Doesn’t matter, Wireshark will hand the PDU to each until one says its interested. A bug in any of these will ruin your day.

There’s 58 on this slide and that’s not all of them.

Some highlights:

- RTPS: very first result - more on this dissector later :P

- DPLAY: an obsolete DirectPlay protocol originally developed by Microsoft in 1995

- DNP: “Distributed Network Protocol is a set of communications protocols used between components in process automation systems. Its main use is in utilities such as electric and water companies. Usage in other industries is not common.” - wiki

- do you run a SCADA network? (then you probably have larger problems)

- BitTorrent: not a simple protocol

- Bitcoin

...

application layer

link

network

transport

app

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

0060 0d 0a 44 61 74 65 3a 20 57 65 64 2c 20 30 32 20 ..Date: Wed, 02

0070 4a 75 6c 20 32 30 31 34 20 32 32 3a 33 32 3a 31 Jul 2014 22:32:1

0080 33 20 47 4d 54 0d 0a 43 6f 6e 6e 65 63 74 69 6f 3 GMT..Connectio

0090 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 50 n: keep-alive..P

00a0 72 61 67 6d 61 3a 20 6e 6f 2d 63 61 63 68 65 0d ragma: no-cache.

00b0 0a 45 78 70 69 72 65 73 3a 20 4d 6f 6e 2c 20 30 .Expires: Mon, 0

00c0 31 20 4a 61 6e 20 31 39 39 30 20 30 30 3a 30 30 1 Jan 1990 00:00

00d0 3a 30 30 20 47 4d 54 0d 0a 43 61 63 68 65 2d 43 :00 GMT..Cache-C

00e0 6f 6e 74 72 6f 6c 3a 20 70 72 69 76 61 74 65 2c ontrol: private,

00f0 20 6e 6f 2d 63 61 63 68 65 2c 20 6e 6f 2d 63 61 no-cache, no-ca

0100 63 68 65 3d 53 65 74 2d 43 6f 6f 6b 69 65 2c 20 che=Set-Cookie,

0110 6e 6f 2d 73 74 6f 72 65 2c 20 70 72 6f 78 79 2d no-store, proxy-

0120 72 65 76 61 6c 69 64 61 74 65 0d 0a 0d 0a revalidate....

The HTTP dissector will check for a CLRF CLRF at the end of the payload as part of its validation.

application layer

link

network

transport

app

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

/epan/dissectors/packet-http.c:

void

proto_reg_handoff_message_http(void)

{

heur_dissector_add("tcp", dissect_http_heur_tcp, proto_http);

}

The HTTP dissector registers a callback (dissect_http_heur_tcp()) in the “tcp” heuristic dissector list.

As such, dissect_http_heur_tcp() is called to determine whether to process this frame as HTTP.

application layer

link

network

transport

app

0010 01 20 ff fd 00 00 80 06 17 31 de ad be ef de ad . .......1..my..

0020 be ef 00 50 c0 1b ef 65 26 24 59 8d f2 f6 50 18 ...P...e&$Y...P.

0030 fa f0 e2 17 00 00 48 54 54 50 2f 31 2e 31 20 32 ......HTTP/1.1 2

0040 30 34 20 4e 6f 20 43 6f 6e 74 65 6e 74 0d 0a 43 04 No Content..C

0050 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 30 ontent-Length: 0

/epan/dissectors/packet-http.c:

static gboolean

dissect_http_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)

{

if((tvb_strncaseeql(tvb, linelen-8, "HTTP/1.1", 8) == 0)||(tvb_strncaseeql(tvb, 0, "HTTP/1.1", 8) == 0)){

dissect_http(tvb, pinfo, tree, data);

return TRUE;

}

}

In this example, the app layer data is HTTP because the payload begins with “HTTP/1.1”.

If it passes validation, the HTTP dissector hands off to dissect_http() to do the actual work.

lame DoS 0days #1-12

/

dozen

Note that while these bugs are 0day, they are a dime a dozen and are caused by design flaws. It wouldn’t do much good to individually report them, as we are sure the Wireshark devs are aware of them. An architecture capable of limiting memory usage and execution time is necessary to properly mitigate these issues.

lame DoS 0days #1-12

  • AKA revenge of the decompression bomb
  • grep -r “tvb_child_uncompress” .
  • at least 12 dissectors affected
    • some of these are heuristic (!)
  • limited to 65535 bytes in some cases
    • solutions:
      • attack protocol doing TCP session reassembly
      • send more packets

To be clear, you can literally find a dozen DoS flaws by grepping for “tvb_child_uncompress” and conducting some simple analysis.

Bugs in heuristic dissectors are particularly bad. Heuristic dissectors are iteratively called to determine whether an unknown PDU is something they can parse. This means that these dissectors frequently handle a wide variety of PDUs.

Because heuristic dissectors are so promiscuous with what they attempt to parse, they are easy to hit: exploit PDUs can be stacked on a variety of transport layers / protocols and can be communicated over virtually arbitrary port numbers. This flexibility is useful when avoiding firewalls.

lame DoS 0day // demo

  • packet-http.c
  • packet-gadugadu.c
  • you get the idea...

less-lame DoS 0days #13-???

  • find loops controlled by user length
    • tvb_get_ntohl/letohl is usually a good sign
  • make sure offset isn’t incremented
    • or can be controlled (offset += user_data)
  • ensure there aren’t any hidden checks
  • most Defcon 20 bugs were this type
  • slightly less lame than prior bugs

Infinite loop flaws are slightly less lame than decompression bombs because they will never complete their task.

less-lame DoS // MongoDB

  • infinite loop in packet-mongo.c, <= v1.8.1
  • found with:

grep -r -B8 -e ‘for.\?(‘ -e ‘while.\?(‘ . | less

  • packet-mongo.c registers interest in TCP port 27017
  • if you can send TCP traffic on 27017

past a listener: profit

References:

https://www.wireshark.org/security/wnpa-sec-2012-14.html

https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=7572

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-4287

less-lame DoS // MongoDB

...what if dissect_bson_document() return 0?

/epan/dissectors/packet-mongo.c:

(multiple functions)

while(offset < tvb_reported_length(tvb)) {

offset += dissect_bson_document(tvb, pinfo, offset, tree, hf_mongo_document, 1);

}

/epan/dissectors/packet-mongo.c:

static int

dissect_bson_document(tvbuff_t *tvb, packet_info *pinfo, guint offset, proto_tree *tree, int hf_mongo_doc, int nest_level)

{

document_length = tvb_get_letohl(tvb, offset);

return document_length;

}

If dissect_bson_document() returns 0 in the above snippet, then the dissector will hang, attempting to dissect the same element indefinately.

dissect_bson_document() returns document_length, a fully attacker-controlled value with insufficient validation, so the attacker can cause it to return 0.

The function tvb_get_letohl() pulls a little-endian (“le”) value out of the “tvb” (Testy Virtual(-ized) Buffer) and converts it to host-endian long (“hl”).

“long” is hardcoded to mean 32bits.

In <= v1.8.1: the only check on the validity of document_length was whether document_length == 5.

In >= v1.8.2: any document_length <= 5 or > BSON_MAX_DOC_SIZE is rejected. In addition, Wireshark bails after recursing 100 (BSON_MAX_NESTING) times.

patch diff: https://anonsvn.wireshark.org/viewvc/trunk/epan/dissectors/packet-mongo.c?r1=44288&r2=44287&pathrev=44288

bug #??? // want more?

  • https://www.wireshark.org/download/automated/captures/
    • check back early and often
    • usually at least one current
  • https://bugs.wireshark.org/bugzilla/
    • search for “crash” and/or “hang”
    • mirror all the capture files that have been uploaded
  • get more pcaps and use Wireshark’s fuzzing framework

super 1337 RCE // code

/epan/dissectors/packet-rtps.c @ rtps_util_add_typecode():

guint32 size[MAX_ARRAY_DIMENSION]; /* Max dimensions */

dim_max = NEXT_guint32(tvb, offset, little_endian);

offset += 4;

for (i = 0; i < MAX_ARRAY_DIMENSION; ++i)

size[i] = 0;

for (i = 0; i < dim_max; ++i) {

size[i] = NEXT_guint32(tvb, offset, little_endian);

offset += 4;

}

// :D :D :D :D :D :D :D

This vulnerability is platform-agnostic, affecting Windows, OS X, Linux and any other platform that can run Wireshark.

The bug was present in the RTPS dissector - a default heuristic dissector registered on top of TCP. This means that default configurations of Wireshark were vulnerable to this flaw and the flaw could be exploited over a wide variety of ports, which is potentially useful for getting around firewalls.

Due to time constraints, we will only discuss Windows exploitation; other platforms introduce additional variance in stack layout and compiler choices.

exploit mitigations // Windows

D:

DEP is on :(

exploit mitigations // Windows

:D

but no ASLR on WinPcap modules :) :)

exploit mitigations // Windows

/GS enabled :(

...but variable reordering wasn’t triggered :) :) :)

super 1337 RCE // exploitation

Overwrite dim_size and i with the right values...

:D

super 1337 RCE // wat

super 1337 RCE // exploitation

  • let’s recap: we’ve got
    • ASLR defeat
    • EIP & ESP
    • what else could we possibly need?
  • attack different versions
    • vuln goes back to v1.03(!), released in Sept 2008
  • stack space...later versions aren’t nice.
  • solution: we have to go deeper

super 1337 RCE // exploitation

/epan/dissectors/packet-rtps.c @ rtps_util_add_typecode():

case RTI_CDR_TK_ARRAY: {

/* Recursive decode seq typecode */

/*offset += */rtps_util_add_typecode(tree, tvb, offset,

little_endian, indent_level, is_pointer,

bitfield, is_key, offset_begin, name, -1, size,

ndds_40_hack);

}

we can recurse before we crash!

super 1337 RCE // exploitation

  • benefits of recursing are many
    • fix up stack frames
    • perform differently with different stack layouts
    • gain more stack space to work with
    • support multiple versions in single packet
  • downside: annoying to write and test
  • let’s start with frame differences...

super 1337 RCE // exploitation

  • v1.10 changed stack layout from v1.8
  • i (and other vars) at different offsets
  • RA & SP at different offsets
    • v1.8 has slightly more stack space
  • strategy:
    • do v1.10 overflow first, then recurse
    • do v1.8 overflow second, and return
    • if no code exec yet, we’ll return again

super 1337 RCE // exploitation

  • works good in theory
  • tvb pointer stored after stored RA
    • greatly reduced stack space :(
  • recurse three times just for v1.10
    • once to copy shellcode to stack
    • once to set up an add ESP,8 pivot
    • once to copy ROP chain above pivot
  • chaining together with v1.8 is hard.

super 1337 RCE // exploitation

Copy in shellcode

Skip frame by long copy

Set up stack pivot

Skip frame by long copy

Copy ROP

Copy in shellcode

Encounters i=0 and returns

Set up stack pivot

N/A

Copy ROP and return

Shared Preamble

First vulnerable frame - desync occurs

v1.10.1

v1.8.9

The packet contains the same preamble necessary to reach the vulnerable code on both versions; however, once reaching the first call to the vulnerable function, they are desynchronized by overwriting i with different values depending on the version. This allows us to skip non-relevant frames per version, leading us to a single packet that can exploit multiple versions of Wireshark.

super 1337 RCE // demo

  • v1.8
  • v1.10

mitigations

  • for users
  • for devs
    • seccomp() / seccomp_bpf()
    • actual privilege separation (separate processes)
    • better killing of long-running dissectors
    • more hardened memory-management functions
    • more official/automated code review
    • don’t handle jumping to 0x41414141?

While Wireshark is in no position to judge whether EIP pointing to an illegitimate location is a malicious or accidental thing, attempting to handle all (or nearly all) faults is a dangerous proposition. Wireshark devs were presented with the choice to either handle the malicious and accidental crashes or handle neither. They probably opted for the former so that peoples’ capturing doesn’t stop; however, because they’re attempting to handle (mostly) arbitrary faults they introduce the possibility for a.) errors in their error handlers (see: Linux) and b.) malicious attackers taking advantage of this rather unique property. The real solution is what was eluded to in a previous bullet: actual privilege separation. Something that must stay up (e.g. the capturing process) should probably catch these and handle them (or at least provide the user with the option). The processes with greater potential to be buggy (e.g. a dissection process) should also be allowed to die when such a thing happens. Dissection processes dying shouldn’t matter in a true-privilege-separation case, e.g. a renderer process crashing in Chrome.

“Privilege separation was implemented in Wireshark 1.0. Only the 'dumpcap' utility needs to be run as root.”

While this is a step in the right direction, this is not the privilege separation we were talking about.

thanks

  • Samurai
  • Wireshark devs
  • Secuinside organizers
  • thesaurus.com

questions

Secuinside 2014 - Preso - Google Slides