This is how I reverse engineered the Instagram app + included linux binaries to locate the private key used for signing requests to their private API and therefore allowing access to uploading photos and fewer rate limits.
It posed as a challenge and I was also itnerested in working out how these 3rd party clients actually upload to Instagram’s servers.
What signing algorithm does Instagram use?
For all requests to their private API Instagram signs the post data with HMAC-SHA256 and passes this in the parameter signed_body using the format signed_body=signed.postdata
for example signed_body=nc8e1774526bf84b58bb4ffebb357bddb822a5183e0355db1effc2dad47107a29.{"_uuid":"00000000-0000-0000-0000-000000000000","password":"password","username":"will","device_id":"00000000-0000-0000-0000-000000000000","_csrftoken":"missing"}
Getting the key for the HMAC-SHA256 hash
- Pull the Instagram apk off your device (make sure USB debugging is enabled in Settings->Developer)
- Run
adb shell pm path com.instagram.android
to get the path of the Instagram app. - Run
adb pull PATH_FROM_ABOVE
to pull the apk.
- Run
- Decompile the apk using apktool:
./apktool d com.instagram.android.apk
- Navigate to the decompiled smali code directory
cd com.instagram.android/smali
- Find the file that makes requests
grep -r "RequestUtil.java" .
- Open that file
nano FILE_LOCATION_RETURNED_ABOVE
- Navigate to the lien starting with
.method public static b(Ljava/lang/String;)Ljava/lang/String;
- Change the line below from
.locals 5
to.locals 6
- Two lines below
.line 70
insert the following code to log the key (comign from variable v0 which is returned from thegetInstagramString
function in the code above):const-string v5, «LOGGING» invoke-static {v5, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I - Save the file and exit nano
- Navigate back to the apktool directory
cd ../..
- Build the apk
./apktool b com.instagram.android
- Navigate to the built apk location
cd com.instagram.android/dist/
- Create a signing key
keytool -genkey -v -keystore android.keystore
- Sign the apk
jarsigner -verbose -keystore android.keystore com.instagram.android.apk mykey
- Verify the apk
zipalign -f -v 4 com.instagram.android.apk instagram.apk
- Push the apk to your device’s sdcard
adb -d push instagram.apk /sdcard/instagram.apk
- Install the apk on your phone.
- Start logcat on your computer
adb -d logcat | grep "LOGGING"
- Open the instagram app, login and the private key will be returned.
Making requests
We can create a simple Ruby app to simulate a login:
require ‘openssl‘ | |
require ‘base64‘ | |
require ‘json‘ | |
require ‘httpclient‘ | |
http = HTTPClient.new(:agent_name => useragent) | |
key = «« #The Private key | |
login_info = {:guid => «00000000-0000-0000-0000-000000000000«, | |
:password => «PASSWORD«, | |
:username => «USERNAME«, | |
:device_id => «android-0000000000000000«, | |
:_csrftoken => «missing« | |
}.to_json | |
signed_body = «#{Digest::HMAC.hexdigest(login_info, key, Digest::SHA256)}.#{login_info}« | |
post_data = {:signed_body => signed_body, :ig_sig_key_version => 4} | |
result = http.post(«https://instagram.com/api/v1/accounts/login/«, post_data, «Content-Type« => «application/json«) | |
p result.body |
Hopefully the login will be successful, you can then request other locations in the private API (sniff the Instagram app to find these endpoints and the required post data.)