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

Cannot dump quic keys on Android #38

Open
Diniboy1123 opened this issue Dec 28, 2024 · 3 comments
Open

Cannot dump quic keys on Android #38

Diniboy1123 opened this issue Dec 28, 2024 · 3 comments
Assignees
Labels
bug Something isn't working

Comments

@Diniboy1123
Copy link

Diniboy1123 commented Dec 28, 2024

Hi,

First of all this is probably one of the most precisely written frida tools out there, I found a gem just now with it. Thanks a lot for developing such a useful tool.

However I can't seem to get it working with Cloudflare warp. It's a free app, you download it, launch it and in the Settings -> Advanced -> Connection options -> Tunnel protocol I picked MASQUE.

In MASQUE mode they are essentially using connect-ip from this RFC which runs through quic so there is definitely TLS 1.3 involved. I can confirm that by using wireshark and inspecting the encrypted packets.

If I run the tool using friTap -m -k keys.log -v -s com.cloudflare.onedotonedotonedotone -do -p test.pcap --full_capture I can see a bunch of keys captured:

[*] libssl.so found & will be hooked on Android!
[***] The module "libssl.so" has 554 exports.
[***] Found SSL_read 0x7c2fcd8120
[***] Found SSL_write 0x7c2fcd8590
[***] Found SSL_get_fd 0x7c2fcd91d0
[***] Found SSL_get_session 0x7c2fce1640
[***] Found SSL_SESSION_get_id 0x7c2fce10c0
[***] Found SSL_new 0x7c2fcd7230
[***] Found SSL_CTX_set_keylog_callback 0x7c2fcdb3e0
[***] Found getpeername 0x7d3677f580
[***] Found getsockname 0x7d3677f540
[***] Found ntohs 0x7d36776d30
[***] Found ntohl 0x7d36776d20
[*] Android dynamic loader hooked.
[*] Logging pcap to test.pcap
[*] Logging keylog file to keys.log
[***] ProviderInstaller could not be found, although it has been loaded
[***] Remaining: AndroidNSSP version 1.0,AndroidOpenSSL version 1.0,CertPathProvider version 1.0,AndroidKeyStoreBCWorkaround version 1.0,BC version 1.68,HarmonyJSSE version 1.0,AndroidKeyStore version 1.0
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_RANDOM REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_RANDOM REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_RANDOM REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_RANDOM REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
EXPORTER_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
EXPORTER_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
EXPORTER_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_RANDOM REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
CLIENT_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
SERVER_TRAFFIC_SECRET_0 REDACTED REDACTED
[***] invoking keylog_callback from OpenSSL_BoringSSL
EXPORTER_SECRET REDACTED REDACTED

So I seemingly end up with some keys, but I assume these are for some other connection, like maybe DNS over HTTPS that happens already in the tunnel or during some API calls, as I cannot seem to decrypt packets using any of these keys in wireshark...

I wrote a go implementation of a similar quic based project where I defined a KeyLogWriter and that dumped these keys:

CLIENT_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
SERVER_HANDSHAKE_TRAFFIC_SECRET REDACTED REDACTED
CLIENT_TRAFFIC_SECRET_0 REDACTED REDACTED
SERVER_TRAFFIC_SECRET_0 REDACTED REDACTED

And that's enough for wireshark to properly decrypt all data. Therefore I am guessing that the quic crypto either isn't done by the statically linked OpenSSL inside the libnativetunnel.so, but maybe it's rust's own thing OR we are actually missing something in friTap to handle quic and openssl properly.

The binary also has some string:

SSLKEYLOGFILE environment variable detected, but no keys will be written due to compilation without the capture_keylogs feature

