Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android FCM scanner #409

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions agent/src/android/scanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { getNSMainBundle } from "../ios/lib/helpers";
import { wrapJavaPerform, getApplicationContext } from "./lib/libjava";
import { ArrayList } from "./lib/types";

export namespace scanner {
export const getfbdatabase = (): Promise<any> => {
return wrapJavaPerform(() => {
// -- Sample Java
//
// String dburl = (String)getString(R.string.firebase_database_url);
const context = getApplicationContext();
const myid = context.getResources().getIdentifier("firebase_database_url", "string", context.getPackageName());
const dburl = context.getString(myid);
return dburl;
});
}
export const getapikeys = (): Promise<string[]> => {
return wrapJavaPerform(() => {
const keynames = [
"google_maps_geocoder_key",
"notification_server_key",
"server_key",
"com.google.android.geo.API_KEY",
"com.google.android.maps.v2.API_KEY",
"googlePlacesWebApi",
"google_crash_reporting_api_key",
"google_api_key"
];
const context = getApplicationContext();
var keys : string[] = new Array;
var count = 0;
for (var i = 0; i < keynames.length; i++) {
try {
var key = context.getResources().getIdentifier(keynames[i], "string", context.getPackageName());
keys[count] = context.getString(key);
count++;
} catch (error) {

}
}
return keys;
});
}
}
5 changes: 5 additions & 0 deletions agent/src/rpc/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { sslpinning } from "../android/pinning";
import { root } from "../android/root";
import { androidshell } from "../android/shell";
import { userinterface } from "../android/userinterface";
import { scanner } from "../android/scanner";

export const android = {
// android clipboard
Expand Down Expand Up @@ -67,6 +68,10 @@ export const android = {
androidRootDetectionDisable: () => root.disable(),
androidRootDetectionEnable: () => root.enable(),

// android scanner declarations
androidGetFbDatabase: () => scanner.getfbdatabase(),
androidGetApiKeys: () => scanner.getapikeys(),

// android user interface
androidUiScreenshot: () => userinterface.screenshot(),
androidUiSetFlagSecure: (v: boolean): Promise<void> => userinterface.setFlagSecure(v),
Expand Down
82 changes: 82 additions & 0 deletions objection/commands/android/scanner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import click
import requests
import re

from objection.state.connection import state_connection

def firebase(args: list) -> None:
"""
Search for a Firebase Database and check if it's leaking data.

:param args:
:return:
"""
api = state_connection.get_api()
try:
fbdb = api.android_get_fb_database()
click.secho('Scanning FireBase DB: {0}'.format(fbdb), fg='green')
click.secho('Note: If the DB is exposed, it may take a while to download', fg='red')
response = requests.get(fbdb + '/.json')
if response.status_code == 401:
click.secho('Firebase DB is not leaking data', fg='green')
elif response.status_code == 200:
click.secho('Firebase DB is leaking data!', fg='red')
if len(response.text) < 1000:
click.secho('Size: {:,.0f}'.format(len(response.text)) + "B", fg='red')
elif len(response.text) >= 1000 and len(response.text) < 1000000:
click.secho('Size: {:,.0f}'.format(len(response.text)/float(1<<10)) + "KB", fg='red')
elif len(response.text) >= 1000000:
click.secho('Size: {:,.0f}'.format(len(response.text)/float(1<<20)) + "MB", fg='red')
else:
click.secho('Something weird happened. Please report the issue.', fg='red')
except:
click.secho('Application doesn''t make use of FireBase', fg='red')

def apikeys(args: list) -> None:
"""
Search for Firebase Cloud Messaging API Keys.
Ref: https://abss.me/posts/fcm-takeover/

:param args:
:return:
"""
api = state_connection.get_api()
output = []
output = api.android_get_api_keys()

# Firebase Cloud Messaging Web API Key
pattern = r'AIzaSy[0-9A-Za-z_-]{33}'
# Firebase Cloud Messaging Server Key
pattern2 = r'AAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140}'

data = '{"registration_ids":["ABC"]}'

for x in output:
if re.search(pattern2, x):
# Now lets create the request to validate the keys
# If the keys validate, they are server keys and can be used to
# send messages
headers = {
'Authorization': 'key={0}'.format(x),
'Content-Type': 'application/json',
}
response = requests.post('https://fcm.googleapis.com/fcm/send', headers=headers, data=data)
if response.status_code == 200:
click.secho('FCM Server Key: {0}'.format(x) + ' - [VALID]', fg='green')
elif response.status_code == 401:
click.secho('FCM Server Key: {0}'.format(x) + ' - [INVALID]', fg='red')
if re.search(pattern, x):
# Now lets create the request to validate the keys
# If the keys validate, they are server keys and can be used to
# send messages
headers = {
'Authorization': 'key={0}'.format(x),
'Content-Type': 'application/json',
}
response = requests.post('https://fcm.googleapis.com/fcm/send', headers=headers, data=data)
if response.status_code == 200:
click.secho('Legacy FCM Server Key: {0}'.format(x) + ' - [VALID]', fg='green')
elif response.status_code == 401:
click.secho('Web API Key: {0}'.format(x) + ' - [Nothing to do here]', fg='red')


14 changes: 14 additions & 0 deletions objection/console/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from ..commands.android import keystore
from ..commands.android import pinning as android_pinning
from ..commands.android import root
from ..commands.android import scanner as android_scanner
from ..commands.ios import binary
from ..commands.ios import bundles
from ..commands.ios import cookies
Expand Down Expand Up @@ -277,6 +278,19 @@
'meta': 'Execute a shell command',
'exec': command.execute
},
'scanner': {
'meta': 'Command used to invoke the scanner',
'commands': {
'firebase': {
'meta': 'Scan for leaking firebase DBs',
'exec': android_scanner.firebase
},
'apikeys' : {
'meta': 'Scan for Firebase Cloud Messaging Keys',
'exec': android_scanner.apikeys
}
},
},
'hooking': {
'meta': 'Commands used for hooking methods in Android',
'commands': {
Expand Down