How Facebook-Research app works
Last night I saw this tweet from Will Strafach:
After reading that article I just needed to reverse this iOS app and see how it worked, this is what I've found so far:
Downloading the app:
As the article mentions, you can download it from 3rd parties like Applause and BetaBound, but after signing up and downloading they both open iTunes with the manifest located at hxxps://r.facebook-program.com/ios/stable/manifest.plist
and this is its content:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>
<![CDATA[https://r.facebook-program.com/ios/stable/package.ipa]]>
</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>com.facebook.research.internalDevelopment</string>
<key>bundle-version</key>
<string>121335361</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>Facebook Research</string>
</dict>
</dict>
</array>
</dict>
</plist>
As you can see they are using com.facebook.research.internalDevelopment
as their Bundle Identifier. internalDevelopment
πand the software-package
(a.k.a. the app) is located at hxxps://r.facebook-program.com/ios/stable/package.ipa
(SHA256: 96b282e39e465d57eba440c53b73f442bc4ce6fc10a4445bdd90a412f87c42bd
) although, like Will said, it probably won't be there for long.
Static Analysis:
The actuall app is called PowerLogs
and uses this embedded.mobileprovision
:
As you can see it belongs to Facebook Inc (In-House)
.
Then I opened the Info.plist
file and found these two values (remember FacebookClientToken
, it will be important later):
FacebookAppID: 166732727149960
FacebookClientToken: 2d49f15626c6430ec7cbdf77f9a977cc
After this, I loaded the binary in Hopper and started browsing through some classes/methods, wanted to find where was the app connecting to; that's when I found a class called PLServerApis
and its initialization method in pseudo-C looks like this:
-(void *)init {
r7 = (sp - 0x14) + 0xc;
sp = sp - 0x30;
r1 = @selector(init);
asm { strd r0, r2, [sp, #0x28 + var_20] };
r4 = objc_msgSendSuper2(sp + 0x8, r1);
if (r4 != 0x0) {
[NSURL URLWithString:@"https://graph.onavo.com"];
r10 = _objc_retainAutoreleasedReturnValue;
r5 = (r10)();
var_24 = r5;
r0 = [AFHTTPSessionManager alloc];
r0 = [r0 initWithBaseURL:r5];
r11 = *ivar_offset(_client);
r8 = _objc_release;
r1 = *(r4 + r11);
*(r4 + r11) = r0;
(r8)(r1, r1, 0x144098);
var_28 = *(r4 + r11);
[AFJSONResponseSerializer serializer];
r5 = (r10)();
[var_28 setResponseSerializer:r5];
(r8)(r5);
r5 = *(r4 + r11);
[AFJSONRequestSerializer serializer];
r6 = (r10)();
[r5 setRequestSerializer:r6];
(r8)(r6);
(r8)(var_24);
}
r0 = r4;
return r0;
}
The API endpoints use the Onavo
host hxxps://graph.onavo.com
, as Will mentioned, this "research" app looks like it's just an Onavo wrapper.
Also I think since this is a research app, the developers really like to hard-code values like this access_token
(ON|255936594766932|c0a691bb6a59bb36ee4cc35e5d0f92f1
):
-(void)registerWithInvitationCode:(void *)arg2 success:(void *)arg3 failure:(void *)arg4 {
r3 = arg3;
r1 = _cmd;
var_DC = self;
var_1C = *___stack_chk_guard;
var_EC = _objc_retain;
r10 = [arg2 retain];
var_E8 = [r3 retain];
var_E0 = [arg4 retain];
r4 = &@class(BaseUtils);
r0 = *r4;
r0 = [r0 generateSecureRandomAlphaNumericStringOfLength:0x18];
r8 = _objc_retainAutoreleasedReturnValue;
var_E4 = (r8)();
var_A4 = @"access_token";
var_60 = @"ON|255936594766932|c0a691bb6a59bb36ee4cc35e5d0f92f1";
var_A0 = @"brand";
var_9C = @"country";
var_5C = @"Apple";
r0 = *r4;
r0 = [r0 getIsoCoutryCode];
r7 = r7;
var_F0 = (r8)();
var_98 = @"device_identifier";
var_94 = @"imei_unique_identifier";
r0 = @class(UIDevice);
r1 = 0x129b28;
:
}
Another example is the ONVEventer
class (which is the class that handles most of the events withing the app) because it's initialized this way:
if ([*0x17810c initialized] == 0x0) {
r1 = @selector(initialized);
r8 = *0x17810c;
r5 = [objc_msgSend(@class(Utils), @selector(getFacebookAppId)) retain];
r0 = [r8 setAppId:r5];
r0 = [r5 release];
r8 = *0x17810c;
r5 = [objc_msgSend(@class(Utils), @selector(getFacebookClientToken)) retain];
r0 = [r8 setLoginSecret:r5];
r0 = [r5 release];
:
:
}
The getFacebookClientToken
method returns the FacebookClientToken
found in the Info.plist
π
(Update: phwd confirmed these tokens are not secrets)
The app also has a plugin called PacketTunnelResearch.appex
that contains a binary called PacketTunnelResearch
which I'm guessing is handling the actuall VPN functionality:
And based on the amount of onavo
references it's pretty clear Facebook was reusing Onavo
's code for this app:
Sadly I didn't install the app when before Apple revoked Facebook's Enterprise Certificate(s), so I tried using Cydia Impactor to install it, but in order to install apps with Network Extensions
we need a paid account:
Even with a paid account Impactor
can't sign the entitlements correctly:
This means I won't be able to dynamically check the app π
Apple responds
Photo by Glen Carrie on Unsplash