Wireshark Custom Development for Fun and Profit
A week or so ago I found I had a need to extract the SSL Certificates used to secure HTTP connections over the wire. I started down the path of SharpPcap & PacketDotNet (C# wrappers around WinPcap) and quickly realized that I'd have to do a lot of mucking around to get to the point where I could reliably and consistently extract SSL Certificates from the packet data I was receiving.
At this point I thought to myself: "What about WireShark? Isn't it open source?".
Special Note: Today Marks the 2 year anniversary of this site!
References
- WinPcap [winpcap.org]
- SharpPcap/PcapDotNet [sourceforge.net]
- Wireshark Developer Page [wireshark.org]
- Wireshark Developer Guide [wireshark.org]
- Wireshark Developer README [anonsvn.wireshark.org]
- Wireshark page on SSL [wiki.wireshark.org]
- Wireshark SSL Filter Reference [wireshark.org]
- Conversation on Wireshark Development Mailing List [wireshark.org]
- Lua Scripting Slideshow/PDF from SharkFest [sharkest.wireshark.org]
- isomalloc.c(734) : error C2065: 'O_WRONLY' : undeclared identifier [social.msdn.microsoft.com]
- How to convert an integer to a string in GLib? [stackoverflow.com]
- Modified packet-ssl.c (from later on in the article) [7thzero.com]
The Basics
My goal was to somehow automate Wireshark into dumping all the SSL Certificates it finds into a directory that I can use later. Before I could do this I had to learn a few things about how Wireshark worked. Wireshark is a big project so I focused on the few things that seemed most relevant to me:
- Get a Windows development environment setup
- Dissectors
- Taps / Listeners
Setup a Wireshark Developer Environment
I was surprised at how easy it was to get Wireshark up and building on my Windows 8 laptop. The Developers Guide covers everything you need to know in a step by step fashion.
The only problem I had was that I didn't install Python beforehand like the guide says to. I already had cygwin, Visual Studio 2010 and SVN / GIT installed. Because I didn't have Python installed I had some trouble getting the packet-ncp2222.c to appear (apparently it uses Python in the build process).
Look at Section 2.2 (Page 10) of the Developers Guide for the whole break-down.
Dissectors & Taps
After getting the Wireshark source code building on my box it was time to figure out how wireshark is structured. I knew from Section 6.1 (Page 55) of the Developers Guide that Dissectors were a key part of Wireshark. A Dissector is used to break-down a packet and extract information specific to a protocol. Out of the box I think there are over 1300 dissectors included with Wireshark.
Fortunately for me I found a robust and very functional SSL dissector included with Wireshark out of the box. I was hoping to interact with the dissector to extract Certificate information. That is where the concept of a Tap or Listener coms into play. A dissector can be coded to include a 'hook' that lets you extract information from it.
Pages 60 and 73 of the Wireshark Developers Guide cover how to create a basic dissector and Tap/Listener.
While focusing on Lua, this Sharkfest slideshow/PDF could be helpful in understanding Dissectors & Taps
Path 1: Lua scripting
When I heard that Wireshark was scriptable via Lua I was pretty excited. Picking up a scripting language should be simpler than rooting through the Wireshark C source code, right? Unfortunately for me I found the Lua support in Wireshark to be a little lacking. It might be a PEBCAK, but I just couldn't find enough documentation to get the Lua binding to successfully interact with the SSL Dissector and extract the certificate bytes.
One nice thing about working in Lua as compared to C is that there is a Lua execution console built right into the shipping builds of Wireshark (Found under Tools -> Lua -> Evaluate) that can be used to prototype Listeners or Dissectors without having to recompile the project.
I was able to get a simple Listener running that could tell me when an SSL Handshake occured. I'd like to share that source here for anyone that it might benefit:
-- This opens up a handy window in Wireshark so I can see -- the results of my Lua script window2 = TextWindow.new("SSL Window"); -- If I want to extract information I have to create a 'field' -- apparently "ssl.handshake.certificate" is a Label type -- and doesn't actually contain any cert data ssl_cert_Info = Field.new("ssl.handshake.certificate"); -- This is a significant line of code. It activates a -- 'listener' that fires the 'tap.packet' method when -- it detects what you tell it to listen for. Here I'm -- trying to listen for ssl handshake certificates tap = Listener.new (nil, "ssl.handshake.certificate"); -- I have to create this method or nothing would happen. -- It's in here that I learn information about the tapped -- packet and use it for something useful (or not in my case) function tap.packet (pinfo, buffer, userdata) -- Never figured out how to use the 'field' to -- extract any cert bytes. local cert = ssl_cert_Info(); -- Simple message indicating that we've found a cert (yay) window2:append( "Found a Cert " ); end
If you run the above source in the Wireshark 'Evaluate' window you may see this message:
Full Error Message Text: Lua: Error During execution of dialog callback: [string "window2 = TextWindow.new("SSL Window");..."]:2: Field_get: A Field extractor must be defined before Taps or Dissectors get called
I haven't figured out what causes the message yet. To work-around the problem close and re-open Wireshark. I kept getting this message intermittently, which is another reason why I chose to abandon the Lua route for Wireshark scripting.
When it works, here's what it looks like when running:
Path of success: Play in the source code!
Given that the Lua path was taking quite a bit of time and would end up being less functional than simply delving into the C source code, I decided to jump straight in and see what I could figure out.
After playing around for a little while I found that the packet-ssl.c file was what I was looking for. This file (Found under <wireshark_repo_path>\epan\dissectors) dissects SSL and TLS traffic. This took awhile as I had to trace the source code to see where the 'work' was being done. Unlike C# in Visual Studio, the C code that makes up Wireshark has no intellisense or Right-click "Find all references". I can see why I stick to C# for business logic! :)
For my purposes, I had to get some data from the static void dissect_ssl3_hnd_cert(tvbuff_t *tvb, proto_tree *tree, guint32 offset, packet_info *pinfo) method in packet-ssl.c. This is the code that handles the SSL/TLS handshake and exposes the certificate used during authentication. As a proof of concept I started altering this method directly. I can come back later and spin the code off into a plugin or another more specialized 'dissector' later on if need be.
After adding a couple of additional headers to this C source file to get it to support the appopriate flags for the ws_open command, I was able to alter the while loop where the certificates were processed to extract the certificates to the hard disk (My Additions are noted in the code comments.):
Additional headers to include (At least on Windows)
#include <io.h> /* Included for */ #include <Fcntl.h> /* File Flags */
/* iterate through each certificate */
while (certificate_list_length > 0)
{
/* get the length of the current certificate */
guint32 cert_length;
cert_length = tvb_get_ntoh24(tvb, offset);
certificate_list_length -= 3 + cert_length;
proto_tree_add_item(subtree, hf_ssl_handshake_certificate_len,
tvb, offset, 3, ENC_BIG_ENDIAN);
offset += 3;
/****addition: Save SSL Certificate ****/
iterationStr = g_strdup_printf("%i", iterationn);
CertFileName = g_strconcat("c:\\InterceptedCertificates\\Cert", iterationStr, ".der", NULL);
fp = ws_open(CertFileName, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0777); /**** End additions ****/
(void)dissect_x509af_Certificate(FALSE, tvb, offset, &asn1_ctx, subtree, hf_ssl_handshake_certificate);
/**** Addition This grabs the Certificate's DER encoded bytes and saves it to a file */
ExtractedBytes = (guint8*)ep_tvb_memdup(tvb, offset, cert_length); /* ExtractedBytes is a guint* */
ws_write(fp, ExtractedBytes, cert_length); ws_close(fp); iterationn++;
/**** end Addition ****/
offset += cert_length;
}
This loop does most of the work for me (giving me the start byte position and length of cert in the buffer), so all I had to do was figure out how to write the cert bytes to disk It took me awhile to arrive at this source. I found that using simple C File operations would result in a binary file which is not recognizable as a certificate. This still puzzles me as I don't think the bytes are obfuscated or wrapped at this level of the code. I was able to validate that the cert bytes were 'different' by examining the PCAP capture in Wireshark and exporting the bytes directly from there.
Fortunately for me the solution was simple: use the same code that the Wireshark UI uses to export the certificate. This led me to ws_open, ws_write and ws_close. Those are all 'helper' methods that can be used to write bytes to the disk. I use the ep_tvb_memdup to narrow down what I want to write to the disk as I don't want any more or less than the certificate bytes.
For more information on Extracting Bytes from Wireshark in C Code, see Section 1.4.2 Extracting data from packets in the Developer's README. This can be very helpful, especially if you have taken the time to read through the Developers Guide PDF and are up to date on GTK Types.
Wrap up
Rolling your own custom wireshark can be fun and rewarding. While it took me some time to get my head back in to 'C mode', once I did I found that I could trace the code and code something to the point where the Wireshark Developers mailing list could help me figure things out. Thanks to all Wireshark Devs, it's a powerful and versatile tool for slicing and dicing network data at a low level!