Digging In The Vineyard, Part 2

The previous post in this series involved trying to make a post to Vine outside of the iOS app by reverse engineering their API. That led to two discoveries:

I am the Keymaster

  1. The Vine API is thankfully simple to deal with.
  2. Being able to make the right API calls is not all that useful without the keys required to upload a video to the Vine S3 bucket.

I'm going to need to find the Keymaster.

To follow along at home with this post, you'll need a jailbroken iOS device. I've got an iPhone running iOS 5 that meets this criteria, but I hear that kind of operation can be performed on iOS 6 these days. Go on, brick liberate your iPhone, then continue onward. You'll also want to install ssh while you're at it, and then you can get a working copy of gdb onto the device. I never said this was going to be easy.

Running Vine on the device and grepping the process list doesn't actually reveal the location of the app—the binary must have a non-obvious name.

Gabriel-Girondas-iPhone:~ root# ps ax | grep -i vine
59390 s000  R+     0:00.00 grep -i vine

Applications from the App Store are installed in /var/mobile/Applications, so that looks like a great place to point find at:

Gabriel-Girondas-iPhone:~ root# find /var/mobile/Applications -iname '*vine*'
/var/mobile/Applications/5EA25DA6-8D29-491E-9C46-DB304017241D/Bing.app/MapSDKResources.bundle/MapControlLibraryResources.bundle/pin_VineyardsWineries.png
/var/mobile/Applications/5EA25DA6-8D29-491E-9C46-DB304017241D/Bing.app/MapSDKResources.bundle/MapControlLibraryResources.bundle/pin_VineyardsWineriesClick.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/Library/Caches/com.crashlytics.data/com.vine.iphone
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/Library/Caches/com.vine.iphone
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/Library/Preferences/com.vine.iphone.plist
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/VineLocationIcon.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/VineLocationIcon@2x.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/VineLocationTag.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/VineLocationTag@2x.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/VineMainFeedLocationIcon.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/VineMainFeedLocationIcon@2x.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/vinebug.png
/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/vinebug@2x.png

They totally have a sense of humour about that kind of thing

Taking a cue from Clippy, Bing has appeared at the most inopportune time to just get in the way. However, it looks like the app bundle has been located at /var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137, and a quick grep of the process list for that UUID shows the location of the running Vine binary.

Gabriel-Girondas-iPhone:~ root# ps ax | grep C36542DB-CAA1-4C7C-8C45-E4CB62B1B137
59361   ??  Ss     0:04.51 /var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/iphone
59429 s000  R+     0:00.00 grep C36542DB-CAA1-4C7C-8C45-E4CB62B1B137

Let's get the binary off the phone to take a closer look at it in a less cloistered environment. One or more of class-dump, file(1), and otool might reveal more information.

$ scp root@10.0.1.3:/var/mobile/Applications/C36542DB-CAA1-4C7C-8C45-E4CB62B1B137/iphone.app/iphone vine-iphone
root@10.0.1.3's password: 
iphone                                                                                               100% 3312KB   3.2MB/s   00:01
$ file vine-iphone vine-iphone: Mach-O universal binary with 2 architectures
vine-iphone (for architecture armv7): Mach-O executable arm
vine-iphone (for architecture cputype (12) cpusubtype (11)):  Mach-O executable arm
$ class-dump vine-iphone 
/*
 *     Generated by class-dump 3.4 (64 bit).
 *
 *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2012 by Steve Nygard.
 */

#pragma mark -

/*
 * File: vine-iphone
 * UUID: 922C1689-2984-337D-983E-AABB8599F67A
 * Arch: armv7
 *       Minimum iOS version: 5.0.0
 *       SDK version: 6.0.0
 *
 *       Objective-C Garbage Collection: Unsupported
 *       This file is encrypted:
 *           cryptid: 0x00000001, cryptoff: 0x00002000, cryptsize: 0x00139000
 */

$ otool -o vine-iphone | head -n 20
vine-iphone (architecture armv7):
Contents of (__DATA,__objc_classlist) section
0013cc34 0x16902c
           isa 0x169018
    superclass 0x0
         cache 0x0
        vtable 0x0
          data 0x13da80 (struct class_ro_t *)
                    flags 0x94 RO_HAS_CXX_STRUCTORS
            instanceStart 4
             instanceSize 32
               ivarLayout 0x1318f4
                layout map: 0x73 0x6f 0x6d 0x65 0x20 0x73 0x74 0x72 0x69 0x6e 0x67 0x20 0x66 0x72 0x6f 0x6d 0x20 0x61 0x20 0x70 0x72 0x6f 0x74 0x65 0x63 0x74 0x65 0x64 0x20 0x73 0x65 0x63 0x74 0x69 0x6f 0x6e 
                     name 0x131873 some string from a protected section
              baseMethods 0x13d854 (struct method_list_t *)
       entsize 12
         count 30
          name 0xffa0f some string from a protected section
         types 0x133cda some string from a protected section
           imp 0x3441

As suspected, the binary is DRM-laden, so class-dump can't do much besides point that out, and running strings(1) against it isn't going to reveal anything of any use either. otool prints some vaguely interesting Objective-C runtime information, but the encryption thwarts it as well.

If we had a decrypted binary we could probably find out more information about the application, and I indeed dumped one when doing research for this post. Instructions on doing this are fairly readily available, and there are apps in Cydia that will perform the entire process for you.

However, for the purpose of pulling the keys from the binary, the information that I found most useful was a list of Objective-C classes used in the application. It isn't necessary to dump a decrypted copy of the app for this— the Objective-C runtime will happily spit it out. Dumping a decrypted copy of the app may be useful for other endeavours, so it's a handy technique to keep in mind. For the purposes of just dumping the classes, I've written a tiny chunk of C to pull this off instead.

