Not Currently Listening

Cracking Meta’s Messenger Certificate Pinning on macOS

With Meta’s Messenger application for macOS being so close to the Texts.com model—that being a standalone desktop application—Batuhan İçöz who is leading the Meta platform project at Texts.com thought we could gain some valuable insight by analyzing it. Everyone knows that intercepting network requests is a great and low barrier-of-entry first-step.

Meta implements certificate pinning into their applications which enhances their security model, and prevents us from being able to execute a MITM (man-in-the-middle) attack on ourselves to analyze the requests made to their servers.

What is Certificate Pinning?

When you set up a proxy client capable of intercepting your requests, you’re forced to configure and trust a “certificate authority,” one which you created. Certificates issued by your certificate authority will be used and will be able to intercept and decrypt information pertaining to the requests.

If a service implements certificate pinning, they’ve effectively opted to accept certificates issued only by specific certificate authorities, preventing certificates issued by your certificate authority from being used.

With certificate pinning enabled, our self-signed certificate is invalid, and thus our requests cannot be intercepted.

Default Behaviour

Without disabling certificate pinning, all requests return an “Internal Error” and our proxy software indicates that the “SSL Handshake Failed” with the request not completing its lifecycle. We thus can infer no information about the request.

Desired Behaviour

We want to be able to successfully make requests and read the request, response and headers from our network debugging tool by using a MITM attack on ourselves.

Potential Approaches

One option I’ve found to work in the past would be to alter the URL strings in the binary to insecure self-hosted endpoints that don’t implement TLS. It would forward requests and responses between the end-client and end-server. This works best for smaller applications, unlike Messenger.

We could use a dynamic instrumentation library, such as Frida to achieve the desired outcome. I’ve found that Messenger in particular is prone to crashes when hooking into it and with all this overhead, it can be difficult to pinpoint the pain-point. There’s also the more complicated distribution process involved with Frida. Those who wanted to run it would need to configure a very specific environment and set of tools.

Despite this, I did attempt to use a Frida script that I’ve been maintaining over the past few years that works to bypass common certificate pinning libraries and methods. It works on the vast majority of applications. Unfortunately, Meta’s subset of applications is not part of this “vast majority.”

In this case, we’ll be looking to turn off certificate pinning entirely in a way which can be easily distributed to my fellow team members using binary patching.

The Approach

After downloading Messenger and moving it into my applications folder, I grabbed the compiled ARM binary from /Applications/Messenger.app/Content/MacOS/Messenger and imported it into Hopper.

Hopper allows us to disassemble, decompile, recompile, debug, and visualize compiled binaries.

Once the binary was loaded and references had loaded, I started by searching for certificate pinning related terminology such as “certificate,” “ssl,” “pinning,” etc.

"SSL pinning verification failed for host:" certainly felt like a great place to start.

Ideally we’ll modify as little as possible. When it comes to compiled binaries, modifications can easily result in gnarly crashes if we overextend ourselves. The best case scenario would be flipping a boolean value, reversing a conditional, etc., ideally modifying a single or few instructions.

I switched to the control flow graph, allowing me to visualize the execution flow and walked myself up a series of linked references. Eventually, I found a string which said "Using custom sandbox -> turn off SSL verification". I liked it. I scanned the file for references to the function that determines this flag and found it in the top of the procedure.

Looking at the function IsUsingSandbox(), we can see exactly where the returned value is being assigned. In the following screenshot, the w0 register is being moved from w19 and then returned. w19 is assigned through a load byte instruction.

Instead of assigning w19 through a load byte instruction, we’re going to just set it to true no matter what. This will effectively force IsUsingSandbox to be true, which judging from that string from earlier, means certificate pinning will be disabled.

Original

ARM: ldrb w19, [sp, #0x40 + var_20]
HEX: F3 83 40 39

Rewritten

ARM: mov w19, #1
HEX: 33 00 80 52

We can do this replacement with the hexadecimal mode, which allows us to directly modify the byte code in the application.

Result

After this, we can export our new executable using the “Produce New Executable” option under “File,” remove the signature from the executable, and we’re off to the races. We’ll replace the original Messenger binary with this new binary we’ve produced.

After relaunching Messenger, we can see that headers, response body, and all other request information is visible in our proxy tool. By modifying just 4 of the binary’s 97,477,728 bytes we can now intercept requests!

Want to see a similar approach for iOS? Read this 2020 post by Hassan Mostafa where he flipped a conditional branching instruction to crack certificate pinning on Instagram on a jailbroken iPhone.

Distribution

After compiling the binary, I sent it over to Batuhan to use. Once he had the binary in-hand he obtained and installed a signing certificate and proceeded to sign the application. Once that was done, he was able to utilize the binary on his system and view his own requests.

codesign --force --deep -s CERTNAME_OR_ID /Applications/Messenger.app

Written with ❤️ by Rida F'kih