Пример #1
0
    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
Пример #2
0
    def do_detect(self):
        with open(pkg_resources.resource_filename(
                __name__, os.path.join('..', 'libs', 'tlds.txt')),
                  'r',
                  encoding='utf-8') as f:
            self.re_tlds = re.compile('^(?:%s)$' % '|'.join(
                re.escape(l.strip())
                for l in f if l and not l.startswith('#')),
                                      flags=re.IGNORECASE)

        with self.context.store() as store:
            for cl in store.query().consts(
                    InvocationPattern(
                        'const-string',
                        r'://|^/[{}$%a-zA-Z0-9_-]+(/[{}$%a-zA-Z0-9_-]+)+|^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+(:[0-9]+)?$'
                    )):
                for match in self.analyzed(cl.p[1].v):
                    for v in match['value']:
                        yield Issue(detector_id=self.option,
                                    confidence=IssueConfidence.FIRM,
                                    cvss3_vector=self.cvss,
                                    summary='detected %s' % match['type_'],
                                    info1=v,
                                    source=store.query().qualname_of(cl))
            for name, val in self.context.string_resources():
                for match in self.analyzed(val):
                    for v in match['value']:
                        yield Issue(detector_id=self.option,
                                    confidence=IssueConfidence.FIRM,
                                    cvss3_vector=self.cvss,
                                    summary='detected %s' % match['type_'],
                                    info1=v,
                                    source='R.string.%s' % name)
Пример #3
0
    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
Пример #4
0
    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))
Пример #5
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
Пример #6
0
 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))
Пример #7
0
    def do_detect_case2(self):
        # XXX: Crude detection
        def should_be_secret(store, k, val):
            return any(
                x in store.query().qualname_of(k).lower()
                for x in ['inapp', 'billing', 'iab', 'sku', 'store', 'key'])

        pat = '^MI[IG][0-9A-Za-z+/=-]{32,}AQAB'
        with self.context.store() as store:
            for cl in store.query().consts(
                    InvocationPattern('const-string', pat)):
                val = cl.p[1].v
                yield Issue(
                    detector_id=self.option,
                    cvss3_vector=self.cvss,
                    confidence={
                        True: IssueConfidence.FIRM,
                        False: IssueConfidence.TENTATIVE
                    }[should_be_secret(store, cl, val)],
                    summary='insecure cryptography: static keys (2)',
                    info1='"%(target_val)s" [%(target_val_len)d] (X.509)' %
                    dict(target_val=val, target_val_len=len(val)),
                    source=store.query().qualname_of(cl),
                    synopsis=
                    'Traces of X.509 certificates has been found the application binary.',
                    description='''\
Traces of X.509 certificates has been found in the application binary.  X.509 ceritificates describe public key materials.  Their notable uses include Google Play in-app billing identity.  If is hardcoded, attackers can extract or replace them.
''',
                    solution='''\
Use a device or installation specific information, or obfuscate them.  Especially, do not use the stock implementation of in-app billing logic.
''')
            for name, val in self.context.string_resources():
                if re.match(pat, val):
                    yield Issue(
                        detector_id=self.option,
                        cvss3_vector=self.cvss,
                        confidence=IssueConfidence.TENTATIVE,
                        summary='insecure cryptography: static keys (2)',
                        info1='"%(target_val)s" [%(target_val_len)d] (X.509)' %
                        dict(target_val=val, target_val_len=len(val)),
                        source='R.string.%s' % name,
                        synopsis=
                        'Traces of X.509 certificates has been found the application binary.',
                        description='''\
Traces of X.509 certificates has been found in the application binary.  X.509 ceritificates describe public key materials.  Their notable uses include Google Play in-app billing identity.  If is hardcoded, attackers can extract or replace them.
''',
                        solution='''\
Use a device or installation specific information, or obfuscate them.  Especially, do not use the stock implementation of in-app billing logic.
''')
Пример #8
0
 def do_detect(self):
     with self.context.store() as store:
         for op in store.query().invocations(
                 InvocationPattern(
                     'invoke-',
                     'Landroid/provider/Settings\$Secure;->getString\(Landroid/content/ContentResolver;Ljava/lang/String;\)Ljava/lang/String;|Landroid/telephony/TelephonyManager;->getDeviceId\(\)Ljava/lang/String;|Landroid/telephony/TelephonyManager;->getSubscriberId\(\)Ljava/lang/String;|Landroid/telephony/TelephonyManager;->getLine1Number\(\)Ljava/lang/String;|Landroid/bluetooth/BluetoothAdapter;->getAddress\(\)Ljava/lang/String;|Landroid/net/wifi/WifiInfo;->getMacAddress\(\)Ljava/lang/String;|Ljava/net/NetworkInterface;->getHardwareAddress\(\)'
                 )):
             val_type = self.analyzed(store, op)
             if val_type is not None:
                 yield Issue(detector_id=self.option,
                             confidence=IssueConfidence.CERTAIN,
                             cvss3_vector=self.cvss,
                             summary='privacy concerns',
                             info1='getting %s' % val_type,
                             source=store.query().qualname_of(op))
Пример #9
0
 def do_detect_plain_pins_hostnameverifier(self):
     with self.context.store() as store:
         pins = set()
         q = store.query()
         for m in itertools.chain(store.query().methods_in_class(
                 'verify(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z',
                 'HostnameVerifier')):
             if any(
                     q.matches_in_method(
                         m,
                         InvocationPattern(
                             'invoke',
                             'contains|equals|verify|Ljavax/net/ssl/SSLSession;->getPeerCertificates'
                         ))):
                 pins.add(q.class_name_of(q.class_of_method(m)))
         return pins
Пример #10
0
 def do_detect(self):
     with self.context.store() as store:
         for cl in store.query().consts(
                 InvocationPattern('const-string', r'%s')):
             for t in self.analyzed(cl.p[1].v):
                 yield Issue(detector_id=self.option,
                             confidence=t['confidence'],
                             cvss3_vector=self.cvss,
                             summary='detected format string',
                             info1=t['value'],
                             source=store.query().qualname_of(cl))
         for name, val in self.context.string_resources():
             for t in self.analyzed(val):
                 yield Issue(detector_id=self.option,
                             confidence=t['confidence'],
                             cvss3_vector=self.cvss,
                             summary='detected format string',
                             info1=t['value'],
                             source='R.string.%s' % name)
Пример #11
0
 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
Пример #12
0
    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
Пример #13
0
    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