I have that checked out, built the iOS version, and scped it over to the iOS device. Time to attach gdb to the Vine process and see if we can avoid segfault-city and garner some useful information.

Gabriel-Girondas-iPhone:~ root# ./gdb -p 59361
GNU gdb 6.3.50-20050815 (Apple version gdb-1821) (Fri Jun 29 08:41:41 UTC 2012)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "arm-apple-darwin".
/private/var/root/59361: No such file or directory
Attaching to process 59361.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ............................................................................................................................................................................ done
Reading symbols for shared libraries + done
0x31473004 in mach_msg_trap ()
(gdb) bt
#0  0x31473004 in mach_msg_trap ()
#1  0x31473200 in mach_msg ()
#2  0x372a03f2 in __CFRunLoopServiceMachPort ()
#3  0x3729f0f0 in __CFRunLoopRun ()
#4  0x372224a4 in CFRunLoopRunSpecific ()
#5  0x3722236c in CFRunLoopRunInMode ()
#6  0x339e1438 in GSEventRunModal ()
#7  0x30fcecd4 in UIApplicationMain ()
#8  0x0005042a in ?? ()
#9  0x000503e0 in ?? ()
(gdb) info sources
No symbol table is loaded.  Use the "file" command.
(gdb) call (void *) dlopen("/var/root/objc_rt_class_dump.dylib")
Reading symbols for shared libraries warning: Could not find object file "/Users/ggironda/Repositories/objc_rt_class_dump/objc_rt_class_dump.o" - no debug information available for "/Users/ggironda/Repositories/objc_rt_class_dump/objc_rt_class_dump.c".

. done
$1 = (void *) 0x2e4540
(gdb)

Loading the library leaves a file at /tmp/objc_rt_class_dump.txt on the device, the content being a list of the Objective-C classes in the application, along with property names, class and instance method names, and method implementation addresses. Grepping this for “Amazon” shows that the AWS iOS SDK is obviously in use here.

$ grep -i amazon objc_rt_class_dump.txt
AmazonS3Client
AmazonErrorHandler
AmazonAbstractWebServiceClient
AmazonJSON
AmazonRequestDelegate
AmazonCredentials
AmazonURLRequest
AmazonMD5Util
AmazonDictionaryUnmarshaller
AmazonServiceResponseUnmarshaller
AmazonServiceResponse
AmazonServiceRequest
AmazonWebServiceClient
AmazonUnmarshallerXMLParserDelegate
AmazonSignatureException
AmazonServiceException
AmazonLogger
AmazonClientException
AmazonAuthUtils
AmazonSDKUtil

Checking out the file in a text editor shows lots of interesting sounding methods like initWithAccessKey:withSecretKey:, which would yield the required keys right there and then. The problem is attaching gdb before the app can call this method, and unfortunately, just executing the binary doesn't launch the app like it does under OS X. Thanks Obama.

Finding a place where the secret key is passed as an argument, or available as an instance variable sounds like an easier place to slide in and intercept the key. Trawling through the list of methods on AmazonS3Client reveals a signS3Request: method, and reading the code for it lands us on a promising looking class method, +[AmazonAUthUtils HMACSign:withKey:usingAlgorithm:]. Setting a breakpoint on the method name itself won't work due to the lack of symbols.

(gdb) break +[AmazonAUthUtils HMACSign:withKey:usingAlgorithm:]
Function "+[AmazonAUthUtils HMACSign:withKey:usingAlgorithm:]" not defined.
Make breakpoint pending on future shared library load? (y or [n])

Thankfully, that rickety chunk of C has provided us with a bounty of addresses to use instead.

$ grep HMACSign objc_rt_class_dump.txt
    HMACSign:withKey:usingAlgorithm:  0x1532b9

Setting a breakpoint at this address and recording a Vine leads to an exciting pause in program execution, rather than the terrifying kind.

(gdb) break *0x1532b9
Breakpoint 1 at 0x1532b9
(gdb) c
Continuing.
Reading symbols for shared libraries . done

Breakpoint 1, 0x001532b8 in ?? ()
(gdb)

I am. In a world. Of shit.

This is kind of another small world of shit. If debugging symbols were available then there'd be smooth seas ahead, but there's just one more tricky little bit of navigation involved. gdb's info args is pretty useless, so another way of accessing the function arguments is required. The standard ARM calling convention states that registers r0 through r3 are used for holding argument values. A peek at the internal mechanics of objc_msgSend shows that the implementation of a method is called with at least two arguments, a pointer to the object that is self, and the selector of the message that was sent. Breaking that down into the arguments that should be given to the implementation, one may expect:

  1. r0 to hold a pointer to self, AmazonAuthUtils in this case.
  2. r1 to contain a SEL representing HMACSign:withKey:usingAlgorithm:.
  3. r2 to reference the NSData passed as the value to HMACSign:.
  4. r3 to reference the NSString containing exactly what we want, the NSString holding the AWS secret key, the argument to withKey:.

And thusly.

(gdb) info args
No symbol table info available.
(gdb) po $r0
AmazonAuthUtils
(gdb) po $r3
ISw3ArT0g0DthEREw4S4rEALk3yHER3but1mNOTM4KInGTh1sTH4T3asY
(gdb)

I haven't printed the actual key here because there's no sign out on top of this site that says “AWS Credential Storage”. If you're that interested, then follow along at home. The work for this post is done, so feel free to exit gdb in an orderly fashion.

In part 3 of this series, I'll tie it all together. Now you too can have yet another place to assault innocent phone-holders with tired old animated gifs.

Update: Check out part 3.