Which is really unfortunate. :(

Any help would be very much appreciated!

@monkeywave
Copy link
Collaborator

Hi @Diniboy1123 ,

Thank you for reaching out and for the detailed problem description.

Regarding the issue, I wanted to clarify: is the OpenSSL library statically linked inside the libnativetunnel.so binary, which is part of the Cloudflare Warp app? From the provided output, it seems that friTap is only hooking the libssl.so library, which is dynamically linked to the Cloudflare Warp app. This might explain why we are not capturing the keys for the QUIC connection.

To expedite the troubleshooting process, you might want to run BoringSecretHunter against libnativetunnel.so. If it identifies anything related to the key material or TLS hooks, it could be a valuable lead for us to proceed further.

Thanks again for your detailed insights and for your patience while we investigate this further.

All the best,

Daniel

@monkeywave monkeywave self-assigned this Dec 30, 2024
@monkeywave monkeywave added the bug Something isn't working label Dec 30, 2024
@Diniboy1123
Copy link
Author

Diniboy1123 commented Jan 1, 2025

Hey,

Thanks for the quick and really helpful response! I couldn't respond earlier, because my PSU died.

Good catch on libssl.so, I didn't even notice it, assumed it's just printing that so name, because it recognized openssl patterns in the app (but I thought it comes from the libnativetunnel.so). Yes, OpenSSL is statically compiled into the rust libnativetunnel.so library. It doesn't seem to have stripped symbol names either (which makes sense).

But I have tried running the mentioned tool before writing the issue, just forgot to paste the result in. Now re-ran it on my server (no new lines, because of my terminal emulator on phone):

Identifying the ssl_log_secret() function for extracting key material using Frida.                                    Version: 0.8 by Daniel Baier                                                                                          [*] Start analyzing binary libnativetunnel.so (CPU Architecture: ARM64). This might take a while ...                  [*] Looking for SERVER_HANDSHAKE_TRAFFIC_SECRET            [*] Trying fallback approach with String CLIENT_RANDOM     [-] No functions found using the string.                   ssl_log_secret() function not found.                       === Finished analyzing libnativetunnel.so ===

Even though it definitely contains some openssl symbols:

$ readelf -a binaries/libnativetunnel.so | grep SSL_connect                            67572: 0000000000b101fc    40 FUNC    LOCAL  HIDDEN    14SSL_connect

I am not actually sure whether its openssl or rustls used for the connection though. But if we could hook openssl manually, that would already be huge. Once my new PSU arrives, I will try to figure out the addresses using Ghidra and try to feed it to friTap manually.

@monkeywave
Copy link
Collaborator

Hi @Diniboy1123,

Thank you for the detailed feedback and insights! I had some time to explore the app and the libnativetunnel.so binary further, and here’s what I’ve found:


Findings

  1. QUIC Implementation:
    It appears that quiche-tokio and quiche are used for the QUIC implementation in the app. quiche internally relies on BoringSSL for its cryptographic operations (reference).

  2. BoringSSL Compilation & Updates:
    I’ve updated BoringSecretHunter so it can now successfully identify the ssl_log_secret() function in the target application. I’ve also written a short Frida script (warp_hook.js) that installs a hook for this function. However, despite the hook being installed, it is never invoked during runtime.

Example invocation:

   frida -U -p $(frida-ps -Uai | grep -i "1.1.1" | awk '{print $1}') -l warp_hook.js

Output:

  ____
/ _  |   Frida 16.2.1 - 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)
Attaching...
BoringSSL module not found.
Hooking dlopen
Hooking android_dlopen_ext
[Pixel 5::PID::1130 ]-> [android_dlopen_ext] Loading library: /data/app/~~sMdWLz6GHUjGpRXPLPJXJQ==/com.cloudflare.onedotonedotonedotone-WzdZfb0wt63A6ML1C4epqg==/split_config.arm64_v8a.apk!/lib/arm64-v8a/libnativetunnel.so
[Dynamic Load] libnativetunnel.so loaded dynamically.
Module Base Address: 0x77b4c8c000
Module Size: 17985536
[*] Start hooking on arch: arm64
Pattern found at (ssl_log_secret()): 0x77b579a978
Analyzing address: 0x77b579a978
Page Info:
Base Address: 0x77b5339000
Size: 10272768
Protection: r-x
/data/app/~~sMdWLz6GHUjGpRXPLPJXJQ==/com.cloudflare.onedotonedotonedotone-WzdZfb0wt63A6ML1C4epqg==/split_config.arm64_v8a.apk
  1. BoringSSL Hooking I explored the binary further and identified some BoringSSL-related functions. Unfortunately, while the hook for ssl_log_secret() installs correctly, it is never invoked during runtime, which suggests the function might not be used in the current implementation.

  2. Hooking quiche Functions: I suspect that hooking some of the quiche functions could yield better results. However, attempts to hook these functions consistently crash the app and Frida. This may be due to how these functions are invoked or optimized in this implementation.
    Below is the script I used:

// Hook the libnativetunnel.so library
function hookQuicheFunctions() {
    var moduleName = "libnativetunnel.so";
    var module = Process.getModuleByName(moduleName);
    if (!module) {
        console.log(moduleName + " not found in memory.");
        return;
    }
    console.log(moduleName + " loaded at base address: " + module.base);

    // Enumerate symbols in the module
    var symbols = module.enumerateSymbols();
    //var symbols = module.enumerateExports();
    symbols.forEach(function (symbol) {
        try {
            if (symbol.name.includes("bssl") || symbol.name.includes("quiche")) {
                // Check if symbol.address is not zero
                console.log("Hooking: " + symbol.name + " at " + symbol.address);

                // Hook the function
                Interceptor.attach(symbol.address, {
                    onEnter: function (args) {
                        console.log("[quiche function] Called: " + symbol.name);
                    },
                    onLeave: function (retval) {
                        console.log("[quiche function] Returned: " + retval);
                    }
                });
            }
        } catch (e) {
            console.log('[!] Error in onLeave: ' + e.message);
        }
    });
}

Unfortunately, I don’t have time to investigate this any further at the moment. If you manage to get more insights or progress on hooking quiche functions, feel free to share your findings. I hope the updates to BoringSecretHunter and the Frida script provide a good starting point for further exploration.

Let me know if you make any progress or have additional questions. I’d be happy to help where I can :-)

All the best,

Daniel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants