Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capturing QUIC Keys #41

Open
SinghSek opened this issue Jan 23, 2025 · 16 comments
Open

Capturing QUIC Keys #41

SinghSek opened this issue Jan 23, 2025 · 16 comments
Assignees

Comments

@SinghSek
Copy link

Hello! I posted a similar question on my previous issue, but have not been able to figure it out and was advised to make a separate post. I have successfully captured TLS keys using FriTap, but my ultimate goal is to decrypt QUIC packets. In wireshark, under QUIC Protocols, I noticed one of the fields is QUIC Ports, and was wondering if there is a way to capture what ports are used when running the FriTap Command.

Additionally, I've read that QUIC may have it's own keys on top of TLS, and would like to ask if anyone has had luck decrypting QUIC (maybe capturing the QUIC Keys) using this tool.

Any advice is greatly appreciated!

@SinghSek
Copy link
Author

For more context, I am trying to capture packets from the android Messages app to analyze RCS packets, which from what I can tell using a pixel 9 device, use either UDP or QUIC.

@monkeywave
Copy link
Collaborator

Hi @SinghSek

thank you for your detailed question and for providing additional context—it’s much appreciated :-)

QUIC and TLS Keys

QUIC derives its encryption keys from the secrets negotiated during the TLS 1.3 handshake. These derived keys are used to secure QUIC traffic (cf. Using Transport Layer Security (TLS) to Secure QUIC). The good news is that Wireshark doesn’t require the derived QUIC keys directly—it can compute them using the TLS secrets (e.g., keys.log) captured by friTap. See Wireshark source file.

However, there are some considerations:

  1. QUIC Ports:

    • Wireshark uses the QUIC port information to associate decrypted packets with the correct streams.
    • friTap doesn’t explicitly log the ports in use, but you can identify the ports by analyzing the packet capture (e.g., log.pcap) generated by friTap. Filter for UDP traffic in Wireshark to see the source and destination ports being used.
  2. Decrypting QUIC:

    • Ensure that Wireshark is configured correctly:
      • Set the SSLKEYLOGFILE environment variable to point to the keys.log file generated by friTap.
    • Once the correct TLS secrets are applied, Wireshark should be able to decrypt the QUIC packets as long as the packets and keys match.

In recent versions of friTap, including 1.2.8.5, we’ve introduced some minor improvements that might help with your use case. There was also a bug in previous versions that could have affected key extraction in certain scenarios. I recommend updating to the latest version and trying again. You can update friTap using:

pip install friTap --upgrade

RCS Packets and friTap

Since you’re analyzing RCS packets from the Android Messages app on a Pixel 9 device:

  • Protocol Insight: RCS typically uses QUIC or UDP for communication, as you noted. Capturing and decrypting QUIC packets with friTap should work as long as the keys are correctly extracted and applied.
  • If friTap does not detect and log the necessary keys, this could indicate that additional hooks are required for specific libraries or functions used by the Messages app. In that case I would suggest to run BoringSecretHunter against all modules being loaded.

All the best

Daniel

@SinghSek
Copy link
Author

SinghSek commented Feb 2, 2025

Thank you for your breakdown, I am now encountering an issue where the keys do not get logged at all, FriTap runs perfectly fine, but after the process is finished, the keys.log file is empty. Any idea how I might overcome this?

@monkeywave
Copy link
Collaborator

Hi @SinghSek,

I'm a little confused— in your initial post, you mentioned that you were able to log some TLS keys but couldn't decrypt the traffic. Am I mistaken, or is this a new issue where no keys are being logged at all?

Also, are you still testing with the same app? Which Android version are you currently running? Which version of friTap you are using?

To better assist you, we’ll need more context. Could you provide details about how you're running friTap (e.g., command used, output logs, version number) so we can investigate further?
Best would be if you can include the debug output -do -v as well :-)

Looking forward to your response :-)

All the best

Daniel

@SinghSek
Copy link
Author

SinghSek commented Feb 6, 2025

Sorry for the confusion, I was initially testing with both a pixel 9 running android 14 and a samsung s21 running android 11. Previously the keys were logged but traffic was not decrypted.

Now I am only using the S21 with the same set up on the same app, and keys are not being logged. I verified decryption works on a pixel 5 as after every UDP and QUIC packet, http packets with the option to decrypt are present (choosing to decrypt QUIC packets show a random string of characters, but I think that is a separate issue for another time).

I am running this command: fritap -m -p log.pcap --full_capture -k keys.log 27944

Here is the debug output:

[] capturing whole traffic of target app
[
] Attaching to the first available USB device...
[] Successfully attached to the mobile device.
[
] doing full capture on Android
[] loading friTap frida script: _ssl_log.js
[
] Running Script on Android
[
] libssl.so found & will be hooked on Android!
[
] The module "libssl.so" has 525 exports.
[
] Found SSL_read 0x71cd9c0b84
[
] Found SSL_write 0x71cd9c0f90
[
] Found SSL_get_fd 0x71cd9c192c
[
] Found SSL_get_session 0x71cd9c7c58
[
] Found SSL_SESSION_get_id 0x71cd9c7864
[
] Found SSL_new 0x71cd9bfeb4
[
] Found SSL_CTX_set_keylog_callback 0x71cd9c33ec
[
] Found getpeername 0x74d9c2e700
[
] Found getsockname 0x74d9c2e6e0
[
] Found ntohs 0x74d9c29130
[
] Found ntohl 0x74d9c29128
[
] libconscrypt_gmscore_jni.so found & will be hooked on Android!
[] The module "libconscrypt_gmscore_jni.so" has 2 exports.
[
] Found getpeername 0x74d9c2e700
[] Found getsockname 0x74d9c2e6e0
[
] Found ntohs 0x74d9c29130
[] Found ntohl 0x74d9c29128
[---] error: skipping module libconscrypt_gmscore_jni.so
[---] Loader error: Error: missing argument
[
] libcronet.133.0.6876.3.so found & will be hooked on Android!
[
] Found getpeername 0x74d9c2e700
[
] Found getsockname 0x74d9c2e6e0
[
] Found ntohs 0x74d9c29130
[
] Found ntohl 0x74d9c29128
[
] Trying Pattern: {"primary":"3F 23 03 D5 FF C3 01 D1 FD 7B 04 A9 F6 57 05 A9 F4 4F 06 A9 FD 03 01 91 08 34 40 F9 08 1? 41 F9 ?8 0? 00 B4","fallback":"3F 23 03 D5 FF 03 02 D1 FD 7B 04 A9 F7 2B 00 F9 F6 57 06 A9 F4 4F 07 A9 FD 03 01 91 08 34 40 F9 08 ?? 41 F9 E8 0F 00 B4"}
[
] Module Base Address: 0x714b780000
[
] Module Size: 6029312
[---] There was an error scanning memory: access violation accessing 0x714bd21000
[---] Trying to rescan memory with permissions in mind
[
] trying to scan only readable parts of libcronet.133.0.6876.3.so ...
[
] Primary pattern failed, trying fallback pattern...
[
] Android dynamic loader hooked.
[] Logging pcap to log.pcap
[
] Logging keylog file to keys.log
[] Pattern found at (fallback_pattern) address: 0x714bb928ac
[
] Pattern based hooks installed.
[***] Remaining: AndroidNSSP version 1.0,AndroidOpenSSL version 1.0,CertPathProvider version 1.0,AndroidKeyStoreBCWorkaround version 1.0,BC version 1.61,HarmonyJSSE version 1.0,AndroidKeyStore version 1.0,KnoxAndroidKeyStore version 1.0,TimaKeyStore version 1.0

[] Ctrl+C detected. Cleaning up...
[
] pulling capture from device:
[] full mobile capture safed to _log.pcap
[
] remember that the full capture won't contain any decrypted TLS traffic. In order to decrypt it use the logged keys from keys.log
[] friTap not trace the sockets in use (--socket_tracing option not enabled)
[
] The resulting PCAP _log.pcap will contain all trafic from the device.
[] Attempting to detach from Frida process...
[
] Successfully detached from Frida process.
[*] Detached friTap from process successfully.

Thx for using friTap
Have a great day

Thank you for your continued help so far, for reference, I am trying to find the plaintext content of an RCS packet to verify the RCS standard is being used.

@monkeywave
Copy link
Collaborator

Hey @SinghSek ,

thx for the detailed outpout :-)
I just tried the latest version of friTap (version 1.2.8.8) against the Android Messages App on my Pixel 5 with Android 13 and I was able to extract the keys. Maybe the latest updates are aleady resolving your issue :-)

If its still not working can you connect to the target app with frida and provide us the output of the following command:

Process.enumerateModules().forEach( (element) => { if(JSON.stringify(Process.getModuleByName(element.name).enumerateExports().filter(exports => exports.name.toLowerCase().includes("ssl_ctx_new"))).length > 2){ console.log(element.name + " : \n" + JSON.
stringify(Process.getModuleByName(element.name).enumerateExports().filter(exports => exports.name.toLowerCase().includes("ssl_ctx_new"))));} });

All the best

Daniel

@monkeywave monkeywave self-assigned this Feb 7, 2025
@SinghSek
Copy link
Author

SinghSek commented Feb 7, 2025

Hey Daniel, thanks so much for the response, as I am using the commands in the terminal, how would I go about connecting to the android messages app via frida, would I just grab the PID? and how would I run the command, you have wrote, would that just be in a python file? Sorry, I'm still a little new to this type of analysis, and your help is really appreciated!

