Decrypting BIG-IP Packet Captures without iRules
In this final article focused on taking and decrypting BIG-IP packet captures, I take the advice of MVPers Nikoolayy1 and Juergen_Mang by losing the iRules and instead utilizing the system database key that allows you to embed the session keys in the tcpdump capture as it's capturing. This by default includes the TLSv1.3 sessions missing from my iRule solution in the last article (though it is possible still with a more robust key capture in the iRules to include them there as well.) All this is covered in the live stream sessions, where I walk through converting the iRule solution to the database key. Read on below for the final solution.
Live Session - Nearing the Final Solution
Live Session - Completion
Solution Details
The script in its entirety is here in the github repo, but I'll cover the imporant updates here. First, the tcpdump string that we modify in the script needs an update to incorporate the f5 flag:
TCPDUMP_BASH_STRING = """timeout -s SIGKILL CAP_SECS tcpdump -s0 -nni 0.0:nnnp --f5 ssl:v VIRTUAL_IP -w /shared/images/autocap_DATESTRING.pcap"""
I'm still saving to /shared/images to be able to use the rest worker node for downloads from that directory. Next, because the database key exports session keys, you only want that key active for troubleshooting. Do make sure that's handled automatically, I created a function to call right before and after the capture:
def toggle_sslprovider(bigip, state):
data = {'value': state}
bigip.modify('/mgmt/tm/sys/db/tcpdump.sslprovider', data)
print(f'\tDatabase key tcpdump.sslprovider has been {state}d...continuing.')
Running the tcpdump itself only required a couple cosmetic naming changes so I won't include that function here. In the previous solution, the session keys were in log files and had to be culled and stored in a file. That function, create_keyfile, is not needed here so it's been removed. Instead, we need to extract the keys from the tcpdump file and then store them in their own file. I created the function extract_keys to do this.
def extract_keys(tcpdump_file):
tshark_process = subprocess.run(["tshark",
"-r", f"{tcpdump_file}",
"-Y", "f5ethtrailer.tls.keylog",
"-Tfields",
"-e", "f5ethtrailer.tls.keylog"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
keyfile = open('session_keys.pms', 'w')
subprocess.run(["sed",
"s/,/\\n/g"],
input=tshark_process.stdout,
stdout=keyfile,
stderr=subprocess.STDOUT)
keyfile.close()
print('\tExtracted keys file: session_keys.pms...continuing.')
It was this function that I got hung up on in the first live session. Piping and redirecting stdout to a file was not something that python's subprocess was in love with in the way that I was attempting. I solved that in the second live session linked above. The decryption function required only a cosmetic change. The final work was just to make sure the workflow of calling the functions was correct:
vip_name, duration, filters = user_responses()
br = instantiate_bigip(int(duration))
toggle_sslprovider(br, 'enable')
tcpdump_file = run_tcpdump(br, duration, vip_name, filters)
toggle_sslprovider(br, 'disable')
download_files(br, tcpdump_file)
delete_files(br, tcpdump_file)
extract_keys(tcpdump_file)
decrypt_capture(tcpdump_file)
And with that, it worked a treat! Using the database key also significantly reduced the complexity of the solution, which I'm always a fan of.
Conclusion
The solution is final, but I'll refactor the code a little to eliminate the need to manage in separate scripts whether it's a case or not, whether you want a qkview or not, etc. I'll converge all of into a single scripts with command line options to enable/disable different features.
And regarding finality, this is only the beginning of what we can automate now that we have a decrypted capture. What might we be able to pull out of the data to make your job easier before you even launch Wireshark? What might we be able to do with that data to format your Wireshark profile upon launch? Hmm...stay tuned for details!