17 August 2016

Poking at iTunes for Automation

Patrick Wardle

Here at Synack, our global team of ethical hackers, the Synack Red Team (SRT) is often asked to analyze our customer’s mobile applications for security vulnerabilities and possible exploitation. To make the SRT more effective and enhance the overall SRT experience, Synack performs initial processing of such applications, allowing the SRT to focus their efforts more on vulnerability detection/testing and less on monotonous tasks. For example, providing a decrypted dump of an application from the iOS App Store allows an SRT member to perform static analysis without having to own a jailbroken iDevice. Neat!

Of course, in order to automatically perform such pre-processing, the application first has to be downloaded. How can this be programmatically accomplished? For iOS apps, we had to figure out how to interface with Apple’s iOS App Store. As always, there are many ways to “skin the proverbial cat”. Here, we’ll briefly discuss various approaches such as “talking” to the App Store directly, controlling iTunes via AppleScript, and instrumenting iTunes via native (injected) code.

//talking directly to the app store.
As always, there are many ways to “skin the proverbial cat”, and in order to talk directly with the iOS App Store, one needs to understand the protocol that iTunes “speaks” when downloading an application — Let’s start here.

Sniffing iTunes.app as an iOS application is downloaded (here, using ‘Charles Proxy’), provides some insight into this protocol:

Controlling iTunes via AppleScript automation and instrumenting iTunes via native code

Controlling iTunes via AppleScript automation and instrumenting iTunes via native code

Most of the protocol seems fairly straightforward, save for the ‘kbsync’ parameter. Googling this parameter reveals various stack overflow discussions surrounding its generation (e.g. iTunes Music Store purchase parameters), some old scripts on github that don’t appear to (still) work, and even a posting in Russia offering $10,000 to reverse the algorithm for generating this parameter.

Controlling iTunes via AppleScript automation and instrumenting iTunes via native code

-> translation
“Requires reverse-engineering of the algorithm for calculating the parameter “kbsync” in iTunes, passed as a parameter when the POST request, the purchase of music / applications. Used iTunes.dll (x86, Obj-C, Windows – 10Mb), iTunes (x86, Obj-C, Mac – 20Mb), itunesstored (ARM, Obj-C – iOS). The string itself is stored and referenced in the clear.
The project budget is $ 10,000;”

Ok, as correctly generating the ‘kbsync’ parameter doesn’t seem trivial, let’s punt on it for now (“fail early” is a good thing, ya?).

//controlling itunes via applescript
AppleScript provides a way to control or automate OS X applications, such as iTunes. Turns out, it’s pretty easy to get iTunes to browse to an app’s page, then trigger a download of said app.