Also, I have just verified that, even on the newest version of FriTap, the S21 running android 11 still results in an empty keys.log file when running the fritap command. I've been able to collect the keys off of a pixel 5 running Android 11 however.

@monkeywave
Copy link
Collaborator

Hi @SinghSek ,

thx for your fast response :-)
You can connect (attach) with frida via PID or the name of the target app :-)
Assume we have the following output:

$ frida-ps -Uai
  PID  Name                    Identifier                               
-----  ----------------------  -----------------------------------------
11246  Android Auto            com.google.android.projection.gearhead   
11453  Chrome                  com.android.chrome                       
10843  Einstellungen           com.android.settings                     
32185  Firefox                 org.mozilla.firefox                      
23750  Google                  com.google.android.googlequicksearchbox  
10444  Google Play Store       com.android.vending                      
 8949  Kamera                  com.google.android.GoogleCamera          
 8183  Maps                    com.google.android.apps.maps             
 6940  Messages                com.google.android.apps.messaging

Than we cann connect to the Messages App using frida the following ways:

frida -U 6940
     ____
    / _  |   Frida 16.6.6 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to Pixel 5 (id=09011FDD4007DJ)
                                                                                
[Pixel 5::PID::6940 ]->

or direct via its name:

$ frida -U Messages
     ____
    / _  |   Frida 16.6.6 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to Pixel 5 (id=09011FDD4007DJ)
                                                                                
[Pixel 5::Messages ]-> Process.enumerateModules().forEach( (element) => { if(JSON.stringify(Process.getModuleByName(element.name).enumerateExports().filter(exports => exports.name.toLowerCase().includes("ssl_ctx_new"))).length > 2){ console.log(element.name + " : \n" + JSON
.
stringify(Process.getModuleByName(element.name).enumerateExports().filter(exports => exports.name.toLowerCase().includes("ssl_ctx_new"))));} });
libssl.so : 
[{"type":"function","name":"SSL_CTX_new","address":"0x6db848f1a4"}]
[Pixel 5::Messages ]->

As you can see I just paste the command I provided to the frida REPL terminal.

Hope that already helps :-)

All the best

Daniel

@SinghSek
Copy link
Author

Thanks so much! Here is the output of that command:

Process.enumerateModules().forEach( (element) => { if(JSON.stringify(Process.getModuleBy
Name(element.name).enumerateExports().filter(exports => exports.name.toLowerCase().includes("ssl_ctx_new"))).lengt
h > 2){ console.log(element.name + " : \n" + JSON.
stringify(Process.getModuleByName(element.name).enumerateExports().filter(exports => exports.name.toLowerCase().in
cludes("ssl_ctx_new"))));} });
libssl.so :
[{"type":"function","name":"SSL_CTX_new","address":"0x751cb59948"}]

And this is on the S21 running android 11

@SinghSek
Copy link
Author

I've just verified that the same thing occurs with a pixel 9 running the latest version of frida and android 11. Both were tested using my windows laptop, could there be a windows reason keys aren't getting logged, like adb blocking a pull? For reference, it pulls the traces just fine, only the keys.log file is empty. the command I am using is:

fritap -m -p log.pcap --full_capture -k keys.log

Thank you so much for your continued help!

@Jss1996
Copy link

Jss1996 commented Feb 11, 2025

I've now tried it with a second machine and on both the latest version of frida and frida 16.6.4 on the rooted pixel 9 running android 15, and still no luck. The traces are captured, but the keys aren't logged, I also made sure to turn rcs off and on to ensure the register process.

Sorry this has been going on so long, but this seems to not want to work for me right now xD

@SinghSek
Copy link
Author

I've noticed something interesting, on the pixel 9, keys were logged for some sessions, but not another. The sessions that the keys were logged for had the PlayIntegrityFix module disabled, but RCS still enabled (not re-registered, so still active), this allowed the keys to be logged for that one instance.

As soon as the PlayIntegrityFix module was activated and RCS re-registered, no keys were logged for that session. I tried to recreate this by disabling PlayIntegrityFix and re-registering RCS, and still no-dice. Could there be some interaction between the PlayIntegrityFix module and FriTap not being able to log the keys?

@SinghSek
Copy link
Author

In the same way, when testing a Motorola phone running android 12, the keys are sometimes captured, and sometimes not. I'd say about a quarter of the time keys are captured for this phone. I've also verified this behaviour with the pixel 9 on a third system, this time running the Frida and FriTap commands in cmd instead of the terminal in pycharm

@monkeywave
Copy link
Collaborator

Hi @SinghSek,

thanks for all the detailed information.

Unfortunately, I can only test friTap on the devices I have available.
I’ve run friTap on an emulator running Android 15, and in that setup, the keys were successfully captured.

From your output, it looks like the friTap hooks are working as expected. The fact that you see SSL_CTX_new being enumerated indicates that friTap is installing its hook correctly; this function is used to set up the keylog callback via BoringSSL.

To further investigate whether the underlying problem is related to friTap and its handling of Frida hooks, I created AndroidTLSKeylogger on codeshare.frida.re.
It uses the same hooks as friTap to capture SSL/TLS keys. You can run it as follows:

chromium.webview_shell
(env)  friTap   main  frida -U --codeshare monkeywave/androidtlskeylogger 6940 
     ____
    / _  |   Frida 16.6.6 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to Android Emulator 5554 (id=emulator-5554)
Attaching...                                                            
Hello! This is the first time you're running this particular snippet, or the snippet's source code has changed.

Project Name: AndroidTLSKeylogger
Author: @monkeywave
Slug: monkeywave/androidtlskeylogger
Fingerprint: 64a11842936672a341e6846a49fe395a9d211ebcc7d69203081482ca7bfbfbe8
URL: https://codeshare.frida.re/@monkeywave/androidtlskeylogger
        
Are you sure you'd like to trust this project? [y/N] y
Adding fingerprint 64a11842936672a341e6846a49fe395a9d211ebcc7d69203081482ca7bfbfbe8 to the trust store! You won't be prompted again unless the code changes.
[*] libssl.so is already loaded. Installing hooks.
[*] Found SSL_CTX_new in libssl.so at 0x7160152a00
...

If the keylogger functions correctly even when PlayIntegrityFix is enabled, that could indicate a bug within friTap. However, if you observe the same behavior regardless, it is more likely that the issue stems from how Frida and PlayIntegrityFix interact.

All the best,

Daniel

@SinghSek
Copy link
Author

When running frida -U --codeshare monkeywave/androidtlskeylogger 3764 (3764 being the PID for the Messages app), I get this output:

(Fritap37) PS C:\Users\Jared\Desktop\pythonProject> frida -U --codeshare monkeywave/androidtlskeylogger 3764
____
/ _ | Frida 16.6.6 - A world-class dynamic instrumentation toolkit
| (| |
> _ | Commands:
/
/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to moto g stylus 5G 2022 (id=ZY22H3N48G)
Attaching...
Hello! This is the first time you're running this particular snippet, or the snippet's source code has changed.

Project Name: AndroidTLSKeylogger
Author: @monkeywave
Slug: monkeywave/androidtlskeylogger
Fingerprint: 64a11842936672a341e6846a49fe395a9d211ebcc7d69203081482ca7bfbfbe8
URL: https://codeshare.frida.re/@monkeywave/androidtlskeylogger

Are you sure you'd like to trust this project? [y/N] y
Adding fingerprint 64a11842936672a341e6846a49fe395a9d211ebcc7d69203081482ca7bfbfbe8 to the trust store! You won't be prompted again unless the code changes.
[] libssl.so is already loaded. Installing hooks.
[
] Found SSL_CTX_new in libssl.so at 0x6d7d8fc37c
[] Hooking dlopen at 0x70d413a01c
[
] Hooking android_dlopen_ext at 0x70d413a0f8
[moto g stylus 5G 2022 ::PID::3764 ]->

It ends with the Frida terminal and I have to exit. Are the keys supposed to be collected upon exiting, so:

[moto g stylus 5G 2022 ::PID::3764 ]-> exit

Thank you for using Frida!
(Fritap37) PS C:\Users\Jared\Desktop\pythonProject>

Or is something else supposed to happen? If exiting collects keys, then this still resulted in the same issue, with no keys being collected. The interesting thing is that, when testing calling through the messages app, keys were collected in a different trace.

Thank you for your help so far!

Jared

@monkeywave
Copy link
Collaborator

Hi Jared,

thanks for reaching out again :-)

The AndroidTLSKeylogger project is designed to print the extracted keys directly to the terminal while Frida is running. You shouldn’t need to exit for the keys to appear—they should be displayed in real-time as they are logged.

From your output, I can see that the necessary hooks have been applied correctly, which means that the script is properly attaching to the target process. However, the fact that no keys are being printed suggests that the hooked functions are not getting executed, which is quite strange.

At this point, I’m not exactly sure why this is happening—especially since you mentioned that keys were collected in a different trace when making a call. Unfortunately, I am unable to reproduce this issue on my available test systems, so I can’t provide a concrete fix.

That said, I have just updated the script on Frida Codeshare. This update might help, but it’s more of a guess than a confirmed fix.

Let me know if you notice any changes after testing again :-)

All the best

Daniel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants