Upgrading a deprecated software or the tale of Flamingo (Part 1)

TL;DR: Code is available on Github

Flamingo

Flamingo is a XMPP client released in 2012 which differentiate itself with a high quality design. Considering the competition consisted almost solely of Adium, a barely maintained, very old (probably the early OS X days) project based on libpurple. From a user experience perspective, Flamingo was light years ahead, but two problems prevented a migration to be a no brainer.

Missing features

While supporting a fair amount of modern platforms at the moment of its release, it was missing a very important features: OTR Integration.

OTR

According to Wikipedia, Off-the-Record Messaging (OTR) is a cryptographic protocol that provides encryption for instant messaging conversations. This protocol has a couple of compromise (no delayed message, only one client online at any point in time…) but once those factored in, there has been no successful cryptographic OR implementation attack in recent memory. The most common library implementing the protocol, libOTR is well regarded and despite some raging design making its use a little bit cumbersome from a developer standpoint, it is the best way I’m aware of to protect an IM discussion.

Adium come with a reference plugin but Flamingo’s developer didn’t provide a proper integration with their homemade XMPP library.

Deprecated

In mid-2015, Flamingo’s developer announced they were not going to continue working on the project and offered to sell it to someone else. One year later, the project still appears to be in limbo and various email to offer making the project open source did not received any response and thus, this article came to be.

Project

My goal, when starting working on this project was to inject into Flamingo seamlessly (from a user standpoint, it must be totally invisible), add a small button in their UI to control a libOTR based wrapper which would, thanks to a couple of hooks, enable Flamingo to use OTR. This post will probably be posted in several parts, one for each milestone and I’ll do my best to explain the approach taken and why it worked, but also what didn’t.

Injection

Injection vehicule

My first idea in order to inject code in Flamingo was to write a small framework that would wake up early in the loading process and would hook as early as possible. However, adding a framework wouldn’t be enough as it wouldn’t be linked to the binary and tweaking the main binary flags to make it load our framework would break code signature. Stripping the code signature was an option but code signing is there for a good reasons and while a possibility, I decided that I wanted to explore other alternatives before manipulating anything in realm of the code signature.

The dynamic library path was the most stable vehicle but if framework wouldn’t cut it, what about a simple dylib? This would work but I’d still have to find a way to perform the actual injection.

Test library

In order to test whenever I would actually achieve code execution, I wrote a very simple library writing to the log whenever it was loaded or unloaded. In order to achieve that, I used the two very simple attributes: __attribute__((constructor)) and __attribute__((destructor)). They enable you to specify which function is to act as the library constructor and destructor. Those are called very early and significantly before main(). Thankfully, probably because of the priority orders, all the other frameworks are already loaded and thus, injection is possible at this stage.

A small sample code is included

Once the library compiled (with clang in Xcode in my case), it was time to inject it.

Injection

One of the reasons that made me tide to the side of the dylib was that I was aware there were ways to tell dyld (dynamic ld, OS X macOS’ dynamic linker) to add a given library to every binary it loads. After some research, I figured could be done using the dreaded DYLD_* environment variables.

Dreaded

Those variables are useful, but also extremely dangerous, as they enable a total takeover of any program spawned for the environment in which the variable is set: that’s an incredibly powerful persistence mechanism, which was used a couple of time to publicly compromise iOS’ and macOS’ security. Because they were so powerful, I didn’t want to set them system wide (it’d also rise eyebrows in the security/privacy community which would be the prime users of this plugin).

However, it was the only way I could think of not involving a kernel extension or an other active monitoring solution which would be as hard to swallow, with the added inconvenient of respectively increased attack surface in the kernel and loss of responsiveness/battery. Finally, those two solutions made the installation process even more cumbersome and were thus quickly discarded.

DYLD_INSERT_LIBRARIES

Of the whole DYLD_ family, DYLD_INSERT_LIBRARIES was the most interesting environment variable as it tells dyld to inject any libraries it contains in any app spawned. It’s limited by the scope in which you could set the variable but still incredibly powerful.

At first, as a proof of concept, I tried to inject system wide DYLD_INSERT_LIBRARIES=”/usr/local/***/test.dylib”.

System-wide injection

First, I saw some old mentions on mailing list claiming that setting variables in ~/.MacOSX/environment.plist would be the way to fo it but this path was deprecated as of 10.10.5 (no idea of the version at which it became the case).

I realized that the modern way is to use launchctl setenv (as a side note, achieving persistence through launchctl rely on adding a LaunchAgents for a users which would perform the various calls). However, I was surprised to discover that setting DYLD_INSERT_LIBRARIES with launchctl setenv wouldn’t actually set the variable, returning an empty string with getenv. Thankfully, this part of macOS is open source and I could dive and try to understand what was going on.

I first looked directly at dyld source code (not realizing the variable wasn’t actually set) and if dyld has some restrictions on when it would accept or not the injection (SUID binaries among others), this wasn’t the culprit. This is the moment launchd comes in. For security reasons, envitem_new (line 5926) will discard any variable with the wildcard DYLD_ if launchd_allow_global_dyld_envvars isn’t set.

This variable is by default set to false, unless:

  • The file /private/var/db/.launchd_allow_global_dyld_envvars exist (the directory isn’t protect by SIP so easy)

  • pid1_magic = false, which happens to mean that launchd PID must not be 1. Sadly, launchd IS PID 1 and thus, there is no reliable, persistant, way to get launchd to accept our environment variable, or is it?

Bypassing launchctl

Launchd is not bypassable, as it’s the foundation of the userland launching mechanism and killing it (so it respawn with a non-1 PID) not realistic in a production environment. However, launchctl isn’t the only to pass an environment variable to a binary when launching it.

After some despair, I ran into an helpful stackoverflow topic which mentioned that you could pass a path to a program when launching it in bash by using the following syntax: PATH=XXXXXX" ./binary. Interesting, could it work for us? Spoiler alert: it does.

The Script

The problem is now to find how to highjack the launch process to insert a small script. The first thing I tried was to add a shell script in Flamingo.app/Contents/MacOS and redirect the launch flow by tweaking Info.plist but sadly, it broke the code signature.

Then, I decided to create a separate application bundle, with the same icon and apparently identical to the original app but embedding the real .app in its Resources directory, along with the library. This started well but for some reasons, still on OS X 10.10.5, OS X couldn’t identify the shell script as a ASCII script and, not recognizing a correct x86 Mach-O file, probably returned EBADARCH resulting in the UI complaining about a PowerPC binary.

After that, I moved to using system() in a very small C script, which had the added bonus of enabling me to ask Xcode to sign the injection bundle, preventing tampering in the application performing the tampering (tampecepetion?). The third try was the good one and ‘Hello world’ appeared in the system console. We had injection! With the added bonus of a seamless build procedure. The only issue was that because the injected app was launched separately of the injector, the debugger isn’t attached to the application, and have to be manually attached at every instance I wanted to break into but hey, that’s already great progress!

Next time, I’ll write about to start messing with Flamingo once injected in the process without breaking everything.

I’m not sure I’ll bother writing the hooking and patching part, as it’s fairly specific and straightforward. If you read to this point and would care about this, drop me a message!

Edit 10/11/2018: Spoiler: I didn’t