Percona has a great set of tools known as the Percona Toolkit, one of which is pt-upgrade. The idea behind this tool is to replay a captured sequence of queries that were executed on a different database server. This is very useful to validate if a new version of the database server works as expected with your application.

Pt-upgrade can process queries from different sources, such as the general log or the slow log. However, there are some issues with these sources. Enabling the general log can impact the server’s performance; it can grow very fast and must be collected on the database server. The slow log has the same issues as the general log if we set a low threshold, and if we set a high threshold, we will miss many queries.

Luckily, pt-upgrade can also process the output of a network capture file (pcap). This is a great option because we can capture the traffic on the application server without impacting the database server. The problem is that most of the time, the traffic between the application and the database server is encrypted using TLS. So, how can we decrypt this traffic to be able to use pt-upgrade?

Spoiler alert: There is a method to decrypt TLS traffic, but this method can’t be used to feed pt-upgrade. At least it can be used currently. The reason is that Wireshark can decrypt TLS traffic, but it does not support exporting the decrypted traffic to a pcap file. If you know how to do it, please let me know.

During my research, I found that Wireshark can decrypt TLS traffic if we provide it with the TLS session keys.

How does TLS encryption work?

I don’t know, but it really does not matter for the purpose of this blog post. You just need to know that TLS negotiates session keys that are used to encrypt the traffic between the client and the server. These session keys are generated during the TLS handshake and are unique for each session. Wireshark can use these keys to decrypt the traffic. But for security reasons, these keys are not stored anywhere.

So, to decode the traffic, we need some secret keys that are ephemeral and not stored anywhere.

The good news is that OpenSSL (the library used by most applications to implement TLS) has a feature that allows an application to retrieve the keys. The bad news is that this feature is not implemented by any of the database clients. It is not enabled for MySQL or PostgreSQL.

It is enabled for curl, but I don’t know how to use curl to connect to a database server.

Wouldn’t it be great if we could enable this feature without modifying the database client? As Obama used to say, and most of the Crypto Bros believe: Yes, we can!

How? Using a dynamic library shim.

What is a Dynamic Library Shim?

A dynamic library shim is a shared library loaded before the target library. The shim intercepts some or all of the function calls from the original library to perform ad hoc processing and can optionally forward the calls to the original library.
Shims are used to:

  • Modify or override the behavior of existing libraries.
  • Add tracing, profiling, logging, or safety checks.
  • Work around bugs or limitations.
  • Provide compatibility between ABI/API versions.

How does it work?

On Unix-like systems, this is achieved using the LD_PRELOAD environment variable. This variable tells the operating system’s dynamic linker: “Before you load any other library, load this one first.”
Because the shim is loaded first, it can define functions with the exact same names as the functions in the real library. The linker will find the function in the shim first and use it instead of the one in the real library.

Writing the shim library to capture TLS keys

Lazy as I am, I asked ChatGPT to write a shim library for me. After some reviews, corrections, and improvements, here is the final code. Just note that most of the comments were added by me, not by the AI. AIs lack the fine sense of humor that I have.

And yes, you don’t have to type it; you can download it from my GitHub repository: https://github.com/peppla/ws-shim/

Compiling the shim shared library

So we have the code, now we need to compile it, but before that, we need to install some packages. On a Red Hat-based system, we need at least:

Now we can compile the code:

And here it is, our shared library is ready to be used.

Let’s capture some traffic and decrypt it using Wireshark

We need to open two terminal sessions. In the first one, we will run Wireshark to capture the traffic, and in the other, we will run our application with the shim library preloaded. In this case, we will use the PostgreSQL client to connect to a PostgreSQL server running on the same host.

Terminal 1: Running Wireshark

We will use tshark, the command-line version of Wireshark, to capture the traffic. As I’m running the test locally, I will capture the traffic on the loopback interface.

If you want to capture the traffic between two different hosts, you need to change the filter to something like:

For MySQL, change the port to 3306. I did not test MongoDB or other databases, but as long as they use dynamically linked OpenSSL libraries, it should work. And do not forget to change the interface.

Once Wireshark is running, we can move to the second terminal.

Terminal 2: Running the PostgreSQL client with the shim library

To use the library, we need to set two environment variables:

The first one tells the dynamic linker to load our library before any other library. The second one tells our library where to write the TLS keys. Replace $HOME with the correct path of the library and the directory where you want to save your keys. Now, we run the PostgreSQL client. In my case, I created a database user named test with the supersecret password test. I’m so lazy that I did not create a database, so I will connect to the default PostgreSQL database.

Now we check that the keys were dumped:

The output should look like this:

This means that the keys were dumped successfully, and we can use them to decrypt the traffic captured by Wireshark.

Terminal 1: Stop Wireshark

Now we can stop tshark by pressing Ctrl+C. The capture file postgres_capture.pcap should be created in the current directory. As we executed the capture as root, we may need to change the ownership of the file and move it to another location.

Analyzing the captured traffic

We will use tshark to analyze the captured traffic. You can also use the Wireshark GUI if you prefer. First, let’s validate that we can decrypt the traffic using the keys we dumped previously. As a security measure, I transferred the pcap file to a different and super-safe directory that I will not disclose here.

What this command does is:

  1. Read the pcap file -r
  2. Add the option to use the keylog file -o
  3. We filter only the packets that contain PostgreSQL traffic with -Y
  4. With -T fields, we tell tshark to output only the specified fields
  5. With -,e we specify the fields we want to see, in this case, the data and the query
  6. Finally, we use grep to filter out empty lines

The results should look like this:

The first line is the query we executed, and the second one is the results. To decode the hex output, you just need to run:

Now that we know that the traffic is properly decrypted with the keys we have, we can analyze the traffic in more detail using:

For those who want to analyze MySQL traffic, the commands are similar, just change the port and the filters:

Analyzing the traffic using Wireshark GUI

If you prefer to use the Wireshark GUI, just open Wireshark and go to Preferences -> Protocols -> TLS. In the (Pre)-Master-Secret log filename field, enter the path to your keylog file (e.g., /home/pep/sslkeys.log). Click OK to save the settings. Then you can open the pcap file, and you should be able to see the decrypted traffic.

And as it is not a blog post without a picture, here is a screenshot of the Wireshark GUI showing the configuration you have to do to load the keylog file.

wireshark preferences

Happy decrypting!

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments