def do_detect(self): with self.context.store() as store: for cl in store.query().invocations( InvocationPattern( 'invoke-', 'L.*->([dwie]|debug|error|exception|warning|info|notice|wtf)\(Ljava/lang/String;Ljava/lang/String;.*?Ljava/lang/(Throwable|.*?Exception);|L.*;->print(ln)?\(Ljava/lang/String;|LException;->printStackTrace\(' )): if 'print' not in cl.p[1].v: try: yield Issue( detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss, summary='detected logging', info1=cl.p[1].v, info2=DataFlows.solved_constant_data_in_invocation( store, cl, 1), source=store.query().qualname_of(cl)) except (DataFlows.NoSuchValueError): yield Issue(detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss, summary='detected logging', info1=cl.p[1].v, source=store.query().qualname_of(cl)) elif 'Exception;->' not in cl.p[1].v: try: yield Issue( detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss, summary='detected logging', info1=cl.p[1].v, info2=DataFlows.solved_constant_data_in_invocation( store, cl, 0), source=store.query().qualname_of(cl)) except (DataFlows.NoSuchValueError): yield Issue(detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss, summary='detected logging', info1=cl.p[1].v, source=store.query().qualname_of(cl)) else: yield Issue(detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss, summary='detected logging', info1=cl.p[1].v, source=store.query().qualname_of(cl))
def do_detect(self): with self.context.store() as store: for cl in store.query().invocations( InvocationPattern( 'invoke-static', 'Ljavax/crypto/Cipher;->getInstance\(Ljava/lang/String;.*?\)' )): try: target_val = DataFlows.solved_possible_constant_data_in_invocation( store, cl, 0) if any((('ECB' in x or '/' not in x) and 'RSA' not in x) for x in target_val): yield Issue( detector_id=self.option, cvss3_vector=self.cvss, confidence=IssueConfidence.CERTAIN, summary= 'insecure cryptography: cipher might be operating in ECB mode', info1=','.join(target_val), source=store.query().qualname_of(cl), synopsis= 'The application might be using ciphers in ECB mode.', description='''\ The application might be using symmetric ciphers in ECB mode. ECB mode is the most basic operating mode that independently transform data blocks. Indepent transformation leaks information about distribution in plaintext. ''', solution='''\ Use CBC or CTR mode. ''') except (DataFlows.NoSuchValueError): pass
def do_detect_plain_pins_x509(self): with self.context.store() as store: pins = set() q = store.query() for m in store.query().methods_in_class('checkServerTrusted', 'X509TrustManager'): if any(q.matches_in_method(m, InvocationPattern('verify', ''))): pins.add(q.class_name_of(q.class_of_method(m))) if any(q.matches_in_method(m, InvocationPattern('throw', ''))): pins.add(q.class_name_of(q.class_of_method(m))) if pins: # XXX crude detection custom_sslcontext_detected = False for cl in self.context.store().query().invocations( InvocationPattern('invoke-virtual', 'Ljavax/net/ssl/SSLContext;->init')): custom_sslcontext_detected = True pins = DataFlows.solved_typeset_in_invocation( store, cl, 1) & pins if not custom_sslcontext_detected: return set() else: return pins else: return pins
def analyzed(self, store, op): x = op.p[1].v if re.search( 'Landroid/provider/Settings\$Secure;->getString\(Landroid/content/ContentResolver;Ljava/lang/String;\)Ljava/lang/String;', x): try: if DataFlows.solved_constant_data_in_invocation( store, op, 1) == 'android_id': return 'ANDROID_ID' else: return None except DataFlows.NoSuchValueError: return None elif re.search( 'Landroid/telephony/TelephonyManager;->getDeviceId\(\)Ljava/lang/String;', x): return 'IMEI' elif re.search( 'Landroid/telephony/TelephonyManager;->getSubscriberId\(\)Ljava/lang/String;', x): return 'IMSI' elif re.search( 'Landroid/telephony/TelephonyManager;->getLine1Number\(\)Ljava/lang/String;', x): return 'phone number' elif re.search( 'Landroid/bluetooth/BluetoothAdapter;->getAddress\(\)Ljava/lang/String;', x): return 'L2 address (Bluetooth)' elif re.search( 'Landroid/net/wifi/WifiInfo;->getMacAddress\(\)Ljava/lang/String;|Ljava/net/NetworkInterface;->getHardwareAddress\(\)', x): return 'L2 address (Wi-Fi)'
def important_args_on_invocation(self, k): method_name = k.p[1].v if re.match( 'L.*/(SecretKey|(Iv|GCM)Parameter|(PKCS8|X509)EncodedKey)Spec-><init>|L.*/MessageDigest;->update', method_name): yield 0 else: yield from range(len(DataFlows.decoded_registers_of(k.p[0])))
def do_detect(self): with self.context.store() as store: targets = {'WebView', 'XWalkView', 'GeckoView'} more = True while more: more = False for cl in store.query().related_classes('|'.join(targets)): name = store.query().class_name_of(cl) if name not in targets: targets.add(name) more = True for fn in (n for n in self.context.disassembled_resources() if 'layout' in n): with open(fn, 'rb') as f: r = ET.parse(f, parser=ET.XMLParser(recover=True)).getroot() for t in functools.reduce( lambda x, y: x + y, (r.xpath('//%s' % self.context.class_name_of_dalvik_class_type( c).replace('$', '_')) for c in targets)): size = LayoutSizeGuesser().guessed_size(t, fn) if size > 0.5: yield Issue( detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss1, summary='tamperable webview', info1='{0} (score: {1:.02f})'.format( t.attrib['{0}id'.format( self.xmlns_android)], size), source=self.context. source_name_of_disassembled_resource(fn)) # XXX: crude detection for op in store.query().invocations( InvocationPattern('invoke-', ';->loadUrl')): try: v = DataFlows.solved_constant_data_in_invocation( store, op, 0) if v.startswith('http://'): yield Issue(detector_id=self.option, confidence=IssueConfidence.FIRM, cvss3_vector=self.cvss2, summary='tamperable webview with URL', info1=v, source=store.query().qualname_of(op)) except DataFlows.NoSuchValueError: pass
def do_detect(self): with self.context.store() as store: for op in store.query().invocations( InvocationPattern( 'invoke-', 'Landroid/net/Uri;->parse\(Ljava/lang/String;\)Landroid/net/Uri;' )): try: if DataFlows.solved_constant_data_in_invocation( store, op, 0).startswith('content://sms/'): yield Issue(detector_id=self.option, confidence=IssueConfidence.CERTAIN, cvss3_vector=self.cvss, summary='privacy concerns', info1='accessing SMS', source=store.query().qualname_of(op)) except DataFlows.NoSuchValueError: pass for op in store.query().invocations( InvocationPattern('invoke-', 'Landroid/telephony/SmsManager;->send')): yield Issue(detector_id=self.option, confidence=IssueConfidence.CERTAIN, cvss3_vector=self.cvss, summary='privacy concerns', info1='sending SMS', source=store.query().qualname_of(op)) for op in store.query().invocations( InvocationPattern( 'invoke-', 'Landroid/telephony/SmsMessage;->createFromPdu\(')): yield Issue(detector_id=self.option, confidence=IssueConfidence.FIRM, cvss3_vector=self.cvss, summary='privacy concerns', info1='intercepting incoming SMS', source=store.query().qualname_of(op))
def do_detect(self): with self.context.store() as store: for cl in store.query().invocations( InvocationPattern( 'invoke-virtual', 'Landroid/content/Context;->openFileOutput\(Ljava/lang/String;I\)' )): try: target_val = int( DataFlows.solved_constant_data_in_invocation( store, cl, 1), 16) if target_val & 3: yield Issue(detector_id=self.option, confidence=IssueConfidence.CERTAIN, cvss3_vector=self.cvss, summary='insecure file permission', info1={ 1: 'MODE_WORLD_READABLE', 2: 'MODE_WORLD_WRITABLE' }[target_val], source=store.query().qualname_of(cl)) except (DataFlows.NoSuchValueError): pass
def do_detect_case1(self): def looks_like_real_key(k): # XXX: silly return len(k) >= 8 and not any( x in k for x in ('Padding', 'SHA1', 'PBKDF2', 'Hmac', 'emulator')) with self.context.store() as store: for cl in store.query().invocations( InvocationPattern( 'invoke-', '^Ljavax?.*/(SecretKey|(Iv|GCM)Parameter|(PKCS8|X509)EncodedKey)Spec|^Ljavax?.*/MessageDigest;->(update|digest)' )): try: for nr in self.important_args_on_invocation(cl): for found in DataFlows.solved_possible_constant_data_in_invocation( store, cl, nr): try: decoded = base64.b64decode(found) info1 = '"%(target_val)s" [%(target_val_len)d] (base64; "%(decoded_val)s" [%(decoded_val_len)d])' % dict( target_val=found, target_val_len=len(found), decoded_val=binascii.hexlify( decoded).decode('ascii'), decoded_val_len=len(decoded)) except (ValueError, binascii.Error): info1 = '"%(target_val)s" [%(target_val_len)d]' % dict( target_val=found, target_val_len=len(found)) if looks_like_real_key(found): yield Issue( detector_id=self.option, cvss3_vector=self.cvss, confidence=IssueConfidence.FIRM, summary= 'insecure cryptography: static keys', info1=info1, source=store.query().qualname_of(cl), synopsis= 'Traces of cryptographic material has been found the application binary.', description='''\ Traces of cryptographic material has been found in the application binary. If cryptographic material is hardcoded, attackers can extract or replace them. ''', solution='''\ Use a device or installation specific information, or obfuscate them. ''') else: yield Issue( detector_id=self.option, cvss3_vector=self.cvss_nonkey, confidence=IssueConfidence.TENTATIVE, summary='Cryptographic constants detected', info1=info1, source=store.query().qualname_of(cl), synopsis= 'Possible cryptographic constants have been found.', description='''\ Possible cryptographic constants has been found in the application binary. ''') except IndexError: pass
def do_detect(self): with self.context.store() as store: targets = set() seeds = {'WebView', 'XWalkView', 'GeckoView'} more = True while more: more = False for cl in store.query().related_classes('|'.join(seeds)): name = store.query().class_name_of(cl) if name not in targets: targets.add(name) more = True for seed in seeds: targets.add('L.*%s;' % seed) # XXX: Crude detection for p in store.query().invocations( InvocationPattern( 'invoke-virtual', 'Landroid/webkit/WebSettings;->setJavaScriptEnabled')): try: if DataFlows.solved_constant_data_in_invocation( store, p, 0): for target in targets: for q in store.query().invocations_in_class( p, InvocationPattern( 'invoke-virtual', '%s->addJavascriptInterface' % target)): try: if DataFlows.solved_constant_data_in_invocation( store, q, 0): yield Issue( detector_id=self.option, confidence=IssueConfidence.FIRM, cvss3_vector=self.cvss, summary= 'insecure Javascript interface', source=store.query().qualname_of( q)) except (DataFlows.NoSuchValueError): yield Issue( detector_id=self.option, confidence=IssueConfidence.TENTATIVE, cvss3_vector=self.cvss, summary='insecure Javascript interface', source=store.query().qualname_of(q)) except (DataFlows.NoSuchValueError): pass for q in store.query().invocations_in_class( p, InvocationPattern( 'invoke-virtual', 'Landroid/webkit/WebSettings;->setMixedContentMode' )): try: val = int( DataFlows.solved_constant_data_in_invocation( store, q, 0), 16) if val == 0: yield Issue(detector_id=self.option, confidence=IssueConfidence.FIRM, cvss3_vector=self.cvss, summary='insecure mixed content mode', info1='MIXED_CONTENT_ALWAYS_ALLOW', source=store.query().qualname_of(q)) except (DataFlows.NoSuchValueError): pass