First tell iTunes to open the application’s page via a link to the application (e.g. https://itunes.apple.com/us/app/maui-top-10/id635483392?mt=8). Then, find the ‘Download’ button and click it. There are several ways to find such UI elements. Manually, one can use a utility such as ‘UIBrowser’:

Controlling iTunes via AppleScript automation and instrumenting iTunes via native code

This button can also be found programmatically by simply iterating through all the UI elements, looking for a button with the text, ‘Download’:

#open app’s page
tell application “iTunes”   #open the app
open location item 1 of argvend tell

#check for button
if class of ele is button then

#does this match ‘Download’
if (accessibility description of ele as text) contains “Download,” then

#click the button
click ele

The pros to this method is that, yes, it can be used to programmatically download an application from the iOS App Store. The cons are simply that this method uses AppleScript. What’s wrong with AppleScript? Well for one, it occasionally fails at random with error codes that are annoyingly vague. Moreover, complex scenarios such as handling modal popups (authentication prompts, etc.) are challenging to deal with in AppleScript. As such, I decided to explore other solutions.

//instrumenting itunes via native (injected) code.
I’m a big fan of low-level or native-code solutions as they often provide a highly-granular level of control. Here, let’s discuss getting a dylib loaded into iTunes, bypassing iTunes’ anti-debugging logic, and swizzling various methods to interact with the interface (i.e. to download an application).

In recent years, Apple has locked down OS X (macOS) to generally disallow generic runtime code injections… even if you’re root. Yes, one may be able to use the ‘processor_set_tasks() function’ on various non-apple processes (http://newosxbook.com/articles/PST2.html), however for iTunes, something else is needed. Luckily, iTunes supports plugins :). As discussed in @osxreverser blog post, “AppleDoesntGiveAFuckAboutSecurity iTunes Evil Plugin Proof of Concept”, one can create an iTunes plugin, save it to ~/Library/iTunes/iTunes Plug-ins/ and then anytime iTunes is launched it will automatically load and execute the plugin. Perfect!

Now we have a reliable way to get code loaded and executed in iTunes. What to do? Well if you attempt to start iTunes under a debugger (lldb) it will fail:

$ lldb /Applications/iTunes.app
(lldb) target create “/Applications/iTunes.app”
Current executable set to ‘/Applications/iTunes.app’ (x86_64).
(lldb) run
Process 76045 launched: ‘/Applications/iTunes.app/Contents/MacOS/iTunes’ (x86_64)
Process 76045 exited with status = 1 (0x00000001)

Moreover, while attaching to a running instance of iTunes appears to initially work, one will quickly realize that after some random amount of time, iTunes simply exits:

$ ps aux | grep [i]Tunes
76166 /Applications/iTunes.app/Contents/MacOS/iTunes

$ sudo lldb -p 76166
(lldb) process attach –pid 76166
Process 76166 stopped

* thread #1: tid = 0xc48989, 0x00007fff8fd8bf72 libsystem_kernel.dylib`mach_msg_trap + 10, name = ‘iTunes main’
stop reason = signal SIGSTOP

Executable module set to “/Applications/iTunes.app/Contents/MacOS/iTunes”.
Architecture set to: x86_64h-apple-macosx.
(lldb) c
Process 76166 resuming
Process 76166 exited with status = 1 (0x00000001)

It is well known that iTunes utilizes anti-debugging logic (see ‘iTunes Hates You, pp 108, The Mac Hacker’s Handbook). Specifically, older versions of iTunes invoked ptrace with the PT_DENY_ATTACH flag. However, newer versions of iTunes do not use this method anymore. So how does iTunes currently attempt to prevent debugging? Disassembling the massive (31.1MB!) iTunes binary reveals an interesting function at 0x10063A74A. Decompiling this in Hopper shows a call to getpid(), followed by a call to sysctl(). Following this call, if some variable AND’d with 0x8 isn’t zero, the application will exit:

sub_10063A74Aloc_10063a798:
getpid();
sysctl(0x1, 0x4, var_-680, 0x288, 0x0, 0x0);
if ((var_-647 & 0x8) != 0x0) goto loc_10063a87d;loc_10063a87d:
_Exit(0x1);

Converting the decompiled code to pseudo-code, it’s pretty easy to see what’s going on:

#define P_TRACED 0x00000800//invoke sysctl() to get current process info

// ->mib: KERN_PROC, KERN_PROC_PID, and pid

sysctl(mib, 4, &info, &size, NULL, 0);//check if process is being traced
if(P_TRACED == (info.kp_proc.p_flag & P_TRACED))
{
//debugger detected
// ->exit!
_exit(0x1)
}

In short, iTunes calls sysctl() to populate a process kinfo_proc structure with information about itself. It then checks the ‘p_flag’ flag to see if it’s being traced (debugged), and if so, exits. This is actually a pretty standard method for a process on OS X to determine if it’s being debugged. Apple even provides sample code for this check (“How do I determine if I’m being run under the debugger?”).

Bypassing this anti-debugging check is trivial. One can either set a breakpoint and exit early (thread return), or patch it out. For example, to NOP out the jump that leads to the _exit(), simply invoke the following from within LLDB (TODO: version):

$ memory write 0x10063a7f3 0x90 0x90 0x90 0x90 0x90 0x90

With the anti-debugging logic neutered, it’s easy to poke around and figure out how to automatically ‘drive’ iTunes, from within.

First, to get code executed when a dylib is loaded, simply create a custom constructor:

//automatically invoked when dylib is loadedvv
__attribute__((constructor))void myConstructor(int argc, const char **argv)
{
//dbg msg
syslog(LOG_ERR, “loaded in %s (%d)\n”, argv[0], getpid());   //do anythingreturn;
}

Since the goal is to download apps from the iOS App Store, either open iTunes with a path to the application, or load the app from within the dylib’s constructor:

$ open /Applications/iTunes.app
itms://itunes.apple.com/us/app/maui-top-10/id635483392
//automatically invoked when dylib is loaded
__attribute__((constructor))void myConstructor(int argc, const char **argv)
{
//dbg msg
syslog(LOG_ERR, “loaded in %s (%d)\n”, argv[0], getpid());   //open app’s download page
NSWorkspace * ws = [NSWorkspace sharedWorkspace];
[ws openURL: [NSURL
URLWithString:@”itms://itunes.apple.com/us/app/maui-top-10/id635483392″]];return;
}

Either method will cause iTunes to load the applications ‘download’ page. But since this happens asynchronously, how will the code know when the app’s download page has loaded?

One way is to swizzle a notification method, specifically something like a ‘didFinishLoadForMainFrame’ method. (Swizzling Objective-C methods are coverted here and here).

Running classdump on iTunes gives a good target of class method to swizzle: ITWebKit2ViewMacOSPrivateWebView’s didFinishLoadForMainFrame method. ITWebKit2ViewMacOSPrivateWebView is the main (webkit) view that host’s the app’s download content. Via classdump, one can see it’s a subclass of the WKView.

$ classdump /Applications/iTunes.app/Contents/MacOS/iTunes

@interface ITWebKit2ViewMacOSPrivateWebView : WKView <ITViewLinking, ITAccessibleLinking, NSDraggingSource>
{
struct weak_ptr<ITWebKit2ViewMacOS> _owningITWebKitView;
_Bool _allowWebViewScroll;
}

Since the ‘WKView’ class doesn’t appear in the classdump, we can dump its methods at runtime (stackoverflow).

//dump an object’s methods

void DumpObjcMethods(Class clz) {   unsigned int methodCount = 0;
Method *methods = class_copyMethodList(clz, &methodCount);syslog(LOG_ERR, “Found %d methods on ‘%s’\n”, methodCount, class_getName(clz));

for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];

syslog(LOG_ERR,”\t’%s’ has method named ‘%s’ of encoding ‘%s’\n”,
class_getName(clz),
sel_getName(method_getName(method)),
method_getTypeEncoding(method));

/**
* Or do whatever you need here…
*/
}

free(methods);
}

Compiling this into the iTunes plugin, and restarting iTunes produces the following output:

$ tail -f /var/log/system.log

6/10/16 12:55:55.871 PM iTunes[82560]: ITUNES DOWNLOADER: loaded in /Applications/iTunes.app/Contents/MacOS/iTunes (82560)
6/10/16 12:55:55.892 PM iTunes[82560]: Found 418 methods on ‘WKView’

6/10/16 12:55:55.893 PM iTunes[82560]: ‘WKView’ has method named ‘_didFinishLoadForMainFrame’ of encoding ‘[email protected]:8’

Swizzling this method, will ensure the dylib’s code will get invoked anytime an app’s page is loaded.

//execute swizzle logic
// ->wrap in dispatch_once() to only do once

dispatch_once(&onceToken, ^{//swizzle
// ->ITWebKit2ViewMacOSPrivateWebView’s didFinishLoadForMainFrame

[self swizzle];});

//swizzle code
-(void)swizzle
{
//original method
Method originalMethod = NULL;

//replacement method
Method replacementMethod = NULL;

//look up orginal ‘_didFinishLoadForMainFrame’ method
originalMethod = class_getInstanceMethod(objc_getClass
(“ITWebKit2ViewMacOSPrivateWebView”), @selector(_didFinishLoadForMainFrame));

//grab it’s implementation (pointer)
// ->save into global, so can invoke in replacement method
originalImp = method_getImplementation(class_getInstanceMethod
(NSClassFromString(@”ITWebKit2ViewMacOSPrivateWebView”),
@selector(_didFinishLoadForMainFrame)));

//look up replacement method
replacementMethod = class_getInstanceMethod([self class],
@selector(swizzled_didFinishLoadForMainFrame));

//swizzle
method_exchangeImplementations(originalMethod, replacementMethod)

In the replacement method (swizzled_didFinishLoadForMainFrame), which is called whenever an app’s download page is loaded in iTunes, we can also grab relevant information about the app. How? First, grab the ITWebKit2ViewMacOSPrivateWebView’s browsingContextController instance variable (which belongs to the super class, WKView, and is of type ‘WKBrowsingContextController’). As shown in the opensource’d WebKit source code on Apple’s site, the ‘WKBrowsingContextController’ class has an ‘activeURL’ method which returns an NSURL of the current page. For application download pages loaded by iTunes, this will be a link to the app.

$ tail -f /var/log/system.log

6/10/16 1:20:56.267 PM iTunes[82741]: ITUNES DOWNLOADER: active url:
https://itunes.apple.com/us/app/maui-top-10/id635483392?mt=8

Extracting the app’s ID from this url, (e.g. 635483392) one can query the iTunes Search API.

//grab app id
appID = [activeURL.lastPathComponent substringFromIndex:[@”id” length]];//query iTunes Search API
appData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString
stringWithFormat:@”https://itunes.apple.com/lookup?id=%@”, appID]]];//convert to JSON
appInfo = [NSJSONSerialization JSONObjectWithData:appData options:kNilOptions error:&error];

This returns an informative log of JSON, with a variety of information about the app:

$ tail -f /var/log/system.log

6/10/16 1:20:56.724 PM iTunes[82741]: ITUNES DOWNLOADER: app info: {
resultCount = 1;
results = (
{
artistName = “DoubleYou Apps LLC”;
averageUserRating = 5;
bundleId = “com.doubleyouapps.mauitop10”;
formattedPrice = Free;
trackName = “Maui Top 10”;
version = “1.5”;

}
);
}

To recap, we’ve got a dynamic library loaded into iTunes, that has a method that’s invoked whenever a new application’s ‘download page’ is loaded by iTunes. The code can extract relevant information about the app, so all that’s left is to download the application.

Previous versions of iTunes provided a simple way to access the DOM tree that made up the applications ‘download page’. By walking this DOM tree, it was trivial to find the ‘download’ button and send it a click event. However, starting with version 12.4, iTunes changed a bunch under the hood. Now, everything is contained in the ITWebKit2ViewMacOSPrivateWebView, which AFAIK, doesn’t provide an easy way to access the DOM. However, one can either poke around at the C++ webkit objects that can be accessed via the ITWebKit2ViewMacOSPrivateWebView/WKView (objective-c) objects, or just send a download button a ‘mouse’ or ‘key down/up’ event. As the latter appeared simpler and more robust and ‘version proof’, this is what the code does.

Once the download button is clicked, iTunes takes care of everything else (communications with the app store, generating the kbsync parameter, etc.). End result? The application’s .ipa is nicely downloaded into ~/Music/iTunes/iTunes Media/Mobile Applications/ automatically. Hooray 🙂