Not too long ago, news broke about a malicious version of Xcode dubbed XcodeGhost that had been surreptitiously injecting malicious code into iOS developers’ apps. Apple responded by pulling infected apps from the App Store and posting a FAQ. Since the infections were focused in China, and our company follows best practices like downloading Xcode from Apple and keeping GateKeeper enabled, there was a tendency to say this is not an issue that would affect us.That was until we recently submitted an app update to the App Store and within a few minutes received an email rejection.
Invalid Executable – The app in [REDACTED].app has been built with an unsupported version of Xcode. Make sure Gatekeeper is enabled, download the latest version of Xcode from developer.apple.com, rebuild the app, and submit it again.
After a lot of failed guesses, head scratching, and dead ends, we discovered that malicious XcodeGhost code had found its way into our app via a third-party framework.
Back in September, security researchers discovered a malicious version of Xcode (dubbed XcodeGhost) that could inject nefarious code into your apps. The rejection message from Apple seemed to be hinting at that, but it made no sense to us because all of our developers download Xcode from either the Mac App Store or Apple Developer Portal.
Not too long ago, we ran into submission problems as a result of having a beta version of OS X on a machine that built an App Store submission, so that was an initial suspect. We tried rebuilding and resubmitting from several different machines, including a clean build server environment; all resulted in the same error.
To be absolutely sure, and as we were running out of ideas, we followed Apple’s instructions to validate all copies of Xcode to ensure XcodeGhost hadn’t somehow found its way onto one of our machines. All gave the same result:
source=Mac App Store
Even after this, we attempted reinstalling Xcode on multiple machines and adjusting changing Gatekeeper settings. The rejections continued. At some point we started to talk through what changes were in this release. Several of the changes were updates to third-party frameworks. What would happen if a third party had used XcodeGhost to compile a framework and then sent it to us?
Initially, we weren’t sure what we could do to check a binary file for signs of XcodeGhost, so we tried something drastic. We started pulling out frameworks to try recompiling and submitting without them. We identified what we thought was the most likely candidate to cause an issue, and decided to start there. Without too much effort, we managed to remove it from the app, and we resubmitted. (We could never actually ship this version of the app, as the third-party framework was essential for the app’s primary functionality.) Within a few minutes we saw the app move out of “Processing…” in iTunes Connect and become available for submission. Removing the third-party framework had stopped the rejections. We added it back in and tried submitting again. Rejected.
Looking for something more concrete to pinpoint the root cause, we started reading around about Xcode Ghost. We came across URLs that were used by XcodeGhost’s C2 (command-and-control) servers. As a shot in the dark, we started grepping the framework for the URLs and got a positive result on the first one we checked. One of the binary files in the framework had the exact string of a URL known to be tied to XcodeGhost (http://init.icloud-analysis.com).
Additionally, we installed a build on a device and proxied the traffic. Shortly after the app launched, we could see requests going out to http://init.icloud-analysis.com. Fortunately, the C2 servers associated with XcodeGhost were taken down (or otherwise disappeared) shortly after they were uncovered in September, so all requests to that URL fail to resolve to anything; no actual information from the device is ever received by anybody at this point.
By now we felt confident enough in the cause of the rejection to reach out to the third-party vendor, explain the problem, and ask for a new framework that was compiled with an authentic version of Xcode. Upon receiving the updated version we ran our checks with grep and the proxy again, and confirmed we were no longer seeing any indications of XcodeGhost.
Our current solution for this specific problem is to recursively grep through all of our projects for all URLs and method names known to be associated with XcodeGhost. To do this we have a text file containing all the strings we want to search for:
The grep command we run in each of our repos is: grep -R -f filewithstrings.txt *
As new variants may arise, or new strings are identified with malware, they can be added to the file to include in the checks. You may also consider adding “http://init.” or “http://init.icloud-“. These of course increase the risk of a false positive, but have the benefit of possibly catching currently unknown URLs that XcodeGhost may have used, or will use in the future. Moving forward, our build servers will run this grep as part of a build step and builds will fail if any positive results occur.
While we can control and check the environments that our developers work in (and trust that they’re not downloading modified versions of Xcode from third parties), third-party libraries and frameworks are a bit out of our control. Most of our projects include code from third parties that is already compiled when we receive it, and often times, it’s not up to us what third-party code is included in the project. We have no control over the environment other vendors compile their code in. Even though we lack this control over these third-party libraries, we are ultimately responsible for integrating, shipping and troubleshooting them; something POSSIBLE Mobile has a lot of experience with. This particular issue was a new one for us. It serves as both a reminder and an example of how the changing landscape of this industry regularly brings new challenges that we’ll need to continue to find solutions for.
Thanks to Claud Xiao for help with additional strings to add to our initial search.
Update: While putting this post together, a new variant of XcodeGhost was identified. This version hides the C2 server URLs from static analysis by concatenating multiple strings at runtime to assemble the full URL. The grep above can still be used to detect old variants. It is unclear at this time if the new variant also obfuscates its method names in a similar fashion or if they would still be caught by grepping.