mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-12 02:06:59 +00:00
iOS Swift, OWASP MASVS/MSTG Support (#1302)
* iOS Swift support * Improved rules for Swift and Objective C * OWASP MSTG support - https://mobile-security.gitbook.io/masvs/security-requirements/ * Changes to Code Matcher * Add Status to iOS Permissions * Standardize IPA binary analysis rules * Refactoring SAST Code * Refactor Classdump * Updated Readme Thanks to @karolpiateknet (Netguru S. A team)
This commit is contained in:
@@ -15,8 +15,8 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
[features requests](#features) and [submitting pull
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests (use
|
||||
[Stack Overflow](http://stackoverflow.com)).
|
||||
* Please **do not** use the issue tracker for personal support requests (use [MobSF Slack channel](https://mobsf.slack.com/join/shared_invite/enQtNzM2NTAyNzA1MjgxLTdjMzkzNDc3ZjdiMjkwZTZhMmFhNDlkZmMwZDhjNDNmYTAzYWE5NGZlMDIzYzliNTdiMDQ2MTRlYjU1MjkyNGM) or
|
||||
[Stack Overflow](https://stackoverflow.com/search?q=mobsf)).
|
||||
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
@@ -39,7 +39,7 @@ Guidelines for bug reports:
|
||||
3. **Isolate the problem** — create a [reduced test
|
||||
case](http://css-tricks.com/reduced-test-cases/) and a live example.
|
||||
|
||||
4. **Add Log files** — Please add the log files `logs/MobSF.log` and `logs/webproxy.log` while opening bugs.
|
||||
4. **Add Log file** — Please add the log file `logs/debug.log` while opening bugs.
|
||||
|
||||
5. **Timely Response** — Once you open a bug, you should also provide additional information if requested. Failure to do so in 25 days will result in closure of the bug without further communication.
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
|
||||
MOBSF_VER = 'v3.0.4 Beta'
|
||||
MOBSF_VER = 'v3.0.5 Beta'
|
||||
|
||||
BANNER = """
|
||||
__ __ _ ____ _____ _____ ___
|
||||
|
||||
@@ -13,89 +13,94 @@ Made with ](https://sonarcloud.io/dashboard?id=MobSF_Mobile-Security-Framework-MobSF)
|
||||
[](https://travis-ci.com/MobSF/Mobile-Security-Framework-MobSF)
|
||||
[](https://pyup.io/repos/github/MobSF/Mobile-Security-Framework-MobSF/)
|
||||
[](http://www.toolswatch.org/2018/01/black-hat-arsenal-top-10-security-tools/)
|
||||
[](http://www.toolswatch.org/2017/02/2016-top-security-tools-as-voted-by-toolswatch-org-readers/)
|
||||
[](https://www.blackhat.com/asia-18/arsenal.html#mobile-security-framework-mobsf)
|
||||
[](http://www.toolswatch.org/2018/01/black-hat-arsenal-top-10-security-tools/)
|
||||
[](https://www.blackhat.com/asia-15/arsenal.html#yso-mobile-security-framework)
|
||||
[](https://www.blackhat.com/asia-18/arsenal.html#mobile-security-framework-mobsf)
|
||||
|
||||
|
||||
MobSF is also bundled with [Android Tamer](https://androidtamer.com/tamer4-release) and [BlackArch](https://blackarch.org/mobile.html)
|
||||
|
||||
## Buy us a Coffee!
|
||||
## Support MobSF
|
||||
|
||||
*Donate via Paypal:* [](https://mobsf.github.io/Mobile-Security-Framework-MobSF/paypal.html) *Send Bitcoins:* [](https://mobsf.github.io/Mobile-Security-Framework-MobSF/donate.html)
|
||||
**Donate via Paypal:** [](https://paypal.me/ajinabraham)
|
||||
|
||||
**Send Bitcoins:** [](https://mobsf.github.io/Mobile-Security-Framework-MobSF/donate.html)
|
||||
|
||||
## Documentation
|
||||
[](https://github.com/MobSF/Mobile-Security-Framework-MobSF/wiki/1.-Documentation)
|
||||
[](https://mobsf.github.io/docs)
|
||||
|
||||
## Try MobSF Static Analyzer Online
|
||||
[](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/MobSF/Mobile-Security-Framework-MobSF/master/scripts/stack/docker-compose.yml)
|
||||
* Try MobSF Static Analyzer Online:
|
||||
[](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/MobSF/Mobile-Security-Framework-MobSF/master/scripts/stack/docker-compose.yml)
|
||||
* Conference Presentations: [Slides & Videos](https://mobsf.github.io/Mobile-Security-Framework-MobSF/presentations.html)
|
||||
* MobSF Online Course: [OpSecX MAS](https://opsecx.com/index.php/product/automated-mobile-application-security-assessment-with-mobsf/)
|
||||
* What's New: [See Changelog](https://mobsf.github.io/Mobile-Security-Framework-MobSF/changelog.html)
|
||||
|
||||
## Collaborators
|
||||
|
||||
[Ajin Abraham](https://in.linkedin.com/in/ajinabraham)  | [Dominik Schlecht](https://github.com/DominikSchlecht)  | [Magaofei](https://github.com/magaofei)  | [Matan Dobrushin](https://github.com/matandobr)  | [Vincent Nadal](https://github.com/superpoussin22) 
|
||||
[Ajin Abraham](https://in.linkedin.com/in/ajinabraham)  | [Dominik Schlecht](https://github.com/sn0b4ll)  | [Magaofei](https://github.com/magaofei)  | [Matan Dobrushin](https://github.com/matandobr)  | [Vincent Nadal](https://github.com/superpoussin22) 
|
||||
|
||||
## e-Learning Courses & Certifications
|
||||
* [Automated Mobile Application Security Assessment with MobSF -MAS](https://opsecx.com/index.php/product/automated-mobile-application-security-assessment-with-mobsf/)
|
||||
* [Android Security Tools Expert -ATX](https://opsecx.com/index.php/product/android-security-tools-expert-atx/)
|
||||
 [Automated Mobile Application Security Assessment with MobSF -MAS](https://opsecx.com/index.php/product/automated-mobile-application-security-assessment-with-mobsf/)
|
||||
|
||||
## MobSF Support Packages
|
||||
* For free limited support, use our Slack Channel: [mobsf.slack.com](https://mobsf.slack.com/join/shared_invite/enQtNzM2NTAyNzA1MjgxLTdjMzkzNDc3ZjdiMjkwZTZhMmFhNDlkZmMwZDhjNDNmYTAzYWE5NGZlMDIzYzliNTdiMDQ2MTRlYjU1MjkyNGM)
|
||||
* For enterprise support, priority feature requests and live training, see [MobSF Support Packages](https://mobsf.github.io/Mobile-Security-Framework-MobSF/support.html)
|
||||
 [Android Security Tools Expert -ATX](https://opsecx.com/index.php/product/android-security-tools-expert-atx/)
|
||||
|
||||
## Presentations
|
||||
* OWASP APPSEC EU 2016 - [Slides](http://www.slideshare.net/ajin25/automated-mobile-application-security-assessment-with-mobsf), [Video](https://www.youtube.com/watch?v=h00v1euuFXg)
|
||||
* NULLCON 2016 - [Slides](https://www.slideshare.net/ajin25/nullcon-goa-2016-automated-mobile-application-security-testing-with-mobile-security-framework-mobsf)
|
||||
* c0c0n 2015 - [Slides](https://www.slideshare.net/ajin25/automated-security-analysis-of-android-ios-applications-with-mobile-security-framework-c0c0n-2015)
|
||||
* G4H Webcast 2015 - [Video](https://www.youtube.com/watch?v=CysfO6AZmo8)
|
||||
## MobSF Support
|
||||
|
||||
* **Free Support:** For free limited support, questions and help, join our Slack channel  [mobsf.slack.com](https://mobsf.slack.com/join/shared_invite/enQtNzM2NTAyNzA1MjgxLTdjMzkzNDc3ZjdiMjkwZTZhMmFhNDlkZmMwZDhjNDNmYTAzYWE5NGZlMDIzYzliNTdiMDQ2MTRlYjU1MjkyNGM)
|
||||
* **Enterprise Support:** For enterprise support, priority feature requests and live training, see [MobSF Support Packages](https://mobsf.github.io/Mobile-Security-Framework-MobSF/support.html)
|
||||
|
||||
## What's New?
|
||||
* [See Changelog](https://mobsf.github.io/Mobile-Security-Framework-MobSF/changelog.html)
|
||||
|
||||
## Contribution, Feature Requests & Bugs
|
||||
|
||||
* Read [CONTRIBUTING.md](https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/master/.github/CONTRIBUTING.md) before opening bugs, feature requests and pull request.
|
||||
* Feature Requests: [@ajinabraham](https://twitter.com/ajinabraham) or [@OpenSecurity_IN](https://twitter.com/OpenSecurity_IN).
|
||||
* For discussions, questions and limited support, use our Slack Channel mobsf.slack.com: [Join MobSF Channel](https://mobsf.slack.com/join/shared_invite/enQtNzM2NTAyNzA1MjgxLTdjMzkzNDc3ZjdiMjkwZTZhMmFhNDlkZmMwZDhjNDNmYTAzYWE5NGZlMDIzYzliNTdiMDQ2MTRlYjU1MjkyNGM)
|
||||
* Open Bugs after reading [Guidelines to Report a Bug](https://github.com/MobSF/Mobile-Security-Framework-MobSF/blob/master/.github/CONTRIBUTING.md#using-the-issue-tracker)
|
||||
* For Project updated and announcements, follow [@ajinabraham](https://twitter.com/ajinabraham) or [@OpenSecurity_IN](https://twitter.com/OpenSecurity_IN).
|
||||
* Github Issues are only for tracking bugs and feature requests. Do not post support or help queries there. We have a slack channel for that.
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
### Static Analysis - Android
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### Static Analysis - iOS
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### Dynamic Analysis - Android APK
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### Web API Viewer
|
||||
|
||||

|
||||
|
||||
|
||||
## Credits
|
||||
## Honorable Contributors
|
||||
|
||||
* Amrutha VC - For the new MobSF logo
|
||||
* Dominik Schlecht - For the awesome work on adding Windows Phone App Static Analysis to MobSF
|
||||
* Esteban - Better Android Manifest Analysis and Static Analysis Improvement.
|
||||
* Matan Dobrushin - For adding Android ARM Emulator support to MobSF - Special thanks goes for cuckoo-droid
|
||||
* Shuxin - Android Binary Analysis
|
||||
* Abhinav Saxena - (@xandfury) - For Travis CI and Logging integration
|
||||
*  [Netguru](https://www.netguru.com/) (@karolpiateknet, @mtbrzeski) - For iOS Swift support, Rule contributions and SAST refactoring.
|
||||
|
||||
## Shoutouts
|
||||
|
||||
* Abhinav Sejpal (@Abhinav_Sejpal) - For poking me with bugs, feature requests, and UI & UX suggestions.
|
||||
* Amrutha VC (@amruthavc) - For the new MobSF logo
|
||||
* Anant Srivastava (@anantshri) - For Activity Tester Idea
|
||||
* Anto Joseph (@antojosep007) - For the help with SuperSU.
|
||||
* Bharadwaj Machiraju (@tunnelshade_) - For writing pyWebProxy from scratch
|
||||
* Dominik Schlecht - For the awesome work on adding Windows Phone App Static Analysis to MobSF
|
||||
* Esteban - Better Android Manifest Analysis and Static Analysis Improvement.
|
||||
* Matan Dobrushin - For adding Android ARM Emulator support to MobSF - Special thanks goes for cuckoo-droid, I got inspired by their code and idea for this implementation.
|
||||
* MindMac - For writing Android Blue Pill
|
||||
* Rahul (@c0dist) - Kali Support
|
||||
* Shuxin - Android Binary Analysis
|
||||
* MindMac - For writing Android Blue Pill
|
||||
* Oscar Alfonso Diaz - (@OscarAkaElvis) - For Dockerfile contributions
|
||||
* Thomas Abraham - For JS Hacks on UI.
|
||||
* Tim Brown (@timb_machine) - For the iOS Binary Analysis Ruleset.
|
||||
* Oscar Alfonso Diaz - (@OscarAkaElvis) - For Dockerfile contributions
|
||||
* Abhinav Saxena - (@xandfury) - For Travis CI and Logging integration
|
||||
|
||||
Binary file not shown.
+10
-2
@@ -57,6 +57,7 @@ def static_analysis_test():
|
||||
'/PDF/?md5=52c50ae824e329ba8b5b7a0f523efffe',
|
||||
'/PDF/?md5=57bb5be0ea44a755ada4a93885c3825e',
|
||||
'/PDF/?md5=8179b557433835827a70510584f3143e',
|
||||
'/PDF/?md5=7b0a23bffc80bac05739ea1af898daad',
|
||||
]
|
||||
else:
|
||||
pdfs = [
|
||||
@@ -64,6 +65,7 @@ def static_analysis_test():
|
||||
'/PDF/?md5=52c50ae824e329ba8b5b7a0f523efffe',
|
||||
'/PDF/?md5=57bb5be0ea44a755ada4a93885c3825e',
|
||||
'/PDF/?md5=8179b557433835827a70510584f3143e',
|
||||
'/PDF/?md5=7b0a23bffc80bac05739ea1af898daad',
|
||||
]
|
||||
|
||||
for pdf in pdfs:
|
||||
@@ -97,12 +99,14 @@ def static_analysis_test():
|
||||
'6c23c2970551be15f32bbab0b5db0c71',
|
||||
'52c50ae824e329ba8b5b7a0f523efffe',
|
||||
'57bb5be0ea44a755ada4a93885c3825e',
|
||||
'8179b557433835827a70510584f3143e']
|
||||
'8179b557433835827a70510584f3143e',
|
||||
'7b0a23bffc80bac05739ea1af898daad']
|
||||
else:
|
||||
scan_md5s = ['3a552566097a8de588b8184b059b0158',
|
||||
'52c50ae824e329ba8b5b7a0f523efffe',
|
||||
'57bb5be0ea44a755ada4a93885c3825e',
|
||||
'8179b557433835827a70510584f3143e']
|
||||
'8179b557433835827a70510584f3143e',
|
||||
'7b0a23bffc80bac05739ea1af898daad']
|
||||
for md5 in scan_md5s:
|
||||
resp = http_client.post('/delete_scan/', {'md5': md5})
|
||||
if resp.status_code == 200:
|
||||
@@ -183,6 +187,7 @@ def api_test():
|
||||
{'hash': '52c50ae824e329ba8b5b7a0f523efffe'},
|
||||
{'hash': '57bb5be0ea44a755ada4a93885c3825e'},
|
||||
{'hash': '8179b557433835827a70510584f3143e'},
|
||||
{'hash': '7b0a23bffc80bac05739ea1af898daad'},
|
||||
]
|
||||
else:
|
||||
pdfs = [
|
||||
@@ -190,6 +195,7 @@ def api_test():
|
||||
{'hash': '52c50ae824e329ba8b5b7a0f523efffe'},
|
||||
{'hash': '57bb5be0ea44a755ada4a93885c3825e'},
|
||||
{'hash': '8179b557433835827a70510584f3143e'},
|
||||
{'hash': '7b0a23bffc80bac05739ea1af898daad'},
|
||||
]
|
||||
for pdf in pdfs:
|
||||
resp = http_client.post(
|
||||
@@ -253,12 +259,14 @@ def api_test():
|
||||
'52c50ae824e329ba8b5b7a0f523efffe',
|
||||
'57bb5be0ea44a755ada4a93885c3825e',
|
||||
'8179b557433835827a70510584f3143e',
|
||||
'7b0a23bffc80bac05739ea1af898daad',
|
||||
]
|
||||
else:
|
||||
scan_md5s = ['3a552566097a8de588b8184b059b0158',
|
||||
'52c50ae824e329ba8b5b7a0f523efffe',
|
||||
'57bb5be0ea44a755ada4a93885c3825e',
|
||||
'8179b557433835827a70510584f3143e',
|
||||
'7b0a23bffc80bac05739ea1af898daad',
|
||||
]
|
||||
for md5 in scan_md5s:
|
||||
resp = http_client.post(
|
||||
|
||||
@@ -11,10 +11,17 @@ from django.conf import settings
|
||||
|
||||
from MobSF.utils import filename_from_path
|
||||
|
||||
from StaticAnalyzer.views.android import android_apis, android_rules
|
||||
from StaticAnalyzer.views.shared_func import (api_rule_matcher,
|
||||
code_rule_matcher,
|
||||
url_n_email_extract)
|
||||
from StaticAnalyzer.views.android.rules import (
|
||||
android_apis,
|
||||
android_rules,
|
||||
)
|
||||
from StaticAnalyzer.views.shared_func import (
|
||||
url_n_email_extract,
|
||||
)
|
||||
from StaticAnalyzer.views.rule_matchers import (
|
||||
api_rule_matcher,
|
||||
code_rule_matcher,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+145
-138
@@ -36,387 +36,394 @@ Rule Format.
|
||||
c. perm - Permission
|
||||
|
||||
"""
|
||||
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
APIS = [
|
||||
{
|
||||
'desc': 'Loading Native Code (Shared Library) ',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'System\.loadLibrary\(|System\.load\(',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get System Service',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'getSystemService',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Dynamic Class and Dexloading',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': (r'dalvik\.system\.DexClassLoader|'
|
||||
r'java\.security\.ClassLoader|'
|
||||
r'java\.net\.URLClassLoader|'
|
||||
r'java\.security\.SecureClassLoader'),
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Java Reflection',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': (r'java\.lang\.reflect\.Method|'
|
||||
r'java\.lang\.reflect\.Field|Class\.forName'),
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Crypto',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'javax\.crypto|kalium\.crypto|bouncycastle\.crypto',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Starting Activity',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'startActivity\(|startActivityForResult\(',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Starting Service',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'startService\(|bindService\(',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Sending Broadcast',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': (r'sendBroadcast\(|'
|
||||
r'sendOrderedBroadcast\(|sendStickyBroadcast\('),
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Local File I/O Operations',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': (r'OpenFileOutput|getSharedPreferences|'
|
||||
r'SharedPreferences\.Editor|getCacheDir|'
|
||||
r'getExternalStorageState|openOrCreateDatabase'),
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Inter Process Communication',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'IRemoteService|IRemoteService\.Stub|IBinder|Intent',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'HTTP Requests, Connections and Sessions',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': (r'http\.client\.HttpClient|net\.http\.AndroidHttpClient|'
|
||||
r'http\.impl\.client\.AbstractHttpClient'),
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'HTTP Connection',
|
||||
'type': 'regex',
|
||||
'match': 'regex_and',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.regex_and,
|
||||
'regex1': r'HttpURLConnection|org\.apache\.http',
|
||||
'regex2': r'openConnection|connect|HttpRequest',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Load and Manipulate Dex Files',
|
||||
'type': 'regex',
|
||||
'match': 'regex_and',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.regex_and,
|
||||
'regex1': (r'dalvik\.system\.PathClassLoader|'
|
||||
r'dalvik\.system\.DexFile|dalvik\.system\.DexPathList'),
|
||||
'regex2': r'loadDex|loadClass|DexClassLoader|loadDexFile',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Content Provider',
|
||||
'type': 'string',
|
||||
'match': 'single_string',
|
||||
'type': MatchType.string,
|
||||
'match': Match.single_string,
|
||||
'string1': 'android.content.ContentProvider',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Obfuscation',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'utils.AESObfuscator',
|
||||
'string2': 'getObfuscator',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Execute OS Command',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'getRuntime().exec(',
|
||||
'string2': 'getRuntime(',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Android Keystore',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'security.KeyStore',
|
||||
'string2': 'Keystore.getInstance(',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'TCP Server Socket',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'ServerSocket',
|
||||
'string2': 'net.ServerSocket',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'TCP Socket',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'Socket',
|
||||
'string2': 'net.Socket',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'UDP Datagram Packet',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'DatagramPacket',
|
||||
'string2': 'net.DatagramPacket',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'UDP Datagram Socket',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'DatagramSocket',
|
||||
'string2': 'net.DatagramSocket',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'WebView JavaScript Interface',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'addJavascriptInterface',
|
||||
'string2': 'WebView',
|
||||
'string3': 'android.webkit',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'WebView GET Request',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'WebView',
|
||||
'string2': 'loadData',
|
||||
'string3': 'android.webkit',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'WebView POST Request',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'WebView',
|
||||
'string2': 'postUrl',
|
||||
'string3': 'android.webkit',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Android Notifications',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'app.NotificationManager',
|
||||
'string2': 'notify',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get Cell Information',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getAllCellInfo',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get Cell Location',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getCellLocation',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get Subscriber ID',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getSubscriberId',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get Device ID, IMEI,MEID/ESN etc.',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getDeviceId',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get Software Version, IMEI/SV etc.',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getDeviceSoftwareVersion',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get SIM Serial Number',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getSimSerialNumber',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get SIM Provider Details',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getSimOperator',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Get SIM Operator Name',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'telephony.TelephonyManager',
|
||||
'string2': 'getSimOperatorName',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Query Database of SMS, Contacts etc.',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'content.ContentResolver',
|
||||
'string2': 'query',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Query Database of SMS, Contacts etc.',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'content.ContentResolver',
|
||||
'string2': 'query',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Base64 Decode',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'android.util.Base64',
|
||||
'string2': '.decode',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Send SMS',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'telephony.SmsManager',
|
||||
'string_or1': 'sendMultipartTextMessage',
|
||||
'string_or2': 'sendTextMessage',
|
||||
'string_or3': 'vnd.android-dir/mms-sms',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'URL Connection to file/http/https/ftp/jar',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'net.URLConnection',
|
||||
'string_or1': 'openConnection',
|
||||
'string_or2': 'connect',
|
||||
'string_or3': 'openStream',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'JAR URL Connection',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'net.JarURLConnection',
|
||||
'string_or1': 'JarURLConnection',
|
||||
'string_or2': 'jar:',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'HTTPS Connection',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'javax.net.ssl.HttpsURLConnection',
|
||||
'string_or1': 'HttpsURLConnection',
|
||||
'string_or2': 'connect',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'URL Connection supports file,http,https,ftp and jar',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'net.URL',
|
||||
'string_or1': 'openConnection',
|
||||
'string_or2': 'openStream',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Set or Read Clipboard data',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'content.ClipboardManager',
|
||||
'string_or1': 'CLIPBOARD_SERVICE',
|
||||
'string_or2': 'ClipboardManager',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Base64 Encode',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'android.util.Base64',
|
||||
'string_or1': '.encodeToString',
|
||||
'string_or2': '.encode',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Base64 Encode',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'android.util.Base64',
|
||||
'string_or1': '.encodeToString',
|
||||
'string_or2': '.encode',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Message Digest',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'java.security.MessageDigest',
|
||||
'string_or1': 'MessageDigestSpi',
|
||||
'string_or2': 'MessageDigest',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'GPS Location',
|
||||
'type': 'string',
|
||||
'match': 'string_and_or',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and_or,
|
||||
'string1': 'android.location',
|
||||
'string_or1': 'getLastKnownLocation(',
|
||||
'string_or2': 'requestLocationUpdates(',
|
||||
'string_or3': 'getLatitude(',
|
||||
'string_or4': 'getLongitude(',
|
||||
'input_case': 'exact',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
]
|
||||
+295
-238
@@ -42,80 +42,97 @@ Rule Format.
|
||||
c. perm - Permission
|
||||
|
||||
"""
|
||||
from StaticAnalyzer.views.standards import (
|
||||
CWE,
|
||||
OWASP,
|
||||
OWASP_MSTG,
|
||||
)
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Level,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
RULES = [
|
||||
{
|
||||
'desc': ('Files may contain hardcoded sensitive '
|
||||
'informations like usernames, passwords, keys etc.'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'(password\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(pass\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(username\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(secret\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(key\s*=\s*[\'|\"].+[\'|\"]\s{0,5})'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'lower',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.lower,
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-312',
|
||||
'owasp': 'M9: Reverse Engineering',
|
||||
'cwe': CWE['CWE-312'],
|
||||
'owasp': OWASP['m9'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': 'IP Address disclosure',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}',
|
||||
'level': 'warning',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 4.3,
|
||||
'cwe': 'CWE-200',
|
||||
'cwe': CWE['CWE-200'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('Hidden elements in view can be used to hide'
|
||||
' data from user. But this data can be leaked'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'setVisibility\(View\.GONE\)|'
|
||||
r'setVisibility\(View\.INVISIBLE\)'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 4.3,
|
||||
'cwe': 'CWE-919',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
'cwe': CWE['CWE-919'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-7'],
|
||||
},
|
||||
{
|
||||
'desc': ('The App uses ECB mode in Cryptographic encryption algorithm.'
|
||||
' ECB mode is known to be weak as it results in the same'
|
||||
' ciphertext for identical blocks of plaintext.'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'Cipher\.getInstance\(\s*"\s*AES\/ECB',
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App uses RSA Crypto without OAEP padding. The purpose'
|
||||
' of the padding scheme is to prevent a number of attacks on'
|
||||
' RSA that only work when the encryption is performed'
|
||||
' without padding.'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'cipher\.getinstance\(\s*"rsa/.+/nopadding',
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'lower',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.lower,
|
||||
'cvss': 5.9,
|
||||
'cwe': 'CWE-780',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-780'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('Insecure Implementation of SSL. Trusting all the '
|
||||
'certificates or accepting self signed certificates'
|
||||
' is a critical Security Hole. This application is'
|
||||
' vulnerable to MITM attacks'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'javax\.net\.ssl',
|
||||
'regex2': (r'TrustAllSSLSocket-Factory|AllTrustSSLSocketFactory|'
|
||||
r'NonValidatingSSLSocketFactory|'
|
||||
@@ -123,260 +140,279 @@ RULES = [
|
||||
r'ALLOW_ALL_HOSTNAME_VERIFIER|'
|
||||
r'\.setDefaultHostnameVerifier\(|'
|
||||
r'NullHostnameVerifier\('),
|
||||
'level': 'high',
|
||||
'match': 'regex_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-295',
|
||||
'owasp': 'M3: Insecure Communication',
|
||||
'cwe': CWE['CWE-295'],
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('WebView load files from external storage. Files in external'
|
||||
' storage can be modified by any application.'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'\.loadUrl\(.*getExternalStorageDirectory\(',
|
||||
'regex2': r'webkit\.WebView',
|
||||
'level': 'high',
|
||||
'match': 'regex_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.0,
|
||||
'cwe': 'CWE-919',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
'cwe': CWE['CWE-919'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-6'],
|
||||
},
|
||||
{
|
||||
'desc': 'The file is World Readable. Any App can read from the file',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'MODE_WORLD_READABLE|Context\.MODE_WORLD_READABLE',
|
||||
'regex2': r'openFileOutput\(\s*".+"\s*,\s*1\s*\)',
|
||||
'level': 'high',
|
||||
'match': 'regex_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 4.0,
|
||||
'cwe': 'CWE-276',
|
||||
'owasp': 'M2: Insecure Data Storage',
|
||||
|
||||
'cwe': CWE['CWE-276'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'The file is World Writable. Any App can write to the file',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'MODE_WORLD_WRITABLE|Context\.MODE_WORLD_WRITABLE',
|
||||
'regex2': r'openFileOutput\(\s*".+"\s*,\s*2\s*\)',
|
||||
'level': 'high',
|
||||
'match': 'regex_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 6.0,
|
||||
'cwe': 'CWE-276',
|
||||
'owasp': 'M2: Insecure Data Storage',
|
||||
'cwe': CWE['CWE-276'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('The file is World Readable and Writable. '
|
||||
'Any App can read/write to the file'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'openFileOutput\(\s*".+"\s*,\s*3\s*\)',
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 6.0,
|
||||
'cwe': 'CWE-276',
|
||||
'owasp': 'M2: Insecure Data Storage',
|
||||
'cwe': CWE['CWE-276'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'Weak Hash algorithm used',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'getInstance(\"md4\")|getInstance(\"rc2\")|'
|
||||
r'getInstance(\"rc4\")|getInstance(\"RC4\")|'
|
||||
r'getInstance(\"RC2\")|getInstance(\"MD4\")'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'MD5 is a weak hash known to have hash collisions.',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'MessageDigest\.getInstance\(\"*MD5\"*\)|'
|
||||
r'MessageDigest\.getInstance\(\"*md5\"*\)|'
|
||||
r'DigestUtils\.md5\('),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'SHA-1 is a weak hash known to have hash collisions.',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'MessageDigest\.getInstance\(\"*SHA-1\"*\)|'
|
||||
r'MessageDigest\.getInstance\(\"*sha-1\"*\)|'
|
||||
r'DigestUtils\.sha\('),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('App can write to App Directory. '
|
||||
'Sensitive Information should be encrypted.'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'MODE_PRIVATE|Context\.MODE_PRIVATE',
|
||||
'level': 'info',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 3.9,
|
||||
'cwe': 'CWE-276',
|
||||
'cwe': CWE['CWE-276'],
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': 'The App uses an insecure Random Number Generator.',
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'\bjava\.util\.Random\b',
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': 'CWE-330',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-330'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-6'],
|
||||
},
|
||||
{
|
||||
'desc': ('The App logs information. '
|
||||
'Sensitive information should never be logged.'),
|
||||
'type': 'regex',
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'Log\.(v|d|i|w|e|f|s)|'
|
||||
r'System\.out\.print|System\.err\.print'),
|
||||
'level': 'info',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': 'CWE-532',
|
||||
'cwe': CWE['CWE-532'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App uses Java Hash Code. It\'s a weak hash function and'
|
||||
' should never be used in Secure Crypto Implementation.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '.hashCode()',
|
||||
'level': 'warning',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 2.3,
|
||||
'cwe': 'CWE-327',
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('These activities prevent '
|
||||
'screenshot when they go to background.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'LayoutParams.FLAG_SECURE',
|
||||
'level': 'good',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-9'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App uses SQL Cipher. But the secret may be hardcoded.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'SQLiteOpenHelper.getWritableDatabase(',
|
||||
'level': 'warning',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['crypto-1'],
|
||||
},
|
||||
{
|
||||
'desc': 'This app has capabilities to prevent tapjacking attacks.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'setFilterTouchesWhenObscured(true)',
|
||||
'level': 'good',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['platform-9'],
|
||||
},
|
||||
{
|
||||
'desc': ('App can read/write to External Storage. '
|
||||
'Any App can read data written to External Storage.'),
|
||||
'perm': 'android.permission.WRITE_EXTERNAL_STORAGE',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '.getExternalStorage',
|
||||
'string2': '.getExternalFilesDir(',
|
||||
'level': 'high',
|
||||
'match': 'string_or_and_perm',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_or_and_perm,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.5,
|
||||
'cwe': 'CWE-276',
|
||||
'owasp': 'M2: Insecure Data Storage',
|
||||
'cwe': CWE['CWE-276'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('App creates temp file. Sensitive '
|
||||
'information should never be written into a temp file.'),
|
||||
'perm': 'android.permission.WRITE_EXTERNAL_STORAGE',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '.createTempFile(',
|
||||
'level': 'high',
|
||||
'match': 'string_and_perm',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and_perm,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.5,
|
||||
'cwe': 'CWE-276',
|
||||
'owasp': 'M2: Insecure Data Storage',
|
||||
'cwe': CWE['CWE-276'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('Insecure WebView Implementation. Execution of user'
|
||||
' controlled code in WebView is a critical Security Hole.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'setJavaScriptEnabled(true)',
|
||||
'string2': '.addJavascriptInterface(',
|
||||
'level': 'warning',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.warning,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 8.8,
|
||||
'cwe': 'CWE-749',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
'cwe': CWE['CWE-749'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-7'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App uses SQL Cipher. SQLCipher '
|
||||
'provides 256-bit AES encryption to sqlite database files.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'SQLiteDatabase.loadLibs(',
|
||||
'string2': 'net.sqlcipher.',
|
||||
'level': 'info',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.info,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['crypto-1'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App download files using Android Download Manager',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'android.app.DownloadManager',
|
||||
'string2': 'getSystemService(DOWNLOAD_SERVICE)',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': '',
|
||||
},
|
||||
{
|
||||
'desc': 'This App use Realm Database with encryption.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'io.realm.Realm',
|
||||
'string2': '.encryptionKey(',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['crypto-1'],
|
||||
},
|
||||
{
|
||||
'desc': ('The App may use weak IVs like '
|
||||
@@ -385,258 +421,277 @@ RULES = [
|
||||
'Not using a random IV makes the resulting '
|
||||
'ciphertext much more predictable and '
|
||||
'susceptible to a dictionary attack.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00',
|
||||
'string2': '0x01,0x02,0x03,0x04,0x05,0x06,0x07',
|
||||
'level': 'high',
|
||||
'match': 'string_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 9.8,
|
||||
'cwe': 'CWE-329',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
'cwe': CWE['CWE-329'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'Remote WebView debugging is enabled.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '.setWebContentsDebuggingEnabled(true)',
|
||||
'string2': 'WebView',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.4,
|
||||
'cwe': 'CWE-919',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
'cwe': CWE['CWE-919'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['resilience-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('This app listens to Clipboard changes.'
|
||||
' Some malwares also listen to Clipboard changes.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'content.ClipboardManager',
|
||||
'string2': 'OnPrimaryClipChangedListener',
|
||||
'level': 'warning',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.warning,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['platform-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App copies data to clipboard. Sensitive data should'
|
||||
' not be copied to clipboard as other'
|
||||
' applications can access it.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'content.ClipboardManager',
|
||||
'string2': 'setPrimaryClip(',
|
||||
'level': 'info',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.info,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-10'],
|
||||
},
|
||||
{
|
||||
'desc': ('Insecure WebView Implementation. WebView ignores SSL'
|
||||
' Certificate errors and accept any SSL Certificate.'
|
||||
' This application is vulnerable to MITM attacks'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'onReceivedSslError(WebView',
|
||||
'string2': '.proceed();',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-295',
|
||||
'owasp': 'M3: Insecure Communication',
|
||||
'cwe': CWE['CWE-295'],
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('App uses SQLite Database and execute raw SQL query. '
|
||||
'Untrusted user input in raw SQL queries can cause'
|
||||
' SQL Injection. Also sensitive information should'
|
||||
' be encrypted and written to the database.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'android.database.sqlite',
|
||||
'string_or1': 'rawQuery(',
|
||||
'string_or2': 'execSQL(',
|
||||
'level': 'high',
|
||||
'match': 'string_and_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': 'CWE-89',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
'cwe': CWE['CWE-89'],
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': '',
|
||||
},
|
||||
{
|
||||
'desc': 'This App detects frida server.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'fridaserver',
|
||||
'string_or1': '27047',
|
||||
'string_or2': 'REJECT',
|
||||
'string_or3': 'LIBFRIDA',
|
||||
'level': 'good',
|
||||
'match': 'string_and_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App uses an SSL Pinning Library '
|
||||
'(org.thoughtcrime.ssl.pinning) to '
|
||||
'prevent MITM attacks in secure'
|
||||
' communication channel.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'org.thoughtcrime.ssl.pinning',
|
||||
'string_or1': 'PinningHelper.getPinnedHttpsURLConnection',
|
||||
'string_or2': 'PinningHelper.getPinnedHttpClient',
|
||||
'string_or3': 'PinningSSLSocketFactory(',
|
||||
'level': 'good',
|
||||
'match': 'string_and_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['network-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App has capabilities to prevent against'
|
||||
' Screenshots from Recent Task History/ Now On Tap etc.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '.FLAG_SECURE',
|
||||
'string_or1': 'getWindow().setFlags(',
|
||||
'string_or2': 'getWindow().addFlags(',
|
||||
'level': 'high',
|
||||
'match': 'string_and_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-9'],
|
||||
},
|
||||
{
|
||||
'desc': ('DexGuard Debug Detection code to detect'
|
||||
' wheather an App is debuggable or not is identified.'),
|
||||
'type': 'string',
|
||||
' whether an App is debuggable or not is identified.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'DebugDetector.isDebuggable',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'DexGuard Debugger Detection code is identified.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'DebugDetector.isDebuggerConnected',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'DexGuard Emulator Detection code is identified.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'EmulatorDetector.isRunningInEmulator',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-5'],
|
||||
},
|
||||
{
|
||||
'desc': ('DexGuard code to detect wheather the App'
|
||||
'desc': ('DexGuard code to detect whether the App'
|
||||
' is signed with a debug key or not is identified.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'DebugDetector.isSignedWithDebugKey',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'DexGuard Root Detection code is identified.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'RootDetector.isDeviceRooted',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-1'],
|
||||
},
|
||||
{
|
||||
'desc': 'DexGuard App Tamper Detection code is identified.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'TamperDetector.checkApk',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('DexGuard Signer Certificate'
|
||||
' Tamper Detection code is identified.'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'import dexguard.util',
|
||||
'string2': 'TCertificateChecker.checkCertificate',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'The App may use package signature for tamper detection.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'PackageManager.GET_SIGNATURES',
|
||||
'string2': 'getPackageName(',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App uses SafetyNet API.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'com.google.android.gms.safetynet.SafetyNetApi',
|
||||
'level': 'good',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-7'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may request root (Super User) privileges.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'com.noshufou.android.su',
|
||||
'string2': 'com.thirdparty.superuser',
|
||||
'string3': 'eu.chainfire.supersu',
|
||||
'string4': 'com.koushikdutta.superuser',
|
||||
'string5': 'eu.chainfire.',
|
||||
'level': 'high',
|
||||
'match': 'string_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': 'CWE-250',
|
||||
'cwe': CWE['CWE-250'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-1'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have root detection capabilities.',
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': '.contains("test-keys")',
|
||||
'string2': '/system/app/Superuser.apk',
|
||||
'string3': 'isDeviceRooted()',
|
||||
@@ -644,24 +699,26 @@ RULES = [
|
||||
'string5': '/system/sd/xbin/su',
|
||||
'string6': '"/system/xbin/which", "su"',
|
||||
'string7': 'RootTools.isAccessGiven()',
|
||||
'level': 'good',
|
||||
'match': 'string_or',
|
||||
'input_case': 'exact',
|
||||
'level': Level.good,
|
||||
'match': Match.string_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-1'],
|
||||
},
|
||||
{
|
||||
'desc': ('The app uses jackson deserialization library'
|
||||
'Deserialization of untrusted input can result in'
|
||||
'arbitary code execution'),
|
||||
'type': 'string',
|
||||
'type': MatchType.string,
|
||||
'string1': 'com.fasterxml.jackson.databind.ObjectMapper',
|
||||
'string2': '.enableDefaultTyping(',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': 'CWE-502',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
'cwe': CWE['CWE-502'],
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-8'],
|
||||
}]
|
||||
@@ -3,11 +3,6 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import stat
|
||||
import subprocess
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from macholib.mach_o import (CPU_TYPE_NAMES, MH_CIGAM_64, MH_MAGIC_64,
|
||||
get_cpu_subtype)
|
||||
@@ -15,10 +10,16 @@ from macholib.MachO import MachO
|
||||
|
||||
from MobSF.utils import is_file_exists
|
||||
|
||||
from StaticAnalyzer.views.ios.classdump import get_class_dump
|
||||
from StaticAnalyzer.views.ios.otool_analysis import (
|
||||
get_otool_out,
|
||||
otool_analysis,
|
||||
)
|
||||
from StaticAnalyzer.views.ios.rules import (
|
||||
ipa_rules,
|
||||
)
|
||||
from StaticAnalyzer.views.rule_matchers import (
|
||||
binary_rule_matcher,
|
||||
)
|
||||
from StaticAnalyzer.tools.strings import strings_util
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -32,66 +33,6 @@ def detect_bin_type(libs):
|
||||
return 'Objective C'
|
||||
|
||||
|
||||
def class_dump(tools_dir, bin_path, app_dir, bin_type):
|
||||
"""Running Classdumpz on binary."""
|
||||
try:
|
||||
webview = {}
|
||||
classdump = ''
|
||||
if platform.system() == 'Darwin':
|
||||
logger.info('Dumping classes')
|
||||
if bin_type == 'Swift':
|
||||
logger.info('Running class-dump-swift against binary')
|
||||
if (len(settings.CLASSDUMP_SWIFT_BINARY) > 0
|
||||
and is_file_exists(settings.CLASSDUMP_SWIFT_BINARY)):
|
||||
class_dump_bin = settings.CLASSDUMP_SWIFT_BINARY
|
||||
else:
|
||||
class_dump_bin = os.path.join(
|
||||
tools_dir, 'class-dump-swift')
|
||||
else:
|
||||
logger.info('Running class-dump against binary')
|
||||
if (len(settings.CLASSDUMP_BINARY) > 0
|
||||
and is_file_exists(settings.CLASSDUMP_BINARY)):
|
||||
class_dump_bin = settings.CLASSDUMP_BINARY
|
||||
else:
|
||||
class_dump_bin = os.path.join(tools_dir, 'class-dump')
|
||||
# Execute permission check
|
||||
if not os.access(class_dump_bin, os.X_OK):
|
||||
os.chmod(class_dump_bin, stat.S_IEXEC)
|
||||
args = [class_dump_bin, bin_path]
|
||||
elif platform.system() == 'Linux':
|
||||
logger.info('Running jtool against the binary for dumping classes')
|
||||
args = get_otool_out(tools_dir, 'classdump', bin_path, '')
|
||||
else:
|
||||
# Platform not supported
|
||||
logger.warning('class-dump is not supported in this platform')
|
||||
return {}
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
# timeout to handle possible deadlock from jtool1
|
||||
classdump = subprocess.check_output(args,
|
||||
stderr=devnull,
|
||||
timeout=30)
|
||||
if b'Source: (null)' in classdump and platform.system() == 'Darwin':
|
||||
logger.info('Running fail safe class-dump-swift')
|
||||
class_dump_bin = os.path.join(
|
||||
tools_dir, 'class-dump-swift')
|
||||
args = [class_dump_bin, bin_path]
|
||||
classdump = subprocess.check_output(args)
|
||||
dump_file = os.path.join(app_dir, 'classdump.txt')
|
||||
with open(dump_file, 'w') as flip:
|
||||
flip.write(classdump.decode('utf-8', 'ignore'))
|
||||
if b'UIWebView' in classdump:
|
||||
webview = {'issue': 'Binary uses WebView Component.',
|
||||
'level': 'info',
|
||||
'description': 'The binary may use WebView Component.',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
}
|
||||
return webview
|
||||
except Exception:
|
||||
logger.error('class-dump/class-dump-swift failed on this binary')
|
||||
|
||||
|
||||
def strings_on_ipa(bin_path):
|
||||
"""Extract Strings from IPA."""
|
||||
try:
|
||||
@@ -126,6 +67,7 @@ def binary_analysis(src, tools_dir, app_dir, executable_name):
|
||||
"""Binary Analysis of IPA."""
|
||||
try:
|
||||
binary_analysis_dict = {}
|
||||
binary_findings = {}
|
||||
logger.info('Starting Binary Analysis')
|
||||
dirs = os.listdir(src)
|
||||
dot_app_dir = ''
|
||||
@@ -143,23 +85,27 @@ def binary_analysis(src, tools_dir, app_dir, executable_name):
|
||||
# Bin Path - Dir/Payload/x.app/x
|
||||
bin_path = os.path.join(bin_dir, bin_name)
|
||||
binary_analysis_dict['libs'] = []
|
||||
binary_analysis_dict['bin_res'] = []
|
||||
binary_analysis_dict['bin_res'] = {}
|
||||
binary_analysis_dict['strings'] = []
|
||||
if not is_file_exists(bin_path):
|
||||
logger.warning('MobSF Cannot find binary in %s', bin_path)
|
||||
logger.warning('Skipping Otool, Classdump and Strings')
|
||||
else:
|
||||
bin_info = get_bin_info(bin_path)
|
||||
otool_dict = otool_analysis(tools_dir, bin_name, bin_path, bin_dir)
|
||||
bin_type = detect_bin_type(otool_dict['libs'])
|
||||
api = class_dump(tools_dir, bin_path, app_dir, bin_type)
|
||||
if not api:
|
||||
api = {}
|
||||
object_data = otool_analysis(
|
||||
tools_dir,
|
||||
bin_name,
|
||||
bin_path,
|
||||
bin_dir)
|
||||
bin_type = detect_bin_type(object_data['libs'])
|
||||
cdump = get_class_dump(tools_dir, bin_path, app_dir, bin_type)
|
||||
binary_rule_matcher(
|
||||
binary_findings,
|
||||
object_data['bindata'] + cdump,
|
||||
ipa_rules.IPA_RULES)
|
||||
strings_in_ipa = strings_on_ipa(bin_path)
|
||||
otool_dict['anal'] = list(
|
||||
filter(None, otool_dict['anal'] + [api]))
|
||||
binary_analysis_dict['libs'] = otool_dict['libs']
|
||||
binary_analysis_dict['bin_res'] = otool_dict['anal']
|
||||
binary_analysis_dict['libs'] = object_data['libs']
|
||||
binary_analysis_dict['bin_res'] = binary_findings
|
||||
binary_analysis_dict['strings'] = strings_in_ipa
|
||||
binary_analysis_dict['macho'] = bin_info
|
||||
binary_analysis_dict['bin_type'] = bin_type
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf_8 -*-
|
||||
"""Handle Classdump for iOS binaries."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import stat
|
||||
import subprocess
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from MobSF.utils import is_file_exists
|
||||
|
||||
from StaticAnalyzer.views.ios.otool_analysis import (
|
||||
get_otool_out,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def classdump_mac(clsdmp_bin, tools_dir, ipa_bin):
|
||||
"""Run Classdump for Objective-C/Swift."""
|
||||
if clsdmp_bin == 'class-dump-swift':
|
||||
logger.info('Running class-dump-swift against binary')
|
||||
external = settings.CLASSDUMP_SWIFT_BINARY
|
||||
else:
|
||||
logger.info('Running class-dump against binary')
|
||||
external = settings.CLASSDUMP_BINARY
|
||||
if (len(external) > 0
|
||||
and is_file_exists(external)):
|
||||
class_dump_bin = external
|
||||
else:
|
||||
class_dump_bin = os.path.join(
|
||||
tools_dir, clsdmp_bin)
|
||||
# Execute permission check
|
||||
if not os.access(class_dump_bin, os.X_OK):
|
||||
os.chmod(class_dump_bin, stat.S_IEXEC)
|
||||
return subprocess.check_output([class_dump_bin, ipa_bin])
|
||||
|
||||
|
||||
def classdump_linux(tools_dir, ipa_bin):
|
||||
"""Run Classdump on Linux."""
|
||||
try:
|
||||
logger.info('Running jtool against the binary for dumping classes')
|
||||
args = get_otool_out(tools_dir, 'classdump', ipa_bin, '')
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
# timeout to handle possible deadlock from jtool1
|
||||
return subprocess.check_output(args,
|
||||
stderr=devnull,
|
||||
timeout=30)
|
||||
except Exception:
|
||||
return b''
|
||||
|
||||
|
||||
def get_class_dump(tools_dir, bin_path, app_dir, bin_type):
|
||||
"""Running Classdump on binary."""
|
||||
try:
|
||||
cdump = b''
|
||||
logger.info('Dumping classes')
|
||||
if platform.system() == 'Darwin':
|
||||
if bin_type == 'Swift':
|
||||
try:
|
||||
cdump = classdump_mac(
|
||||
'class-dump-swift',
|
||||
tools_dir,
|
||||
bin_path,
|
||||
)
|
||||
except Exception:
|
||||
cdump = classdump_mac(
|
||||
'class-dump',
|
||||
tools_dir,
|
||||
bin_path,
|
||||
)
|
||||
else:
|
||||
try:
|
||||
cdump = classdump_mac(
|
||||
'class-dump',
|
||||
tools_dir,
|
||||
bin_path,
|
||||
)
|
||||
except Exception:
|
||||
cdump = classdump_mac(
|
||||
'class-dump-swift',
|
||||
tools_dir,
|
||||
bin_path,
|
||||
)
|
||||
if b'Source: (null)' in cdump:
|
||||
# Run failsafe if classdump failed
|
||||
logger.info('Running fail safe class-dump-swift')
|
||||
cdump = classdump_mac(
|
||||
'class-dump-swift',
|
||||
tools_dir,
|
||||
bin_path,
|
||||
)
|
||||
elif platform.system() == 'Linux':
|
||||
cdump = classdump_linux(tools_dir, bin_path)
|
||||
else:
|
||||
# Platform not supported
|
||||
logger.warning('class-dump is not supported in this platform')
|
||||
with open(os.path.join(app_dir, 'classdump.txt'), 'wb') as flip:
|
||||
flip.write(cdump)
|
||||
return cdump
|
||||
except Exception:
|
||||
logger.error('class-dump/class-dump-swift failed on this binary')
|
||||
return cdump
|
||||
@@ -2,62 +2,95 @@ import io
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from enum import Enum
|
||||
|
||||
from MalwareAnalyzer.views.domain_check import malware_check
|
||||
|
||||
from StaticAnalyzer.views.ios import ios_apis, ios_rules
|
||||
from StaticAnalyzer.views.shared_func import (api_rule_matcher,
|
||||
code_rule_matcher,
|
||||
url_n_email_extract)
|
||||
from StaticAnalyzer.views.ios.rules import (
|
||||
ios_apis, objc_rules,
|
||||
swift_rules,
|
||||
)
|
||||
from StaticAnalyzer.views.shared_func import (
|
||||
url_n_email_extract,
|
||||
)
|
||||
from StaticAnalyzer.views.rule_matchers import (
|
||||
api_rule_matcher,
|
||||
code_rule_matcher,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _SourceType(Enum):
|
||||
swift = 'Swift'
|
||||
objc = 'Objective-C'
|
||||
swift_and_objc = 'Swift, Objective-C'
|
||||
nocode = 'No Code'
|
||||
|
||||
|
||||
def ios_source_analysis(src):
|
||||
"""IOS Objective-C Code Analysis."""
|
||||
"""IOS Objective-C and Swift Code Analysis."""
|
||||
try:
|
||||
logger.info('Starting iOS Source Code and PLIST Analysis')
|
||||
source_type = 'Swift'
|
||||
api_rules = ios_apis.CODE_APIS
|
||||
code_rules = ios_rules.CODE_RULES
|
||||
|
||||
code_findings = {}
|
||||
api_findings = {}
|
||||
email_n_file = []
|
||||
url_n_file = []
|
||||
url_list = []
|
||||
domains = {}
|
||||
source_type = ''
|
||||
source_types = set()
|
||||
|
||||
for dirname, _, files in os.walk(src):
|
||||
for jfile in files:
|
||||
|
||||
if jfile.endswith('.m'):
|
||||
source_type = 'Objective C'
|
||||
jfile_path = os.path.join(src, dirname, jfile)
|
||||
if '+' in jfile:
|
||||
new_path = os.path.join(
|
||||
src, dirname, jfile.replace('+', 'x'))
|
||||
shutil.move(jfile_path, new_path)
|
||||
jfile_path = new_path
|
||||
dat = ''
|
||||
with io.open(jfile_path,
|
||||
mode='r',
|
||||
encoding='utf8',
|
||||
errors='ignore') as flip:
|
||||
dat = flip.read()
|
||||
api_rules = ios_apis.CODE_APIS
|
||||
code_rules = objc_rules.OBJC_RULES
|
||||
source_types.add(_SourceType.objc)
|
||||
elif jfile.endswith('.swift'):
|
||||
api_rules = ios_apis.CODE_APIS
|
||||
code_rules = swift_rules.SWIFT_RULES
|
||||
source_types.add(_SourceType.swift)
|
||||
else:
|
||||
continue
|
||||
|
||||
# Code Analysis
|
||||
relative_src_path = jfile_path.replace(src, '')
|
||||
code_rule_matcher(code_findings, [], dat,
|
||||
relative_src_path, code_rules)
|
||||
# API Analysis
|
||||
api_rule_matcher(api_findings, [], dat,
|
||||
relative_src_path, api_rules)
|
||||
jfile_path = os.path.join(src, dirname, jfile)
|
||||
if '+' in jfile:
|
||||
new_path = os.path.join(
|
||||
src, dirname, jfile.replace('+', 'x'))
|
||||
shutil.move(jfile_path, new_path)
|
||||
jfile_path = new_path
|
||||
dat = ''
|
||||
with io.open(jfile_path,
|
||||
mode='r',
|
||||
encoding='utf8',
|
||||
errors='ignore') as flip:
|
||||
dat = flip.read()
|
||||
|
||||
# Code Analysis
|
||||
relative_src_path = jfile_path.replace(src, '')
|
||||
code_rule_matcher(code_findings, [], dat,
|
||||
relative_src_path, code_rules)
|
||||
# API Analysis
|
||||
api_rule_matcher(api_findings, [], dat,
|
||||
relative_src_path, api_rules)
|
||||
|
||||
# Extract URLs and Emails
|
||||
urls, urls_nf, emails_nf = url_n_email_extract(
|
||||
dat, relative_src_path)
|
||||
url_list.extend(urls)
|
||||
url_n_file.extend(urls_nf)
|
||||
email_n_file.extend(emails_nf)
|
||||
|
||||
if not source_types:
|
||||
source_type = _SourceType.nocode.value
|
||||
elif len(source_types) > 1:
|
||||
source_type = _SourceType.swift_and_objc.value
|
||||
else:
|
||||
source_type = source_types.pop().value
|
||||
|
||||
# Extract URLs and Emails
|
||||
urls, urls_nf, emails_nf = url_n_email_extract(
|
||||
dat, relative_src_path)
|
||||
url_list.extend(urls)
|
||||
url_n_file.extend(urls_nf)
|
||||
email_n_file.extend(emails_nf)
|
||||
urls_list = list(set(url_list))
|
||||
# Domain Extraction and Malware Check
|
||||
logger.info('Performing Malware Check on extracted Domains')
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
"""
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
3. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b .regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
c. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
d. single_string - if string1 in input
|
||||
e. string_and - if (string1 in input) and (string2 in input)
|
||||
f. string_or - if (string1 in input) or (string2 in input)
|
||||
g. string_and_or - if (string1 in input) and ((string2 in input)
|
||||
or (string3 in input))
|
||||
h. string_or_and - if (string1 in input) or ((string2 in input)
|
||||
and (string3 in input))
|
||||
|
||||
4. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
5. others
|
||||
a. string<no> - string1, string2, string3, string_or1, string_and1
|
||||
b. regex<no> - regex1, regex2, regex3
|
||||
|
||||
"""
|
||||
CODE_APIS = [
|
||||
{
|
||||
'desc': 'Network Calls',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'regex1': r'NSURL|CFStream|NSStream',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'Local File I/O Operations.',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'regex1': (r'Keychain|kSecAttrAccessibleWhenUnlocked|'
|
||||
r'kSecAttrAccessibleAfterFirstUnlock|SecItemAdd|'
|
||||
r'SecItemUpdate|NSDataWritingFileProtectionComplete'),
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'WebView Component',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'regex1': r'UIWebView',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'Encryption API',
|
||||
'type': 'regex',
|
||||
'match': 'single_regex',
|
||||
'regex1': r'RNEncryptor|RNDecryptor|AESCrypt',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'Keychain Access',
|
||||
'type': 'string',
|
||||
'match': 'single_string',
|
||||
'string1': 'PDKeychainBindings',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'WebView Load Request',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'string1': 'loadRequest',
|
||||
'string2': 'webView',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'WebView Load HTML String',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'string1': 'loadHTMLString',
|
||||
'string2': 'webView',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'Cookie Storage',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'string1': 'NSHTTPCookieStorage',
|
||||
'string2': 'sharedHTTPCookieStorage',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
{
|
||||
'desc': 'Set or Read Clipboard',
|
||||
'type': 'string',
|
||||
'match': 'string_and',
|
||||
'string1': 'UIPasteboard',
|
||||
'string2': 'generalPasteboard',
|
||||
'input_case': 'exact',
|
||||
},
|
||||
]
|
||||
@@ -1,375 +0,0 @@
|
||||
"""
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
3. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b .regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
c. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
d. single_string - if string1 in input
|
||||
e. string_and - if (string1 in input) and (string2 in input)
|
||||
f. string_or - if (string1 in input) or (string2 in input)
|
||||
g. string_and_or - if (string1 in input) and ((string_or1 in input)
|
||||
or (string_or2 in input))
|
||||
h. string_or_and - if (string1 in input) or ((string_and1 in input)
|
||||
and (string_and2 in input))
|
||||
|
||||
4. level
|
||||
a. high
|
||||
b. warning
|
||||
c. info
|
||||
d. good
|
||||
|
||||
5. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
6. others
|
||||
a. string<no> - string1, string2, string3, string_or1, string_and1
|
||||
b. regex<no> - regex1, regex2, regex3
|
||||
|
||||
"""
|
||||
CODE_RULES = [
|
||||
{
|
||||
'desc': ('The App may contain banned API(s). '
|
||||
'These API(s) are insecure and must not be used.'),
|
||||
'type': 'regex',
|
||||
'regex1': (r'strcpy|memcpy|strcat|strncat|'
|
||||
r'strncpy|sprintf|vsprintf|gets'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'cvss': 2.2,
|
||||
'cwe': 'CWE-676',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
},
|
||||
{
|
||||
'desc': ('App allows self signed or invalid '
|
||||
'SSL certificates. App is vulnerable to MITM attacks.'),
|
||||
'type': 'regex',
|
||||
'regex1': (r'canAuthenticateAgainstProtectionSpace|'
|
||||
r'continueWithoutCredentialForAuthenticationChallenge|'
|
||||
r'kCFStreamSSLAllowsExpiredCertificates|'
|
||||
r'kCFStreamSSLAllowsAnyRoot|'
|
||||
r'kCFStreamSSLAllowsExpiredRoots|'
|
||||
r'validatesSecureCertificate\s*=\s*(no|NO)|'
|
||||
r'allowInvalidCertificates\s*=\s*(YES|yes)'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-295',
|
||||
'owasp': 'M3: Insecure Communication',
|
||||
},
|
||||
{
|
||||
'desc': ('UIWebView in App ignore SSL errors and accept'
|
||||
' any SSL Certificate. App is vulnerable to MITM attacks.'),
|
||||
'type': 'regex',
|
||||
'regex1': (r'setAllowsAnyHTTPSCertificate:\s*YES|'
|
||||
r'allowsAnyHTTPSCertificateForHost|'
|
||||
r'loadingUnvalidatedHTTPSPage\s*=\s*(YES|yes)'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-295',
|
||||
'owasp': 'M3: Insecure Communication',
|
||||
},
|
||||
{
|
||||
'desc': ('Files may contain hardcoded sensitive'
|
||||
' informations like usernames, passwords, keys etc.'),
|
||||
'type': 'regex',
|
||||
'regex1': (r'(password\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(pass\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(username\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(secret\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(key\s*=\s*[\'|\"].+[\'|\"]\s{0,5})'),
|
||||
'level': 'high',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'lower',
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-312',
|
||||
'owasp': 'M9: Reverse Engineering',
|
||||
},
|
||||
{
|
||||
'desc': 'IP Address disclosure',
|
||||
'type': 'regex',
|
||||
'regex1': r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}',
|
||||
'level': 'warning',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'cvss': 4.3,
|
||||
'cwe': 'CWE-200',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('The App logs information. '
|
||||
'Sensitive information should never be logged.'),
|
||||
'type': 'regex',
|
||||
'regex1': r'NSLog|NSAssert|fprintf|fprintf|Logging',
|
||||
'level': 'info',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'cvss': 7.5,
|
||||
'cwe': 'CWE-532',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('This app listens to Clipboard changes. '
|
||||
'Some malwares also listen to Clipboard changes.'),
|
||||
'type': 'regex',
|
||||
'regex1': (r'UIPasteboardChangedNotification|'
|
||||
r'generalPasteboard\]\.string'),
|
||||
'level': 'warning',
|
||||
'match': 'single_regex',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('App uses SQLite Database. '
|
||||
'Sensitive Information should be encrypted.'),
|
||||
'type': 'string',
|
||||
'string1': 'sqlite3_exec',
|
||||
'level': 'info',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('Untrusted user input to "NSTemporaryDirectory()"'
|
||||
' will result in path traversal vulnerability.'),
|
||||
'type': 'string',
|
||||
'string1': 'NSTemporaryDirectory(),',
|
||||
'level': 'warning',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'cvss': 7.5,
|
||||
'cwe': 'CWE-22',
|
||||
'owasp': 'M10: Extraneous Functionality',
|
||||
},
|
||||
{
|
||||
'desc': ('File is stored in an encrypted format on '
|
||||
'disk and cannot be read from or written to '
|
||||
'while the device is locked or booting.'),
|
||||
'type': 'string',
|
||||
'string1': 'NSFileProtectionComplete',
|
||||
'level': 'good',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('File is stored in an encrypted format '
|
||||
'on disk after it is closed.'),
|
||||
'type': 'string',
|
||||
'string1': 'NSFileProtectionCompleteUnlessOpen',
|
||||
'level': 'good',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('File is stored in an encrypted format '
|
||||
'on disk and cannot be accessed until after '
|
||||
'the device has booted.'),
|
||||
'type': 'string',
|
||||
'string1': (
|
||||
'NSFileProtectionComplete'
|
||||
'UntilFirstUserAuthentication'),
|
||||
'level': 'good',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('The file has no special protections '
|
||||
'associated with it.'),
|
||||
'type': 'string',
|
||||
'string1': 'NSFileProtectionNone',
|
||||
'level': 'warning',
|
||||
'match': 'single_string',
|
||||
'input_case': 'exact',
|
||||
'cvss': 4.3,
|
||||
'cwe': 'CWE-311',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
},
|
||||
{
|
||||
'desc': ('User input in "loadHTMLString" '
|
||||
'will result in JavaScript Injection.'),
|
||||
'type': 'string',
|
||||
'string1': 'loadHTMLString',
|
||||
'string2': 'webView',
|
||||
'level': 'warning',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 8.8,
|
||||
'cwe': 'CWE-95',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
},
|
||||
{
|
||||
'desc': 'SFAntiPiracy Jailbreak checks found',
|
||||
'type': 'string',
|
||||
'string1': 'SFAntiPiracy.h',
|
||||
'string2': 'SFAntiPiracy',
|
||||
'string3': 'isJailbroken',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': 'SFAntiPiracy Piracy checks found',
|
||||
'type': 'string',
|
||||
'string1': 'SFAntiPiracy.h',
|
||||
'string2': 'SFAntiPiracy',
|
||||
'string3': 'isPirated',
|
||||
'level': 'good',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': 'MD5 is a weak hash known to have hash collisions.',
|
||||
'type': 'string',
|
||||
'string1': 'CommonDigest.h',
|
||||
'string2': 'CC_MD5',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 7.4,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
},
|
||||
{
|
||||
'desc': 'SHA1 is a weak hash known to have hash collisions.',
|
||||
'type': 'string',
|
||||
'string1': 'CommonDigest.h',
|
||||
'string2': 'CC_SHA1',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 5.9,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
},
|
||||
{
|
||||
'desc': ('The App uses ECB mode in Cryptographic encryption algorithm.'
|
||||
' ECB mode is known to be weak as it results in the same'
|
||||
' ciphertext for identical blocks of plaintext.'),
|
||||
'type': 'string',
|
||||
'string1': 'kCCOptionECBMode',
|
||||
'string2': 'kCCAlgorithmAES',
|
||||
'level': 'high',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 5.9,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
},
|
||||
{
|
||||
'desc': 'The App has ant-debugger code using ptrace() ',
|
||||
'type': 'string',
|
||||
'string1': 'ptrace_ptr',
|
||||
'string2': 'PT_DENY_ATTACH',
|
||||
'level': 'info',
|
||||
'match': 'string_and',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': 'This App has anti-debugger code using Mach Exception Ports.',
|
||||
'type': 'string',
|
||||
'string1': 'mach/mach_init.h',
|
||||
'string_or1': 'MACH_PORT_VALID',
|
||||
'string_or2': 'mach_task_self()',
|
||||
'level': 'info',
|
||||
'match': 'string_and_or',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': ('This App copies data to clipboard. Sensitive data should'
|
||||
' not be copied to clipboard as other applications'
|
||||
' can access it.'),
|
||||
'type': 'string',
|
||||
'string1': 'UITextField',
|
||||
'string_or1': '@select(cut:)',
|
||||
'string_or2': '@select(copy:)',
|
||||
'level': 'info',
|
||||
'match': 'string_and_or',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have Jailbreak detection capabilities.',
|
||||
'type': 'string',
|
||||
'string1': '/Applications/Cydia.app',
|
||||
'string2': '/Library/MobileSubstrate/MobileSubstrate.dylib',
|
||||
'string3': '/usr/sbin/sshd',
|
||||
'string4': '/etc/apt',
|
||||
'string5': 'cydia://',
|
||||
'string6': '/var/lib/cydia',
|
||||
'string7': '/Applications/FakeCarrier.app',
|
||||
'string8': '/Applications/Icy.app',
|
||||
'string9': '/Applications/IntelliScreen.app',
|
||||
'string10': '/Applications/SBSettings.app',
|
||||
'string11': ('/Library/MobileSubstrate/DynamicLibraries/'
|
||||
'LiveClock.plist'),
|
||||
'string12': '/System/Library/LaunchDaemons/com.ikey.bbot.plist',
|
||||
'string13': ('/System/Library/LaunchDaemons/'
|
||||
'com.saurik.Cydia.Startup.plist'),
|
||||
'string14': '/etc/ssh/sshd_config',
|
||||
'string15': '/private/var/tmp/cydia.log',
|
||||
'string16': '/usr/libexec/ssh-keysign',
|
||||
'string17': '/Applications/MxTube.app',
|
||||
'string18': '/Applications/RockApp.app',
|
||||
'string19': '/Applications/WinterBoard.app',
|
||||
'string20': '/Applications/blackra1n.app',
|
||||
'string21': '/Library/MobileSubstrate/DynamicLibraries/Veency.plist',
|
||||
'string22': '/private/var/lib/apt',
|
||||
'string23': '/private/var/lib/cydia',
|
||||
'string24': '/private/var/mobile/Library/SBSettings/Themes',
|
||||
'string25': '/private/var/stash',
|
||||
'string26': '/usr/bin/sshd',
|
||||
'string27': '/usr/libexec/sftp-server',
|
||||
'string28': '/var/cache/apt',
|
||||
'string29': '/var/lib/apt',
|
||||
'string30': '/usr/sbin/frida-server',
|
||||
'string31': '/usr/bin/cycript',
|
||||
'string32': '/usr/local/bin/cycript',
|
||||
'string33': '/usr/lib/libcycript.dylib',
|
||||
'string34': 'frida-server',
|
||||
'level': 'good',
|
||||
'match': 'string_or',
|
||||
'input_case': 'exact',
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
},
|
||||
]
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import platform
|
||||
import stat
|
||||
import subprocess
|
||||
@@ -11,11 +10,8 @@ from django.utils.html import escape
|
||||
|
||||
from MobSF.utils import is_file_exists
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
SECURE = 'good'
|
||||
IN_SECURE = 'high'
|
||||
INFO = 'info'
|
||||
WARNING = 'warning'
|
||||
|
||||
|
||||
def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir):
|
||||
@@ -91,329 +87,29 @@ def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir):
|
||||
|
||||
def otool_analysis(tools_dir, bin_name, bin_path, bin_dir):
|
||||
"""OTOOL Analysis of Binary."""
|
||||
bindata = []
|
||||
otool_dict = {
|
||||
'libs': [],
|
||||
'bindata': '',
|
||||
}
|
||||
try:
|
||||
otool_dict = {
|
||||
'libs': [],
|
||||
'anal': [],
|
||||
}
|
||||
logger.info('Running Object analysis of binary: %s', bin_name)
|
||||
otool_dict['libs'] = get_otool_out(
|
||||
tools_dir, 'libs', bin_path, bin_dir)
|
||||
# PIE
|
||||
pie_dat = get_otool_out(tools_dir, 'header', bin_path, bin_dir)
|
||||
if b'PIE' in pie_dat:
|
||||
pie_flag = {
|
||||
'issue': 'fPIE -pie flag is Found',
|
||||
'level': SECURE,
|
||||
'description': ('App is compiled with Position Independent '
|
||||
'Executable (PIE) flag. This enables Address'
|
||||
' Space Layout Randomization (ASLR), a memory'
|
||||
' protection mechanism for'
|
||||
' exploit mitigation.'),
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
}
|
||||
else:
|
||||
pie_flag = {
|
||||
'issue': 'fPIE -pie flag is not Found',
|
||||
'level': IN_SECURE,
|
||||
'description': ('with Position Independent Executable (PIE) '
|
||||
'flag. So Address Space Layout Randomization '
|
||||
'(ASLR) is missing. ASLR is a memory '
|
||||
'protection mechanism for '
|
||||
'exploit mitigation.'),
|
||||
'cvss': 2,
|
||||
'cwe': 'CWE-119',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
}
|
||||
# Stack Smashing Protection & ARC
|
||||
dat = get_otool_out(tools_dir, 'symbols', bin_path, bin_dir)
|
||||
if b'stack_chk_guard' in dat:
|
||||
ssmash = {
|
||||
'issue': 'fstack-protector-all flag is Found',
|
||||
'level': SECURE,
|
||||
'description': ('App is compiled with Stack Smashing Protector'
|
||||
' (SSP) flag and is having protection against'
|
||||
' Stack Overflows/Stack Smashing Attacks.'),
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
}
|
||||
else:
|
||||
ssmash = {
|
||||
'issue': 'fstack-protector-all flag is not Found',
|
||||
'level': IN_SECURE,
|
||||
'description': ('App is not compiled with Stack Smashing '
|
||||
'Protector (SSP) flag. It is vulnerable to'
|
||||
'Stack Overflows/Stack Smashing Attacks.'),
|
||||
'cvss': 2,
|
||||
'cwe': 'CWE-119',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
}
|
||||
|
||||
# ARC
|
||||
if b'_objc_release' in dat:
|
||||
arc_flag = {
|
||||
'issue': 'fobjc-arc flag is Found',
|
||||
'level': SECURE,
|
||||
'description': ('App is compiled with Automatic Reference '
|
||||
'Counting (ARC) flag. ARC is a compiler '
|
||||
'feature that provides automatic memory '
|
||||
'management of Objective-C objects and is an '
|
||||
'exploit mitigation mechanism against memory '
|
||||
'corruption vulnerabilities.'),
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
}
|
||||
else:
|
||||
arc_flag = {
|
||||
'issue': 'fobjc-arc flag is not Found',
|
||||
'level': IN_SECURE,
|
||||
'description': ('App is not compiled with Automatic Reference '
|
||||
'Counting (ARC) flag. ARC is a compiler '
|
||||
'feature that provides automatic memory '
|
||||
'management of Objective-C objects and '
|
||||
'protects from memory corruption '
|
||||
'vulnerabilities.'),
|
||||
'cvss': 2,
|
||||
'cwe': 'CWE-119',
|
||||
'owasp': 'M1: Improper Platform Usage',
|
||||
}
|
||||
|
||||
banned_apis = {}
|
||||
baned = re.findall(
|
||||
rb'\b_alloca\b|\b_gets\b|\b_memcpy\b|\b_printf\b|\b_scanf\b|'
|
||||
rb'\b_sprintf\b|\b_sscanf\b|\b_strcat\b|'
|
||||
rb'\bStrCat\b|\b_strcpy\b|\bStrCpy\b|\b_strlen\b|\bStrLen\b|'
|
||||
rb'\b_strncat\b|\bStrNCat\b|\b_strncpy\b|'
|
||||
rb'\bStrNCpy\b|\b_strtok\b|\b_swprintf\b|\b_vsnprintf\b|'
|
||||
rb'\b_vsprintf\b|\b_vswprintf\b|\b_wcscat\b|\b_wcscpy\b|'
|
||||
rb'\b_wcslen\b|\b_wcsncat\b|\b_wcsncpy\b|\b_wcstok\b|\b_wmemcpy\b|'
|
||||
rb'\b_fopen\b|\b_chmod\b|\b_chown\b|\b_stat\b|\b_mktemp\b', dat)
|
||||
baned = list(set(baned))
|
||||
baned_s = b', '.join(baned)
|
||||
if len(baned_s) > 1:
|
||||
banned_apis = {
|
||||
'issue': 'Binary make use of insecure API(s)',
|
||||
'level': IN_SECURE,
|
||||
'description': ('The binary may contain'
|
||||
' the following insecure API(s) {}.').format(
|
||||
baned_s.decode('utf-8', 'ignore')),
|
||||
'cvss': 6,
|
||||
'cwe': 'CWE-676',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
}
|
||||
|
||||
weak_cryptos = {}
|
||||
weak_algo = re.findall(
|
||||
rb'\bkCCAlgorithmDES\b|'
|
||||
rb'\bkCCAlgorithm3DES\b|'
|
||||
rb'\bkCCAlgorithmRC2\b|'
|
||||
rb'\bkCCAlgorithmRC4\b|'
|
||||
rb'\bkCCOptionECBMode\b|'
|
||||
rb'\bkCCOptionCBCMode\b', dat)
|
||||
weak_algo = list(set(weak_algo))
|
||||
weak_algo_s = b', '.join(weak_algo)
|
||||
if len(weak_algo_s) > 1:
|
||||
weak_cryptos = {
|
||||
'issue': 'Binary make use of some Weak Crypto API(s)',
|
||||
'level': IN_SECURE,
|
||||
'description': ('The binary may use the'
|
||||
' following weak crypto API(s) {}.').formnat(
|
||||
weak_algo_s.decode('utf-8', 'ignore')),
|
||||
'cvss': 3,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
}
|
||||
|
||||
crypto = {}
|
||||
crypto_algo = re.findall(
|
||||
rb'\bCCKeyDerivationPBKDF\b|\bCCCryptorCreate\b|\b'
|
||||
rb'CCCryptorCreateFromData\b|\b'
|
||||
rb'CCCryptorRelease\b|\bCCCryptorUpdate\b|\bCCCryptorFinal\b|\b'
|
||||
rb'CCCryptorGetOutputLength\b|\bCCCryptorReset\b|\b'
|
||||
rb'CCCryptorRef\b|\bkCCEncrypt\b|\b'
|
||||
rb'kCCDecrypt\b|\bkCCAlgorithmAES128\b|\bkCCKeySizeAES128\b|\b'
|
||||
rb'kCCKeySizeAES192\b|\b'
|
||||
rb'kCCKeySizeAES256\b|\bkCCAlgorithmCAST\b|\b'
|
||||
rb'SecCertificateGetTypeID\b|\b'
|
||||
rb'SecIdentityGetTypeID\b|\bSecKeyGetTypeID\b|\b'
|
||||
rb'SecPolicyGetTypeID\b|\b'
|
||||
rb'SecTrustGetTypeID\b|\bSecCertificateCreateWithData\b|\b'
|
||||
rb'SecCertificateCreateFromData\b|\bSecCertificateCopyData\b|\b'
|
||||
rb'SecCertificateAddToKeychain\b|\bSecCertificateGetData\b|\b'
|
||||
rb'SecCertificateCopySubjectSummary\b|\b'
|
||||
rb'SecIdentityCopyCertificate\b|\b'
|
||||
rb'SecIdentityCopyPrivateKey\b|\bSecPKCS12Import\b|\b'
|
||||
rb'SecKeyGeneratePair\b|\b'
|
||||
rb'SecKeyEncrypt\b|\bSecKeyDecrypt\b|\bSecKeyRawSign\b|\b'
|
||||
rb'SecKeyRawVerify\b|\b'
|
||||
rb'SecKeyGetBlockSize\b|\bSecPolicyCopyProperties\b|\b'
|
||||
rb'SecPolicyCreateBasicX509\b|\bSecPolicyCreateSSL\b|\b'
|
||||
rb'SecTrustCopyCustomAnchorCertificates\b|\b'
|
||||
rb'SecTrustCopyExceptions\b|\b'
|
||||
rb'SecTrustCopyProperties\b|\bSecTrustCopyPolicies\b|\b'
|
||||
rb'SecTrustCopyPublicKey\b|\bSecTrustCreateWithCertificates\b|\b'
|
||||
rb'SecTrustEvaluate\b|\bSecTrustEvaluateAsync\b|\b'
|
||||
rb'SecTrustGetCertificateCount\b|\b'
|
||||
rb'SecTrustGetCertificateAtIndex\b|\b'
|
||||
rb'SecTrustGetTrustResult\b|\bSecTrustGetVerifyTime\b|\b'
|
||||
rb'SecTrustSetAnchorCertificates\b|\b'
|
||||
rb'SecTrustSetAnchorCertificatesOnly\b|\b'
|
||||
rb'SecTrustSetExceptions\b|\bSecTrustSetPolicies\b|\b'
|
||||
rb'SecTrustSetVerifyDate\b|\bSecCertificateRef\b|\b'
|
||||
rb'SecIdentityRef\b|\bSecKeyRef\b|\bSecPolicyRef\b|\b'
|
||||
rb'SecTrustRef\b', dat)
|
||||
crypto_algo = list(set(crypto_algo))
|
||||
crypto_algo_s = b', '.join(crypto_algo)
|
||||
if len(crypto_algo_s) > 1:
|
||||
crypto = {
|
||||
'issue': 'Binary make use of the following Crypto API(s)',
|
||||
'level': INFO,
|
||||
'description': ('The binary may use '
|
||||
'the following crypto API(s) {}.').format(
|
||||
crypto_algo_s.decode('utf-8', 'ignore')),
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
}
|
||||
|
||||
weak_hashes = {}
|
||||
weak_hash_algo = re.findall(
|
||||
rb'\bCC_MD2_Init\b|\bCC_MD2_Update\b|\b'
|
||||
rb'CC_MD2_Final\b|\bCC_MD2\b|\bMD2_Init\b|\b'
|
||||
rb'MD2_Update\b|\bMD2_Final\b|\bCC_MD4_Init\b|\b'
|
||||
rb'CC_MD4_Update\b|\bCC_MD4_Final\b|\b'
|
||||
rb'CC_MD4\b|\bMD4_Init\b|\bMD4_Update\b|\b'
|
||||
rb'MD4_Final\b|\bCC_MD5_Init\b|\bCC_MD5_Update'
|
||||
rb'\b|\bCC_MD5_Final\b|\bCC_MD5\b|\bMD5_Init\b|\b'
|
||||
rb'MD5_Update\b|\bMD5_Final\b|\bMD5Init\b|\b'
|
||||
rb'MD5Update\b|\bMD5Final\b|\bCC_SHA1_Init\b|\b'
|
||||
rb'CC_SHA1_Update\b|\b'
|
||||
rb'CC_SHA1_Final\b|\bCC_SHA1\b|\bSHA1_Init\b|\b'
|
||||
rb'SHA1_Update\b|\bSHA1_Final\b', dat)
|
||||
weak_hash_algo = list(set(weak_hash_algo))
|
||||
weak_hash_algo_s = b', '.join(weak_hash_algo)
|
||||
if len(weak_hash_algo_s) > 1:
|
||||
weak_hashes = {
|
||||
'issue': 'Binary make use of the following Weak Hash API(s)',
|
||||
'level': IN_SECURE,
|
||||
'description': (
|
||||
'The binary may use the '
|
||||
'following weak hash API(s) {}.').format(
|
||||
weak_hash_algo_s.decode('utf-8', 'ignore')),
|
||||
'cvss': 3,
|
||||
'cwe': 'CWE-327',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
}
|
||||
|
||||
hashes = {}
|
||||
hash_algo = re.findall(
|
||||
rb'\bCC_SHA224_Init\b|\bCC_SHA224_Update\b|\b'
|
||||
rb'CC_SHA224_Final\b|\bCC_SHA224\b|\b'
|
||||
rb'SHA224_Init\b|\bSHA224_Update\b|\b'
|
||||
rb'SHA224_Final\b|\bCC_SHA256_Init\b|\b'
|
||||
rb'CC_SHA256_Update\b|\bCC_SHA256_Final\b|\b'
|
||||
rb'CC_SHA256\b|\bSHA256_Init\b|\b'
|
||||
rb'SHA256_Update\b|\bSHA256_Final\b|\b'
|
||||
rb'CC_SHA384_Init\b|\bCC_SHA384_Update\b|\b'
|
||||
rb'CC_SHA384_Final\b|\bCC_SHA384\b|\b'
|
||||
rb'SHA384_Init\b|\bSHA384_Update\b|\b'
|
||||
rb'SHA384_Final\b|\bCC_SHA512_Init\b|\b'
|
||||
rb'CC_SHA512_Update\b|\bCC_SHA512_Final\b|\b'
|
||||
rb'CC_SHA512\b|\bSHA512_Init\b|\b'
|
||||
rb'SHA512_Update\b|\bSHA512_Final\b', dat)
|
||||
hash_algo = list(set(hash_algo))
|
||||
hash_algo_s = b', '.join(hash_algo)
|
||||
if len(hash_algo_s) > 1:
|
||||
hashes = {
|
||||
'issue': 'Binary make use of the following Hash API(s)',
|
||||
'level': INFO,
|
||||
'description': ('The binary may use the'
|
||||
' following hash API(s) {}.').format(
|
||||
hash_algo_s.decode('utf-8', 'ignore')),
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
}
|
||||
|
||||
randoms = {}
|
||||
rand_algo = re.findall(rb'\b_srand\b|\b_random\b', dat)
|
||||
rand_algo = list(set(rand_algo))
|
||||
rand_algo_s = b', '.join(rand_algo)
|
||||
if len(rand_algo_s) > 1:
|
||||
randoms = {
|
||||
'issue': 'Binary make use of the insecure Random Function(s)',
|
||||
'level': IN_SECURE,
|
||||
'description': ('The binary may use the following '
|
||||
'insecure Random Function(s) {}.').format(
|
||||
rand_algo_s.decode('utf-8', 'ignore')),
|
||||
'cvss': 3,
|
||||
'cwe': 'CWE-338',
|
||||
'owasp': 'M5: Insufficient Cryptography',
|
||||
}
|
||||
|
||||
logging = {}
|
||||
log = re.findall(rb'\b_NSLog\b', dat)
|
||||
log = list(set(log))
|
||||
log_s = b', '.join(log)
|
||||
if len(log_s) > 1:
|
||||
logging = {
|
||||
'issue': 'Binary make use of Logging Function',
|
||||
'level': INFO,
|
||||
'description': ('The binary may use NSLog'
|
||||
' function for logging.'),
|
||||
'cvss': 7.5,
|
||||
'cwe': 'CWE-532',
|
||||
'owasp': '',
|
||||
}
|
||||
|
||||
malloc = {}
|
||||
mal = re.findall(rb'\b_malloc\b', dat)
|
||||
mal = list(set(mal))
|
||||
mal_s = b', '.join(mal)
|
||||
if len(mal_s) > 1:
|
||||
malloc = {
|
||||
'issue': 'Binary make use of malloc Function',
|
||||
'level': IN_SECURE,
|
||||
'description': ('The binary may use malloc'
|
||||
' function instead of calloc.'),
|
||||
'cvss': 2,
|
||||
'cwe': 'CWE-789',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
}
|
||||
|
||||
debug = {}
|
||||
ptrace = re.findall(rb'\b_ptrace\b', dat)
|
||||
ptrace = list(set(ptrace))
|
||||
ptrace_s = b', '.join(ptrace)
|
||||
if len(ptrace_s) > 1:
|
||||
debug = {
|
||||
'issue': 'Binary calls ptrace Function for anti-debugging.',
|
||||
'level': WARNING,
|
||||
'description': ('The binary may use ptrace function. It can be'
|
||||
' used to detect and prevent debuggers.'
|
||||
'Ptrace is not a public API and Apps that use'
|
||||
' non-public APIs will be rejected'
|
||||
' from AppStore.'),
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': 'M7: Client Code Quality',
|
||||
}
|
||||
otool_dict['anal'] = [pie_flag,
|
||||
ssmash,
|
||||
arc_flag,
|
||||
banned_apis,
|
||||
weak_cryptos,
|
||||
crypto,
|
||||
weak_hashes,
|
||||
hashes,
|
||||
randoms,
|
||||
logging,
|
||||
malloc,
|
||||
debug]
|
||||
return otool_dict
|
||||
tools_dir,
|
||||
'libs',
|
||||
bin_path,
|
||||
bin_dir)
|
||||
bindata.append(get_otool_out(
|
||||
tools_dir,
|
||||
'header',
|
||||
bin_path,
|
||||
bin_dir))
|
||||
bindata.append(get_otool_out(
|
||||
tools_dir,
|
||||
'symbols',
|
||||
bin_path,
|
||||
bin_dir))
|
||||
otool_dict['bindata'] = b'\n'.join(bindata)
|
||||
except Exception:
|
||||
logger.exception('Performing Object Analysis of Binary')
|
||||
logger.exception('Performing Object analysis of binary')
|
||||
return otool_dict
|
||||
|
||||
@@ -2,117 +2,91 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# List taken from
|
||||
# https://developer.apple.com/library/archive/documentation/
|
||||
# General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html
|
||||
|
||||
COCOA_KEYS = {
|
||||
'NETestAppMapping': (
|
||||
('Enables testing of per-app VPN app extensions '
|
||||
'without using an MDM server.'),
|
||||
'normal'),
|
||||
'NFCReaderUsageDescription': (
|
||||
'Access device’s NFC reader.',
|
||||
'dangerous'),
|
||||
'NSAppleMusicUsageDescription': (
|
||||
'Access Apple Media Library.',
|
||||
'dangerous'),
|
||||
'NSBluetoothPeripheralUsageDescription': (
|
||||
'Access Bluetooth Interface.',
|
||||
'dangerous'),
|
||||
'NSCalendarsUsageDescription': (
|
||||
'Access Calendars.',
|
||||
'dangerous'),
|
||||
'NSCameraUsageDescription': (
|
||||
'Access the Camera.',
|
||||
'dangerous'),
|
||||
'NSContactsUsageDescription': (
|
||||
'Access Contacts.',
|
||||
'dangerous'),
|
||||
'NSFaceIDUsageDescription': (
|
||||
'Access the ability to authenticate with Face ID.',
|
||||
'normal'),
|
||||
'NSHealthClinicalHealthRecordsShareUsageDescription': (
|
||||
'Access user’s clinical health records.',
|
||||
'dangerous'),
|
||||
'NSHealthShareUsageDescription': (
|
||||
'Read Health Data.',
|
||||
'dangerous'),
|
||||
'NSHealthUpdateUsageDescription': (
|
||||
'Write Health Data.',
|
||||
'dangerous'),
|
||||
'NSHomeKitUsageDescription': (
|
||||
'Access HomeKit configuration data.',
|
||||
'dangerous'),
|
||||
'NSLocationAlwaysUsageDescription': (
|
||||
'Access location information at all times.',
|
||||
'dangerous'),
|
||||
'NSLocationUsageDescription': (
|
||||
'Access location information at all times (< iOS 8).',
|
||||
'dangerous'),
|
||||
'NSLocationWhenInUseUsageDescription': (
|
||||
'Access location information when app is in the foreground.',
|
||||
'dangerous'),
|
||||
'NSMicrophoneUsageDescription': (
|
||||
'Access microphone.',
|
||||
'dangerous'),
|
||||
'NSMotionUsageDescription': (
|
||||
'Access the device’s accelerometer.',
|
||||
'dangerous'),
|
||||
'NSPhotoLibraryUsageDescription': (
|
||||
'Access the user’s photo library.',
|
||||
'dangerous'),
|
||||
'NSRemindersUsageDescription': (
|
||||
'Access the user’s reminders.',
|
||||
'dangerous'),
|
||||
'NSSiriUsageDescription': (
|
||||
'Allow app to send user data to Siri',
|
||||
'dangerous'),
|
||||
'NSSpeechRecognitionUsageDescription': (
|
||||
'Allow app to send user data to Apple’s speech recognition servers.',
|
||||
'normal'),
|
||||
'NSVideoSubscriberAccountUsageDescription': (
|
||||
'Access the user’s TV provider account.',
|
||||
'normal'),
|
||||
}
|
||||
|
||||
|
||||
def check_permissions(p_list):
|
||||
"""Check the permissions the app requests."""
|
||||
# List taken from
|
||||
# https://developer.apple.com/library/content/
|
||||
# documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html
|
||||
logger.info('Checking Permissions')
|
||||
permissions = []
|
||||
if 'NSAppleMusicUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSAppleMusicUsageDescription',
|
||||
'description': 'Access Apple Media Library.',
|
||||
'reason': p_list['NSAppleMusicUsageDescription'],
|
||||
})
|
||||
if 'NSBluetoothPeripheralUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSBluetoothPeripheralUsageDescription',
|
||||
'description': 'Access Bluetooth Interface.',
|
||||
'reason': p_list['NSBluetoothPeripheralUsageDescription'],
|
||||
})
|
||||
if 'NSCalendarsUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSCalendarsUsageDescription',
|
||||
'description': 'Access Calendars.',
|
||||
'reason': p_list['NSCalendarsUsageDescription'],
|
||||
})
|
||||
if 'NSCameraUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSCameraUsageDescription',
|
||||
'description': 'Access the Camera.',
|
||||
'reason': p_list['NSCameraUsageDescription'],
|
||||
})
|
||||
if 'NSContactsUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSContactsUsageDescription',
|
||||
'description': 'Access Contacts.',
|
||||
'reason': p_list['NSContactsUsageDescription'],
|
||||
})
|
||||
if 'NSHealthShareUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSHealthShareUsageDescription',
|
||||
'description': 'Read Health Data.',
|
||||
'reason': p_list['NSHealthShareUsageDescription'],
|
||||
})
|
||||
if 'NSHealthUpdateUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSHealthUpdateUsageDescription',
|
||||
'description': 'Write Health Data.',
|
||||
'reason': p_list['NSHealthUpdateUsageDescription'],
|
||||
})
|
||||
if 'NSHomeKitUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSHomeKitUsageDescription',
|
||||
'description': 'Access HomeKit configuration data.',
|
||||
'reason': p_list['NSHomeKitUsageDescription'],
|
||||
})
|
||||
if 'NSLocationAlwaysUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSLocationAlwaysUsageDescription',
|
||||
'description': 'Access location information at all times.',
|
||||
'reason': p_list['NSLocationAlwaysUsageDescription'],
|
||||
})
|
||||
if 'NSLocationUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSLocationUsageDescription',
|
||||
'description': ('Access location information'
|
||||
' at all times (< iOS 8).'),
|
||||
'reason': p_list['NSLocationUsageDescription'],
|
||||
})
|
||||
if 'NSLocationWhenInUseUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSLocationWhenInUseUsageDescription',
|
||||
'description': ('Access location information when'
|
||||
' app is in the foreground.'),
|
||||
'reason': p_list['NSLocationWhenInUseUsageDescription'],
|
||||
})
|
||||
if 'NSMicrophoneUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSMicrophoneUsageDescription',
|
||||
'description': 'Access microphone.',
|
||||
'reason': p_list['NSMicrophoneUsageDescription'],
|
||||
})
|
||||
if 'NSMotionUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSMotionUsageDescription',
|
||||
'description': 'Access the device’s accelerometer.',
|
||||
'reason': p_list['NSMotionUsageDescription'],
|
||||
})
|
||||
if 'NSPhotoLibraryUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSPhotoLibraryUsageDescription',
|
||||
'description': 'Access the user’s photo library.',
|
||||
'reason': p_list['NSPhotoLibraryUsageDescription'],
|
||||
})
|
||||
if 'NSRemindersUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSRemindersUsageDescription',
|
||||
'description': 'Access the user’s reminders.',
|
||||
'reason': p_list['NSRemindersUsageDescription'],
|
||||
})
|
||||
if 'NSVideoSubscriberAccountUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSVideoSubscriberAccountUsageDescription',
|
||||
'description': 'Access the user’s TV provider account.',
|
||||
'reason': p_list['NSVideoSubscriberAccountUsageDescription'],
|
||||
})
|
||||
if 'NSFaceIDUsageDescription' in p_list:
|
||||
permissions.append({
|
||||
'name': 'NSFaceIDUsageDescription',
|
||||
'description': 'Access the ability to authenticate with Face ID.',
|
||||
'reason': p_list['NSFaceIDUsageDescription'],
|
||||
})
|
||||
|
||||
for perm, desc in COCOA_KEYS.items():
|
||||
if perm in p_list:
|
||||
permissions.append({
|
||||
'name': perm,
|
||||
'description': desc[0],
|
||||
'status': desc[1],
|
||||
'reason': p_list.get(perm, ''),
|
||||
})
|
||||
return permissions
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
This file contains common iOS security rules used in source code analysis.
|
||||
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
3. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b .regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
c. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
d. single_string - if string1 in input
|
||||
e. string_and - if (string1 in input) and (string2 in input)
|
||||
f. string_or - if (string1 in input) or (string2 in input)
|
||||
g. string_and_or - if (string1 in input) and ((string_or1 in input)
|
||||
or (string_or2 in input))
|
||||
h. string_or_and - if (string1 in input) or ((string_and1 in input)
|
||||
and (string_and2 in input))
|
||||
|
||||
4. level
|
||||
a. high
|
||||
b. warning
|
||||
c. info
|
||||
d. good
|
||||
|
||||
5. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
6. others
|
||||
a. string<no> - string1, string2, string3, string_or1, string_and1
|
||||
b. regex<no> - regex1, regex2, regex3
|
||||
|
||||
"""
|
||||
|
||||
from StaticAnalyzer.views.standards import (
|
||||
CWE,
|
||||
OWASP,
|
||||
OWASP_MSTG,
|
||||
)
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Level,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
COMMON_RULES = [
|
||||
{
|
||||
'desc': 'IP Address disclosure',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 4.3,
|
||||
'cwe': CWE['CWE-200'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('Files may contain hardcoded sensitive'
|
||||
' informations like usernames, passwords, keys etc.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'(password\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(pass\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(username\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(secret\s*=\s*[\'|\"].+[\'|\"]\s{0,5})|'
|
||||
r'(key\s*=\s*[\'|\"].+[\'|\"]\s{0,5})'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.lower,
|
||||
'cvss': 7.4,
|
||||
'cwe': CWE['CWE-312'],
|
||||
'owasp': OWASP['m9'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': ('App uses SQLite Database. '
|
||||
'Sensitive Information should be encrypted.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'sqlite3_exec',
|
||||
'string2': 'sqlite3_finalize',
|
||||
'level': Level.info,
|
||||
'match': Match.string_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': ('User input in "loadHTMLString" '
|
||||
'will result in JavaScript Injection.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'loadHTMLString',
|
||||
'string2': 'webView',
|
||||
'level': Level.warning,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 8.8,
|
||||
'cwe': CWE['CWE-95'],
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-5'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have Jailbreak detection capabilities.',
|
||||
'type': MatchType.string,
|
||||
'string1': '/Applications/Cydia.app',
|
||||
'string2': '/Library/MobileSubstrate/MobileSubstrate.dylib',
|
||||
'string3': '/usr/sbin/sshd',
|
||||
'string4': '/etc/apt',
|
||||
'string5': 'cydia://',
|
||||
'string6': '/var/lib/cydia',
|
||||
'string7': '/Applications/FakeCarrier.app',
|
||||
'string8': '/Applications/Icy.app',
|
||||
'string9': '/Applications/IntelliScreen.app',
|
||||
'string10': '/Applications/SBSettings.app',
|
||||
'string11': ('/Library/MobileSubstrate/DynamicLibraries/'
|
||||
'LiveClock.plist'),
|
||||
'string12': '/System/Library/LaunchDaemons/com.ikey.bbot.plist',
|
||||
'string13': ('/System/Library/LaunchDaemons/'
|
||||
'com.saurik.Cydia.Startup.plist'),
|
||||
'string14': '/etc/ssh/sshd_config',
|
||||
'string15': '/private/var/tmp/cydia.log',
|
||||
'string16': '/usr/libexec/ssh-keysign',
|
||||
'string17': '/Applications/MxTube.app',
|
||||
'string18': '/Applications/RockApp.app',
|
||||
'string19': '/Applications/WinterBoard.app',
|
||||
'string20': '/Applications/blackra1n.app',
|
||||
'string21': '/Library/MobileSubstrate/DynamicLibraries/Veency.plist',
|
||||
'string22': '/private/var/lib/apt',
|
||||
'string23': '/private/var/lib/cydia',
|
||||
'string24': '/private/var/mobile/Library/SBSettings/Themes',
|
||||
'string25': '/private/var/stash',
|
||||
'string26': '/usr/bin/sshd',
|
||||
'string27': '/usr/libexec/sftp-server',
|
||||
'string28': '/var/cache/apt',
|
||||
'string29': '/var/lib/apt',
|
||||
'string30': '/usr/sbin/frida-server',
|
||||
'string31': '/usr/bin/cycript',
|
||||
'string32': '/usr/local/bin/cycript',
|
||||
'string33': '/usr/lib/libcycript.dylib',
|
||||
'string34': 'frida-server',
|
||||
'string35': '/etc/apt/sources.list.d/electra.list',
|
||||
'string36': '/etc/apt/sources.list.d/sileo.sources',
|
||||
'string37': '/.bootstrapped_electra',
|
||||
'string38': '/usr/lib/libjailbreak.dylib',
|
||||
'string39': '/jb/lzma',
|
||||
'string40': '/.cydia_no_stash',
|
||||
'string41': '/.installed_unc0ver',
|
||||
'string42': '/jb/offsets.plist',
|
||||
'string43': '/usr/share/jailbreak/injectme.plist',
|
||||
'string44': '/Library/MobileSubstrate/MobileSubstrate.dylib',
|
||||
'string45': '/usr/libexec/cydia/firmware.sh',
|
||||
'string46': '/private/var/cache/apt/',
|
||||
'string47': '/Library/MobileSubstrate/CydiaSubstrate.dylib',
|
||||
'level': Level.good,
|
||||
'match': Match.string_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-1'],
|
||||
},
|
||||
]
|
||||
Executable
+137
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
This files contains rules that detects usage of API.
|
||||
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
3. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b .regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
c. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
d. single_string - if string1 in input
|
||||
e. string_and - if (string1 in input) and (string2 in input)
|
||||
f. string_or - if (string1 in input) or (string2 in input)
|
||||
g. string_and_or - if (string1 in input) and ((string2 in input)
|
||||
or (string3 in input))
|
||||
h. string_or_and - if (string1 in input) or ((string2 in input)
|
||||
and (string3 in input))
|
||||
|
||||
4. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
5. others
|
||||
a. string<no> - string1, string2, string3, string_or1, string_and1
|
||||
b. regex<no> - regex1, regex2, regex3
|
||||
|
||||
"""
|
||||
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
CODE_APIS = [
|
||||
{
|
||||
'desc': 'Network Calls',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'URLSession|CFStream|NSStream',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Keychain Access.',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': (r'Keychain|SecItemAdd|SecItemUpdate'
|
||||
r'SecItemCopy| kSecAttr'),
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'WebView Component',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'UIWebView|WKWebView',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'SFSafariViewController Component',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'SFSafariViewController',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Encryption API',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.single_regex,
|
||||
'regex1': r'CommonCrypto|CryptoKit',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'WebView Load Request',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'loadRequest',
|
||||
'string2': 'webView',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'WebView Load HTML String',
|
||||
'type': MatchType.string,
|
||||
'match': Match.string_and,
|
||||
'string1': 'loadHTMLString',
|
||||
'string2': 'webView',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Cookie Storage',
|
||||
'type': MatchType.regex,
|
||||
'match': Match.regex_and,
|
||||
'regex1': r'HTTPCookieStorage',
|
||||
'regex2': r'shared|sharedHTTPCookieStorage',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'UIPasteboard',
|
||||
'type': MatchType.string,
|
||||
'match': Match.single_string,
|
||||
'string1': 'UIPasteboard',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'UserDefaults',
|
||||
'type': MatchType.string,
|
||||
'match': Match.single_string,
|
||||
'string1': 'UserDefaults',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'CoreData',
|
||||
'type': MatchType.string,
|
||||
'match': Match.single_string,
|
||||
'string1': 'CoreData',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'Local Authentication Framework',
|
||||
'type': MatchType.string,
|
||||
'match': Match.single_string,
|
||||
'string1': 'LAContext',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
{
|
||||
'desc': 'UIActivity Sharing',
|
||||
'type': MatchType.string,
|
||||
'match': Match.single_string,
|
||||
'string1': 'UIActivity',
|
||||
'input_case': InputCase.exact,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
This file contains IPA security rules used in binary analysis.
|
||||
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. detailed_desc - Detailed description. If a {} placeholder is used, captures
|
||||
will be inserted to detailed description on rule match.
|
||||
formatting is only supported with regex
|
||||
|
||||
3. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
4. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b. single_string - if string1 in input
|
||||
|
||||
5. level
|
||||
a. high
|
||||
b. warning
|
||||
c. info
|
||||
d. good
|
||||
|
||||
6. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
7. others
|
||||
a. string1
|
||||
b. regex2
|
||||
|
||||
8. conditional - Conditional rules to match the opposite case.
|
||||
The opposite case must contain a dict of desc,
|
||||
detailed_desc, level, cvss, cwe, owasp, and,
|
||||
owasp-mstg
|
||||
"""
|
||||
from StaticAnalyzer.views.standards import (
|
||||
CWE,
|
||||
OWASP,
|
||||
OWASP_MSTG,
|
||||
)
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Level,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
IPA_RULES = [
|
||||
{
|
||||
'desc': 'Binary make use of insecure API(s)',
|
||||
'detailed_desc': (
|
||||
'The binary may contain'
|
||||
' the following insecure API(s) {}.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (
|
||||
rb'\b_alloca\b|\b_gets\b|\b_memcpy\b|\b_printf\b|\b_scanf\b|'
|
||||
rb'\b_sprintf\b|\b_sscanf\b|\b_strcat\b|'
|
||||
rb'\bStrCat\b|\b_strcpy\b|\bStrCpy\b|\b_strlen\b|\bStrLen\b|'
|
||||
rb'\b_strncat\b|\bStrNCat\b|\b_strncpy\b|'
|
||||
rb'\bStrNCpy\b|\b_strtok\b|\b_swprintf\b|\b_vsnprintf\b|'
|
||||
rb'\b_vsprintf\b|\b_vswprintf\b|\b_wcscat\b|\b_wcscpy\b|'
|
||||
rb'\b_wcslen\b|\b_wcsncat\b|\b_wcsncpy\b|\b_wcstok\b|\b_wmemcpy\b|'
|
||||
rb'\b_fopen\b|\b_chmod\b|\b_chown\b|\b_stat\b|\b_mktemp\b'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 6,
|
||||
'cwe': CWE['CWE-676'],
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': OWASP_MSTG['code-8'],
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of some weak Crypto API(s)',
|
||||
'detailed_desc': (
|
||||
'The binary may use the'
|
||||
' following weak crypto API(s) {}.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (
|
||||
rb'\bkCCAlgorithmDES\b|'
|
||||
rb'\bkCCAlgorithm3DES\b|'
|
||||
rb'\bkCCAlgorithmRC2\b|'
|
||||
rb'\bkCCAlgorithmRC4\b|'
|
||||
rb'\bkCCOptionECBMode\b|'
|
||||
rb'\bkCCOptionCBCMode\b'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 3,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of the following Crypto API(s)',
|
||||
'detailed_desc': (
|
||||
'The binary may use '
|
||||
'the following crypto API(s) {}.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (
|
||||
rb'\bCCKeyDerivationPBKDF\b|\bCCCryptorCreate\b|\b'
|
||||
rb'CCCryptorCreateFromData\b|\b'
|
||||
rb'CCCryptorRelease\b|\bCCCryptorUpdate\b|\bCCCryptorFinal\b|\b'
|
||||
rb'CCCryptorGetOutputLength\b|\bCCCryptorReset\b|\b'
|
||||
rb'CCCryptorRef\b|\bkCCEncrypt\b|\b'
|
||||
rb'kCCDecrypt\b|\bkCCAlgorithmAES128\b|\bkCCKeySizeAES128\b|\b'
|
||||
rb'kCCKeySizeAES192\b|\b'
|
||||
rb'kCCKeySizeAES256\b|\bkCCAlgorithmCAST\b|\b'
|
||||
rb'SecCertificateGetTypeID\b|\b'
|
||||
rb'SecIdentityGetTypeID\b|\bSecKeyGetTypeID\b|\b'
|
||||
rb'SecPolicyGetTypeID\b|\b'
|
||||
rb'SecTrustGetTypeID\b|\bSecCertificateCreateWithData\b|\b'
|
||||
rb'SecCertificateCreateFromData\b|\bSecCertificateCopyData\b|\b'
|
||||
rb'SecCertificateAddToKeychain\b|\bSecCertificateGetData\b|\b'
|
||||
rb'SecCertificateCopySubjectSummary\b|\b'
|
||||
rb'SecIdentityCopyCertificate\b|\b'
|
||||
rb'SecIdentityCopyPrivateKey\b|\bSecPKCS12Import\b|\b'
|
||||
rb'SecKeyGeneratePair\b|\b'
|
||||
rb'SecKeyEncrypt\b|\bSecKeyDecrypt\b|\bSecKeyRawSign\b|\b'
|
||||
rb'SecKeyRawVerify\b|\b'
|
||||
rb'SecKeyGetBlockSize\b|\bSecPolicyCopyProperties\b|\b'
|
||||
rb'SecPolicyCreateBasicX509\b|\bSecPolicyCreateSSL\b|\b'
|
||||
rb'SecTrustCopyCustomAnchorCertificates\b|\b'
|
||||
rb'SecTrustCopyExceptions\b|\b'
|
||||
rb'SecTrustCopyProperties\b|\bSecTrustCopyPolicies\b|\b'
|
||||
rb'SecTrustCopyPublicKey\b|\bSecTrustCreateWithCertificates\b|\b'
|
||||
rb'SecTrustEvaluate\b|\bSecTrustEvaluateAsync\b|\b'
|
||||
rb'SecTrustGetCertificateCount\b|\b'
|
||||
rb'SecTrustGetCertificateAtIndex\b|\b'
|
||||
rb'SecTrustGetTrustResult\b|\bSecTrustGetVerifyTime\b|\b'
|
||||
rb'SecTrustSetAnchorCertificates\b|\b'
|
||||
rb'SecTrustSetAnchorCertificatesOnly\b|\b'
|
||||
rb'SecTrustSetExceptions\b|\bSecTrustSetPolicies\b|\b'
|
||||
rb'SecTrustSetVerifyDate\b|\bSecCertificateRef\b|\b'
|
||||
rb'SecIdentityRef\b|\bSecKeyRef\b|\bSecPolicyRef\b|\b'
|
||||
rb'SecTrustRef\b'),
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': '',
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of some weak Hashing API(s)',
|
||||
'detailed_desc': (
|
||||
'The binary may use the '
|
||||
'following weak hash API(s) {}.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (
|
||||
rb'\bCC_MD2_Init\b|\bCC_MD2_Update\b|\b'
|
||||
rb'CC_MD2_Final\b|\bCC_MD2\b|\bMD2_Init\b|\b'
|
||||
rb'MD2_Update\b|\bMD2_Final\b|\bCC_MD4_Init\b|\b'
|
||||
rb'CC_MD4_Update\b|\bCC_MD4_Final\b|\b'
|
||||
rb'CC_MD4\b|\bMD4_Init\b|\bMD4_Update\b|\b'
|
||||
rb'MD4_Final\b|\bCC_MD5_Init\b|\bCC_MD5_Update'
|
||||
rb'\b|\bCC_MD5_Final\b|\bCC_MD5\b|\bMD5_Init\b|\b'
|
||||
rb'MD5_Update\b|\bMD5_Final\b|\bMD5Init\b|\b'
|
||||
rb'MD5Update\b|\bMD5Final\b|\bCC_SHA1_Init\b|\b'
|
||||
rb'CC_SHA1_Update\b|\b'
|
||||
rb'CC_SHA1_Final\b|\bCC_SHA1\b|\bSHA1_Init\b|\b'
|
||||
rb'SHA1_Update\b|\bSHA1_Final\b'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 3,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of the following Hash API(s)',
|
||||
'detailed_desc': (
|
||||
'The binary may use the'
|
||||
' following hash API(s) {}.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (
|
||||
rb'\bCC_SHA224_Init\b|\bCC_SHA224_Update\b|\b'
|
||||
rb'CC_SHA224_Final\b|\bCC_SHA224\b|\b'
|
||||
rb'SHA224_Init\b|\bSHA224_Update\b|\b'
|
||||
rb'SHA224_Final\b|\bCC_SHA256_Init\b|\b'
|
||||
rb'CC_SHA256_Update\b|\bCC_SHA256_Final\b|\b'
|
||||
rb'CC_SHA256\b|\bSHA256_Init\b|\b'
|
||||
rb'SHA256_Update\b|\bSHA256_Final\b|\b'
|
||||
rb'CC_SHA384_Init\b|\bCC_SHA384_Update\b|\b'
|
||||
rb'CC_SHA384_Final\b|\bCC_SHA384\b|\b'
|
||||
rb'SHA384_Init\b|\bSHA384_Update\b|\b'
|
||||
rb'SHA384_Final\b|\bCC_SHA512_Init\b|\b'
|
||||
rb'CC_SHA512_Update\b|\bCC_SHA512_Final\b|\b'
|
||||
rb'CC_SHA512\b|\bSHA512_Init\b|\b'
|
||||
rb'SHA512_Update\b|\bSHA512_Final\b'),
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': '',
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of the insecure Random function(s)',
|
||||
'detailed_desc': (
|
||||
'The binary may use the following '
|
||||
'insecure Random function(s) {}.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': rb'\b_srand\b|\b_random\b',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 3,
|
||||
'cwe': CWE['CWE-330'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-6'],
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of Logging function',
|
||||
'detailed_desc': (
|
||||
'The binary may use {}'
|
||||
' function for logging.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': rb'\b_NSLog\b',
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': CWE['CWE-532'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'Binary make use of malloc function',
|
||||
'detailed_desc': (
|
||||
'The binary may use {}'
|
||||
' function instead of calloc.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': rb'\b_malloc\b',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 2,
|
||||
'cwe': CWE['CWE-789'],
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': OWASP_MSTG['code-8'],
|
||||
},
|
||||
{
|
||||
'desc': 'Binary calls ptrace() function for anti-debugging.',
|
||||
'detailed_desc': (
|
||||
'The binary may use ptrace() function. It can be'
|
||||
' used to detect and prevent debuggers.'
|
||||
' Ptrace is not a public API and apps that'
|
||||
' use non-public APIs will be rejected'
|
||||
' from AppStore.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': rb'\b_ptrace\b',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': OWASP_MSTG['resilience-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'fPIE -pie flag is Found',
|
||||
'detailed_desc': (
|
||||
'App is compiled with Position Independent '
|
||||
'Executable (PIE) flag. This enables Address'
|
||||
' Space Layout Randomization (ASLR), a memory'
|
||||
' protection mechanism for'
|
||||
' exploit mitigation.'),
|
||||
'type': MatchType.string,
|
||||
'string1': b'PIE',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
'conditional': {
|
||||
'desc': 'fPIE -pie flag is Found',
|
||||
'detailed_desc': (
|
||||
'with Position Independent Executable (PIE) '
|
||||
'flag. So Address Space Layout Randomization '
|
||||
'(ASLR) is missing. ASLR is a memory '
|
||||
'protection mechanism for '
|
||||
'exploit mitigation.'),
|
||||
'level': Level.high,
|
||||
'cvss': 2,
|
||||
'cwe': CWE['CWE-119'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
},
|
||||
},
|
||||
{
|
||||
'desc': 'fstack-protector-all flag is Found',
|
||||
'detailed_desc': (
|
||||
'App is compiled with Stack Smashing Protector'
|
||||
' (SSP) flag and is having protection against'
|
||||
' Stack Overflows/Stack Smashing Attacks.'),
|
||||
'type': MatchType.string,
|
||||
'string1': b'stack_chk_guard',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
'conditional': {
|
||||
'desc': 'fstack-protector-all flag is not Found',
|
||||
'detailed_desc': (
|
||||
'App is not compiled with Stack Smashing '
|
||||
'Protector (SSP) flag. It is vulnerable to'
|
||||
'Stack Overflows/Stack Smashing Attacks.'),
|
||||
'level': Level.high,
|
||||
'cvss': 2,
|
||||
'cwe': CWE['CWE-119'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
},
|
||||
},
|
||||
{
|
||||
'desc': 'fobjc-arc flag is Found',
|
||||
'detailed_desc': (
|
||||
'App is compiled with Automatic Reference '
|
||||
'Counting (ARC) flag. ARC is a compiler '
|
||||
'feature that provides automatic memory '
|
||||
'management of Objective-C objects and is an '
|
||||
'exploit mitigation mechanism against memory '
|
||||
'corruption vulnerabilities.'),
|
||||
'type': MatchType.string,
|
||||
'string1': b'_objc_release',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
'conditional': {
|
||||
'desc': 'fobjc-arc flag is not Found',
|
||||
'detailed_desc': (
|
||||
'App is not compiled with Automatic Reference '
|
||||
'Counting (ARC) flag. ARC is a compiler '
|
||||
'feature that provides automatic memory '
|
||||
'management of Objective-C objects and '
|
||||
'protects from memory corruption '
|
||||
'vulnerabilities.'),
|
||||
'level': Level.high,
|
||||
'cvss': 2,
|
||||
'cwe': CWE['CWE-119'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
},
|
||||
},
|
||||
{
|
||||
'desc': 'Binary uses WebView Component.',
|
||||
'detailed_desc': 'The binary may use UIWebView Component.',
|
||||
'type': MatchType.string,
|
||||
'string1': b'UIWebView',
|
||||
'level': Level.info,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['code-9'],
|
||||
},
|
||||
]
|
||||
Executable
+323
@@ -0,0 +1,323 @@
|
||||
"""
|
||||
This file contains Objective-C security rules used in source code analysis.
|
||||
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
3. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b .regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
c. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
d. single_string - if string1 in input
|
||||
e. string_and - if (string1 in input) and (string2 in input)
|
||||
f. string_or - if (string1 in input) or (string2 in input)
|
||||
g. string_and_or - if (string1 in input) and ((string_or1 in input)
|
||||
or (string_or2 in input))
|
||||
h. string_or_and - if (string1 in input) or ((string_and1 in input)
|
||||
and (string_and2 in input))
|
||||
|
||||
4. level
|
||||
a. high
|
||||
b. warning
|
||||
c. info
|
||||
d. good
|
||||
|
||||
5. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
6. others
|
||||
a. string<no> - string1, string2, string3, string_or1, string_and1
|
||||
b. regex<no> - regex1, regex2, regex3
|
||||
|
||||
"""
|
||||
from StaticAnalyzer.views.standards import (
|
||||
CWE,
|
||||
OWASP,
|
||||
OWASP_MSTG,
|
||||
)
|
||||
from StaticAnalyzer.views.ios.rules import common_rules
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Level,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
OBJC_RULES = common_rules.COMMON_RULES + [
|
||||
{
|
||||
'desc': ('The App may contain banned API(s). '
|
||||
'These API(s) are insecure and must not be used.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'strcpy|memcpy|strcat|strncat|'
|
||||
r'strncpy|sprintf|vsprintf|gets'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 2.2,
|
||||
'cwe': CWE['CWE-676'],
|
||||
'owasp': OWASP['m7'],
|
||||
'owasp-mstg': OWASP_MSTG['code-8'],
|
||||
},
|
||||
{
|
||||
'desc': ('App allows self signed or invalid '
|
||||
'SSL certificates. App is vulnerable to MITM attacks.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'canAuthenticateAgainstProtectionSpace|'
|
||||
r'continueWithoutCredentialForAuthenticationChallenge|'
|
||||
r'kCFStreamSSLAllowsExpiredCertificates|'
|
||||
r'kCFStreamSSLAllowsAnyRoot|'
|
||||
r'kCFStreamSSLAllowsExpiredRoots|'
|
||||
r'validatesSecureCertificate\s*=\s*(no|NO)|'
|
||||
r'allowInvalidCertificates\s*=\s*(YES|yes)'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': CWE['CWE-295'],
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('UIWebView in App ignore SSL errors and accept'
|
||||
' any SSL Certificate. App is vulnerable to MITM attacks.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'setAllowsAnyHTTPSCertificate:\s*YES|'
|
||||
r'allowsAnyHTTPSCertificateForHost|'
|
||||
r'loadingUnvalidatedHTTPSPage\s*=\s*(YES|yes)'),
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': CWE['CWE-295'],
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('The App logs information. '
|
||||
'Sensitive information should never be logged.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'NSLog|NSAssert|fprintf|fprintf|Logging',
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': CWE['CWE-532'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('This app listens to Clipboard changes. '
|
||||
'Some malwares also listen to Clipboard changes.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': (r'UIPasteboardChangedNotification|'
|
||||
r'generalPasteboard\]\.string'),
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['platform-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('Untrusted user input to "NSTemporaryDirectory()"'
|
||||
' will result in path traversal vulnerability.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'NSTemporaryDirectory(),',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': CWE['CWE-22'],
|
||||
'owasp': OWASP['m10'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('File is stored in an encrypted format on '
|
||||
'disk and cannot be read from or written to '
|
||||
'while the device is locked or booting.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'NSFileProtectionComplete',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': ('File is stored in an encrypted format '
|
||||
'on disk after it is closed.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'NSFileProtectionCompleteUnlessOpen',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': ('File is stored in an encrypted format '
|
||||
'on disk and cannot be accessed until after '
|
||||
'the device has booted.'),
|
||||
'type': MatchType.string,
|
||||
'string1': (
|
||||
'NSFileProtectionComplete'
|
||||
'UntilFirstUserAuthentication'),
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': ('The file has no special protections '
|
||||
'associated with it.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'NSFileProtectionNone',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 4.3,
|
||||
'cwe': CWE['CWE-311'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'SFAntiPiracy Jailbreak checks found',
|
||||
'type': MatchType.string,
|
||||
'string1': 'SFAntiPiracy.h',
|
||||
'string2': 'SFAntiPiracy',
|
||||
'string3': 'isJailbroken',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-1'],
|
||||
},
|
||||
{
|
||||
'desc': 'SFAntiPiracy Piracy checks found',
|
||||
'type': MatchType.string,
|
||||
'string1': 'SFAntiPiracy.h',
|
||||
'string2': 'SFAntiPiracy',
|
||||
'string3': 'isPirated',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'MD5 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.string,
|
||||
'string1': 'CommonDigest.h',
|
||||
'string2': 'CC_MD5',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.4,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'SHA1 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.string,
|
||||
'string1': 'CommonDigest.h',
|
||||
'string2': 'CC_SHA1',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('The App uses ECB mode in Cryptographic encryption algorithm.'
|
||||
' ECB mode is known to be weak as it results in the same'
|
||||
' ciphertext for identical blocks of plaintext.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'kCCOptionECBMode',
|
||||
'string2': 'kCCAlgorithmAES',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'The App has anti-debugger code using ptrace() ',
|
||||
'type': MatchType.string,
|
||||
'string1': 'ptrace_ptr',
|
||||
'string2': 'PT_DENY_ATTACH',
|
||||
'level': Level.info,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-2'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App has anti-debugger code using Mach Exception Ports.',
|
||||
'type': MatchType.string,
|
||||
'string1': 'mach/mach_init.h',
|
||||
'string_or1': 'MACH_PORT_VALID',
|
||||
'string_or2': 'mach_task_self()',
|
||||
'level': Level.info,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('This App copies data to clipboard. Sensitive data should'
|
||||
' not be copied to clipboard as other applications'
|
||||
' can access it.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'UITextField',
|
||||
'string_or1': '@select(cut:)',
|
||||
'string_or2': '@select(copy:)',
|
||||
'level': Level.info,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-10'],
|
||||
},
|
||||
{
|
||||
'desc': ('App uses Realm Database. '
|
||||
'Sensitive Information should be encrypted.'),
|
||||
'type': MatchType.string,
|
||||
'string1': '[realm transactionWithBlock:',
|
||||
'level': Level.info,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,435 @@
|
||||
"""
|
||||
This file contains Swift security rules used in source code analysis.
|
||||
|
||||
Rule Format.
|
||||
|
||||
1. desc - Description of the findings
|
||||
|
||||
2. type
|
||||
a. string
|
||||
b. regex
|
||||
|
||||
3. match
|
||||
a. single_regex - if re.findall(regex1, input)
|
||||
b .regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
c. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
d. single_string - if string1 in input
|
||||
e. string_and - if (string1 in input) and (string2 in input)
|
||||
f. string_or - if (string1 in input) or (string2 in input)
|
||||
g. string_and_or - if (string1 in input) and ((string_or1 in input)
|
||||
or (string_or2 in input))
|
||||
h. string_or_and - if (string1 in input) or ((string_and1 in input)
|
||||
and (string_and2 in input))
|
||||
|
||||
4. level
|
||||
a. high
|
||||
b. warning
|
||||
c. info
|
||||
d. good
|
||||
|
||||
5. input_case
|
||||
a. upper
|
||||
b. lower
|
||||
c. exact
|
||||
|
||||
6. others
|
||||
a. string<no> - string1, string2, string3, string_or1, string_and1
|
||||
b. regex<no> - regex1, regex2, regex3
|
||||
|
||||
"""
|
||||
from StaticAnalyzer.views.standards import (
|
||||
CWE,
|
||||
OWASP,
|
||||
OWASP_MSTG,
|
||||
)
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Level,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
from StaticAnalyzer.views.ios.rules import common_rules
|
||||
|
||||
SWIFT_RULES = common_rules.COMMON_RULES + [
|
||||
{
|
||||
'desc': ('The App logs information. '
|
||||
'Sensitive information should never be logged.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(print|NSLog|os_log|OSLog|os_signpost)\(.*\)',
|
||||
'level': Level.info,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': CWE['CWE-532'],
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'SHA1 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(?i)SHA1\(',
|
||||
'regex2': r'CC_SHA1\(',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'MD2 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(?i)MD2\(',
|
||||
'regex2': r'CC_MD2\(',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'MD4 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(?i)MD4\(',
|
||||
'regex2': r'CC_MD4\(',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'MD5 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(?i)MD5\(',
|
||||
'regex2': r'CC_MD5\(',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'MD6 is a weak hash known to have hash collisions.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(?i)MD6\(',
|
||||
'regex2': r'CC_MD6\(',
|
||||
'level': Level.high,
|
||||
'match': Match.regex_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 5.9,
|
||||
'cwe': CWE['CWE-327'],
|
||||
'owasp': OWASP['m5'],
|
||||
'owasp-mstg': OWASP_MSTG['crypto-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have custom keyboards disabled.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'extensionPointIdentifier == .*\.keyboard',
|
||||
'level': Level.good,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['platform-11'],
|
||||
},
|
||||
{
|
||||
'desc': ('Keyboard cache should be disabled for all '
|
||||
'sensitve data inputs.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'.autocorrectionType = .no',
|
||||
'level': Level.good,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-5'],
|
||||
},
|
||||
{
|
||||
'desc': ('It is recommended to use WKWebView instead '
|
||||
'of SFSafariViewController or UIViewView.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'UIViewView|SFSafariViewController',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-5'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have Reverse enginering detection capabilities.',
|
||||
'type': MatchType.string,
|
||||
'string1': '\"FridaGadget\"',
|
||||
'string2': '\"cynject\"',
|
||||
'string3': '\"libcycript\"',
|
||||
'string4': '\"/usr/sbin/frida-server\"',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have Emulator detection capabilities.',
|
||||
'type': MatchType.string,
|
||||
'string1': '\"SIMULATOR_DEVICE_NAME\"',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['resilience-5'],
|
||||
},
|
||||
{
|
||||
'desc': ('Biometric authentication should be based '
|
||||
'on Keychain, not based on bool.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'\.evaluatePolicy\(.deviceOwnerAuthentication',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['auth-8'],
|
||||
},
|
||||
{
|
||||
'desc': 'The file has no special protections associated with it.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'(?i)\.noFileProtection',
|
||||
'level': Level.high,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 4.3,
|
||||
'cwe': CWE['CWE-311'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-1'],
|
||||
},
|
||||
{
|
||||
'desc': ('This application uses UIPasteboard, improper use '
|
||||
'of this class can lead to security issues.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'UIPasteboard',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-4'],
|
||||
},
|
||||
{
|
||||
'desc': 'Usage of generalPasteboard should be avoided.',
|
||||
'type': MatchType.string,
|
||||
'string1': 'UIPasteboard.generalPasteboard',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('The app should not export sensitive functionality via '
|
||||
'custom URL schemes, unless these '
|
||||
'mechanisms are properly protected.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'CFBundleURLSchemes',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-3'],
|
||||
},
|
||||
{
|
||||
'desc': 'Some UI controls have secure text entry configured.',
|
||||
'type': MatchType.string,
|
||||
'string1': '.secureTextEntry = true',
|
||||
'level': Level.good,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-5'],
|
||||
},
|
||||
{
|
||||
'desc': 'This App may have Certificate Pinning mechanism.',
|
||||
'type': MatchType.string,
|
||||
'string1': 'PinnedCertificatesTrustEvaluator',
|
||||
'string2': 'TrustKit.initSharedInstance',
|
||||
'level': Level.good,
|
||||
'match': Match.string_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': CWE['CWE-295'],
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-4'],
|
||||
},
|
||||
{
|
||||
'desc': ('App uses Realm Database. '
|
||||
'Sensitive Information should be encrypted.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'realm.write',
|
||||
'level': Level.info,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0,
|
||||
'cwe': CWE['CWE-311'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': ('App uses CoreData Database. '
|
||||
'Sensitive Information should be encrypted.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'NSManagedObjectContext',
|
||||
'string2': '.save()',
|
||||
'level': Level.info,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': CWE['CWE-311'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': 'Used Realm database has configured encryption.',
|
||||
'type': MatchType.string,
|
||||
'string1': 'Realm.Configuration(encryptionKey:',
|
||||
'string2': 'Realm(configuration:',
|
||||
'level': Level.good,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': CWE['CWE-311'],
|
||||
'owasp': OWASP['m2'],
|
||||
'owasp-mstg': OWASP_MSTG['storage-14'],
|
||||
},
|
||||
{
|
||||
'desc': 'Copy feature may be disabled in some UI controls.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'canPerformAction',
|
||||
'regex2': r'UIResponderStandardEditActions\.copy|\.copy',
|
||||
'level': Level.info,
|
||||
'match': Match.regex_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': '',
|
||||
},
|
||||
{
|
||||
'desc': 'Opening App with URLs can lead to potencial security leaks.',
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'func application\(.*open url: URL',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': CWE['CWE-939'],
|
||||
'owasp': OWASP['m1'],
|
||||
'owasp-mstg': OWASP_MSTG['platform-3'],
|
||||
},
|
||||
{
|
||||
'desc': ('Sensitive data shouldn\'t be included in backups generated '
|
||||
'by the mobile operating system.'),
|
||||
'type': MatchType.regex,
|
||||
'regex1': r'kSecAttrAccessible[a-zA-Z]*ThisDeviceOnly',
|
||||
'level': Level.good,
|
||||
'match': Match.single_regex,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['storage-8'],
|
||||
},
|
||||
{
|
||||
'desc': ('JavaScript should be disabled in WebViews unless '
|
||||
'explicitly required.'),
|
||||
'type': MatchType.string,
|
||||
'string1': 'WKWebView',
|
||||
'string2': '.javaScriptEnabled = false',
|
||||
'level': Level.warning,
|
||||
'match': Match.string_and_not,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': '',
|
||||
'owasp-mstg': OWASP_MSTG['platform-5'],
|
||||
},
|
||||
{
|
||||
'desc': ('TLS 1.3 should be used. '
|
||||
'Detected old version'),
|
||||
'type': MatchType.string,
|
||||
'string1': '.TLSMinimumSupportedProtocolVersion',
|
||||
'string_or1': 'tls_protocol_version_t.TLSv10',
|
||||
'string_or2': 'tls_protocol_version_t.TLSv11',
|
||||
'level': Level.high,
|
||||
'match': Match.string_and_or,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 7.5,
|
||||
'cwe': CWE['CWE-757'],
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('TLS 1.3 should be used. '
|
||||
'Detected old version - TLS 1.2.'),
|
||||
'type': MatchType.string,
|
||||
'string1': '.TLSMinimumSupportedProtocolVersion',
|
||||
'string2': 'tls_protocol_version_t.TLSv12',
|
||||
'level': Level.warning,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('DTLS 1.2 should be used. '
|
||||
'Detected old version - DTLS 1.0.'),
|
||||
'type': MatchType.string,
|
||||
'string1': '.TLSMinimumSupportedProtocolVersion',
|
||||
'string2': 'tls_protocol_version_t.DTLSv10',
|
||||
'level': Level.warning,
|
||||
'match': Match.string_and,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-2'],
|
||||
},
|
||||
{
|
||||
'desc': ('Use of deprecated property tlsMinimumSupportedProtocol. '
|
||||
'To avoid potential security risks, use '
|
||||
'tlsMinimumSupportedProtocolVersion'),
|
||||
'type': MatchType.string,
|
||||
'string1': '.tlsMinimumSupportedProtocol',
|
||||
'level': Level.warning,
|
||||
'match': Match.single_string,
|
||||
'input_case': InputCase.exact,
|
||||
'cvss': 0.0,
|
||||
'cwe': '',
|
||||
'owasp': OWASP['m3'],
|
||||
'owasp-mstg': OWASP_MSTG['network-2'],
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,264 @@
|
||||
"""Rule Matchers."""
|
||||
import re
|
||||
import logging
|
||||
from enum import Enum
|
||||
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
InputCase,
|
||||
Match,
|
||||
MatchType,
|
||||
)
|
||||
|
||||
from django.utils.html import escape
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _MatcherType(Enum):
|
||||
code = 1
|
||||
api = 2
|
||||
|
||||
|
||||
def _get_list_match_items(ruleset):
|
||||
"""Get List of Match item."""
|
||||
match_list = []
|
||||
i = 1
|
||||
identifier = ruleset['type'].value
|
||||
if ruleset['match'] == Match.string_and_or:
|
||||
identifier = 'string_or'
|
||||
elif ruleset['match'] == Match.string_or_and:
|
||||
identifier = 'string_and'
|
||||
while identifier + str(i) in ruleset:
|
||||
match_list.append(ruleset[identifier + str(i)])
|
||||
i = i + 1
|
||||
if not (identifier + str(i)) in ruleset:
|
||||
break
|
||||
return match_list
|
||||
|
||||
|
||||
def _add_code_findings(findings, file_path, rule):
|
||||
"""Add Code Analysis Findings."""
|
||||
desc = rule['desc']
|
||||
if desc in findings:
|
||||
tmp_list = findings[desc]['path']
|
||||
if escape(file_path) not in tmp_list:
|
||||
tmp_list.append(escape(file_path))
|
||||
findings[desc]['path'] = tmp_list
|
||||
else:
|
||||
findings[desc] = {'path': [escape(file_path)],
|
||||
'level': rule['level'].value,
|
||||
'cvss': rule['cvss'],
|
||||
'cwe': rule['cwe'],
|
||||
'owasp': rule['owasp'],
|
||||
'owasp-mstg': rule['owasp-mstg']}
|
||||
|
||||
|
||||
def _add_apis_findings(findings, file_path, rule):
|
||||
"""Add API Findings."""
|
||||
desc = rule['desc']
|
||||
if desc in findings:
|
||||
tmp_list = findings[desc]['path']
|
||||
if escape(file_path) not in tmp_list:
|
||||
tmp_list.append(escape(file_path))
|
||||
findings[desc]['path'] = tmp_list
|
||||
else:
|
||||
findings[desc] = {'path': [escape(file_path)]}
|
||||
|
||||
|
||||
def _add_findings(matcher, findings, file_path, rule):
|
||||
if matcher == _MatcherType.code:
|
||||
_add_code_findings(findings, file_path, rule)
|
||||
elif matcher == _MatcherType.api:
|
||||
_add_apis_findings(findings, file_path, rule)
|
||||
|
||||
|
||||
def _match_regex_rule(rule, matcher, findings, perms, data,
|
||||
file_path, tmp_data):
|
||||
if rule['match'] == Match.single_regex:
|
||||
if re.findall(rule['regex1'], tmp_data):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.regex_and:
|
||||
and_match_rgx = True
|
||||
match_list = _get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if bool(re.findall(match, tmp_data)) is False:
|
||||
and_match_rgx = False
|
||||
break
|
||||
if and_match_rgx:
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.regex_or:
|
||||
match_list = _get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if re.findall(match, tmp_data):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
break
|
||||
elif rule['match'] == Match.regex_and_perm:
|
||||
if (rule['perm'] in perms
|
||||
and re.findall(rule['regex1'], tmp_data)):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
else:
|
||||
logger.error('Code Regex Rule Match Error\n %s', rule)
|
||||
|
||||
|
||||
def _match_string_rule(rule, matcher, findings, perms, data,
|
||||
file_path, tmp_data):
|
||||
if rule['match'] == Match.single_string:
|
||||
if rule['string1'] in tmp_data:
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.string_and:
|
||||
and_match_str = True
|
||||
match_list = _get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if (match in tmp_data) is False:
|
||||
and_match_str = False
|
||||
break
|
||||
if and_match_str:
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.string_or:
|
||||
match_list = _get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
break
|
||||
elif rule['match'] == Match.string_and_or:
|
||||
match_list = _get_list_match_items(rule)
|
||||
string_or_stat = False
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
string_or_stat = True
|
||||
break
|
||||
if string_or_stat and (rule['string1'] in tmp_data):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.string_or_and:
|
||||
match_list = _get_list_match_items(rule)
|
||||
string_and_stat = True
|
||||
for match in match_list:
|
||||
if match in tmp_data is False:
|
||||
string_and_stat = False
|
||||
break
|
||||
if string_and_stat or (rule['string1'] in tmp_data):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.string_and_perm:
|
||||
if (rule['perm'] in perms
|
||||
and rule['string1'] in tmp_data):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.string_or_and_perm:
|
||||
match_list = _get_list_match_items(rule)
|
||||
string_or_ps = False
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
string_or_ps = True
|
||||
break
|
||||
if (rule['perm'] in perms) and string_or_ps:
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
elif rule['match'] == Match.string_and_not:
|
||||
if (rule['string1'] in tmp_data
|
||||
and rule['string2'] not in tmp_data):
|
||||
_add_findings(matcher, findings, file_path, rule)
|
||||
else:
|
||||
logger.error('Code String Rule Match Error\n%s', rule)
|
||||
|
||||
|
||||
def _rule_matcher(matcher, findings, perms, data, file_path, rules):
|
||||
"""Static Analysis Rule Matcher."""
|
||||
try:
|
||||
for rule in rules:
|
||||
|
||||
# CASE CHECK
|
||||
if rule['input_case'] == InputCase.lower:
|
||||
tmp_data = data.lower()
|
||||
elif rule['input_case'] == InputCase.upper:
|
||||
tmp_data = data.upper()
|
||||
elif rule['input_case'] == InputCase.exact:
|
||||
tmp_data = data
|
||||
|
||||
# MATCH TYPE
|
||||
if rule['type'] == MatchType.regex:
|
||||
_match_regex_rule(rule, matcher, findings, perms,
|
||||
data, file_path, tmp_data)
|
||||
|
||||
elif rule['type'] == MatchType.string:
|
||||
_match_string_rule(rule, matcher, findings, perms,
|
||||
data, file_path, tmp_data)
|
||||
else:
|
||||
logger.error('Code Rule Error\n%s', rule)
|
||||
except Exception:
|
||||
logger.exception('Error in Code Rule Processing')
|
||||
|
||||
|
||||
def api_rule_matcher(findings, perms, data, file_path, rules):
|
||||
_rule_matcher(_MatcherType.api, findings, perms, data, file_path, rules)
|
||||
|
||||
|
||||
def code_rule_matcher(findings, perms, data, file_path, rules):
|
||||
_rule_matcher(_MatcherType.code, findings, perms, data, file_path, rules)
|
||||
|
||||
|
||||
def _add_bfindings(findings, desc, detailed_desc, rule):
|
||||
"""Add Binary Analysis Findings."""
|
||||
findings[desc] = {'detailed_desc': detailed_desc,
|
||||
'level': rule['level'].value,
|
||||
'cvss': rule['cvss'],
|
||||
'cwe': rule['cwe'],
|
||||
'owasp': rule['owasp'],
|
||||
'owasp-mstg': rule['owasp-mstg']}
|
||||
|
||||
|
||||
def get_desc(desc_str, match):
|
||||
"""Generate formatted detailed description with matches."""
|
||||
return desc_str.format(
|
||||
b', '.join(list(set(match))).decode('utf-8', 'ignore'))
|
||||
|
||||
|
||||
def binary_rule_matcher(findings, data, ipa_rules):
|
||||
"""Static Analysis Rule Matcher."""
|
||||
try:
|
||||
for rule in ipa_rules:
|
||||
|
||||
# CASE CHECK
|
||||
if rule['input_case'] == InputCase.lower:
|
||||
tmp_data = data.lower()
|
||||
elif rule['input_case'] == InputCase.upper:
|
||||
tmp_data = data.upper()
|
||||
elif rule['input_case'] == InputCase.exact:
|
||||
tmp_data = data
|
||||
|
||||
# MATCH TYPE
|
||||
if rule['type'] == MatchType.regex:
|
||||
if rule['match'] == Match.single_regex:
|
||||
matched = re.findall(rule['regex1'], tmp_data)
|
||||
if matched:
|
||||
detailed_desc = get_desc(
|
||||
rule['detailed_desc'],
|
||||
matched)
|
||||
_add_bfindings(findings,
|
||||
rule['desc'],
|
||||
detailed_desc,
|
||||
rule)
|
||||
elif 'conditional' in rule:
|
||||
detailed_desc = rule['conditional']['detailed_desc']
|
||||
_add_bfindings(findings,
|
||||
rule['conditional']['desc'],
|
||||
detailed_desc,
|
||||
rule['conditional'])
|
||||
else:
|
||||
logger.error('Binary Regex Rule Match Error\n %s', rule)
|
||||
elif rule['type'] == MatchType.string:
|
||||
if rule['match'] == Match.single_string:
|
||||
if rule['string1'] in tmp_data:
|
||||
_add_bfindings(findings,
|
||||
rule['desc'],
|
||||
rule['detailed_desc'],
|
||||
rule)
|
||||
elif 'conditional' in rule:
|
||||
detailed_desc = rule['conditional']['detailed_desc']
|
||||
_add_bfindings(findings,
|
||||
rule['conditional']['desc'],
|
||||
detailed_desc,
|
||||
rule['conditional'])
|
||||
else:
|
||||
logger.error('Binary String Rule Match Error\n%s', rule)
|
||||
else:
|
||||
logger.error('Binary Rule Error\n%s', rule)
|
||||
except Exception:
|
||||
logger.exception('Error in Binary Rule Processing')
|
||||
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
This file contains enums used to define static source code analysis rules.
|
||||
|
||||
Match - It is an Enum used to run proper rule detection.
|
||||
1. single_regex - if re.findall(regex1, input)
|
||||
2. regex_and - if re.findall(regex1, input) and re.findall(regex2, input)
|
||||
3. regex_or - if re.findall(regex1, input) or re.findall(regex2, input)
|
||||
4. single_string - if string1 in input
|
||||
5. string_and - if (string1 in input) and (string2 in input)
|
||||
6. string_or - if (string1 in input) or (string2 in input)
|
||||
7. string_and_or - if (string1 in input) and ((string_or1 in input)
|
||||
or (string_or2 in input))
|
||||
8. string_or_and - if (string1 in input) or ((string_and1 in input)
|
||||
and (string_and2 in input))
|
||||
9. regex_and_perm - if re.findall(regex, input) and (permission in
|
||||
permission_list_from_manifest)
|
||||
10. string_and_perm - if (string1 in input)
|
||||
and (permission in permission_list_from_manifest)
|
||||
11. string_or_and_perm - if ((string1 in input) or (string2 in input))
|
||||
and (permission in permission_list_from_manifest)
|
||||
12. string_and_not - if ((string1 in input) and (string2 not in input))
|
||||
|
||||
MatchType - It is an Enum used to define match type.
|
||||
1. string
|
||||
2. regex
|
||||
|
||||
Level - It defines level of the rule.
|
||||
1. high - Rule has a high security impact.
|
||||
It will decrease security result by 15 points.
|
||||
2. warning - Rule warns about potencial security leaks.
|
||||
It will decrease security result by 10 points.
|
||||
3. info - Rule informs about best practice in some posible cases.
|
||||
It won't decrease security result.
|
||||
4. good - Rule increase app security.
|
||||
It will increase security result by 5 points.
|
||||
|
||||
InputCase - It is an Enum that defines how we should match pattern.
|
||||
1. upper
|
||||
2. lower
|
||||
3. exact
|
||||
"""
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Match(Enum):
|
||||
single_regex = 1
|
||||
regex_and = 2
|
||||
regex_or = 3
|
||||
single_string = 4
|
||||
string_and = 5
|
||||
string_or = 6
|
||||
string_and_or = 7
|
||||
string_or_and = 8
|
||||
regex_and_perm = 9
|
||||
string_and_perm = 10
|
||||
string_or_and_perm = 11
|
||||
string_and_not = 12
|
||||
|
||||
|
||||
class MatchType(Enum):
|
||||
string = 'string'
|
||||
regex = 'regex'
|
||||
|
||||
|
||||
class Level(Enum):
|
||||
high = 'high'
|
||||
warning = 'warning'
|
||||
info = 'info'
|
||||
good = 'good'
|
||||
|
||||
|
||||
class InputCase(Enum):
|
||||
upper = 'upper'
|
||||
lower = 'lower'
|
||||
exact = 'exact'
|
||||
@@ -40,6 +40,9 @@ from StaticAnalyzer.views.ios.db_interaction import (
|
||||
get_context_from_db_entry as idb)
|
||||
from StaticAnalyzer.views.windows.db_interaction import (
|
||||
get_context_from_db_entry as wdb)
|
||||
from StaticAnalyzer.views.rules_properties import (
|
||||
Level,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
try:
|
||||
@@ -257,255 +260,6 @@ def handle_pdf_win(static_db):
|
||||
return context, template
|
||||
|
||||
|
||||
def get_list_match_items(ruleset):
|
||||
"""Get List of Match item."""
|
||||
match_list = []
|
||||
i = 1
|
||||
identifier = ruleset['type']
|
||||
if ruleset['match'] == 'string_and_or':
|
||||
identifier = 'string_or'
|
||||
elif ruleset['match'] == 'string_or_and':
|
||||
identifier = 'string_and'
|
||||
while identifier + str(i) in ruleset:
|
||||
match_list.append(ruleset[identifier + str(i)])
|
||||
i = i + 1
|
||||
if not (identifier + str(i)) in ruleset:
|
||||
break
|
||||
return match_list
|
||||
|
||||
|
||||
def add_findings(findings, desc, file_path, rule):
|
||||
"""Add Code Analysis Findings."""
|
||||
if desc in findings:
|
||||
tmp_list = findings[desc]['path']
|
||||
if escape(file_path) not in tmp_list:
|
||||
tmp_list.append(escape(file_path))
|
||||
findings[desc]['path'] = tmp_list
|
||||
else:
|
||||
findings[desc] = {'path': [escape(file_path)],
|
||||
'level': rule['level'],
|
||||
'cvss': rule['cvss'],
|
||||
'cwe': rule['cwe'],
|
||||
'owasp': rule['owasp']}
|
||||
|
||||
|
||||
def code_rule_matcher(findings, perms, data, file_path, code_rules):
|
||||
"""Static Analysis Rule Matcher."""
|
||||
try:
|
||||
for rule in code_rules:
|
||||
|
||||
# CASE CHECK
|
||||
if rule['input_case'] == 'lower':
|
||||
tmp_data = data.lower()
|
||||
elif rule['input_case'] == 'upper':
|
||||
tmp_data = data.upper()
|
||||
elif rule['input_case'] == 'exact':
|
||||
tmp_data = data
|
||||
|
||||
# MATCH TYPE
|
||||
if rule['type'] == 'regex':
|
||||
if rule['match'] == 'single_regex':
|
||||
if re.findall(rule['regex1'], tmp_data):
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'regex_and':
|
||||
and_match_rgx = True
|
||||
match_list = get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if bool(re.findall(match, tmp_data)) is False:
|
||||
and_match_rgx = False
|
||||
break
|
||||
if and_match_rgx:
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'regex_or':
|
||||
match_list = get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if re.findall(match, tmp_data):
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
break
|
||||
elif rule['match'] == 'regex_and_perm':
|
||||
if (rule['perm'] in perms
|
||||
and re.findall(rule['regex1'], tmp_data)):
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
else:
|
||||
logger.error('Code Regex Rule Match Error\n %s', rule)
|
||||
|
||||
elif rule['type'] == 'string':
|
||||
if rule['match'] == 'single_string':
|
||||
if rule['string1'] in tmp_data:
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'string_and':
|
||||
and_match_str = True
|
||||
match_list = get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if (match in tmp_data) is False:
|
||||
and_match_str = False
|
||||
break
|
||||
if and_match_str:
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'string_or':
|
||||
match_list = get_list_match_items(rule)
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
break
|
||||
elif rule['match'] == 'string_and_or':
|
||||
match_list = get_list_match_items(rule)
|
||||
string_or_stat = False
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
string_or_stat = True
|
||||
break
|
||||
if string_or_stat and (rule['string1'] in tmp_data):
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'string_or_and':
|
||||
match_list = get_list_match_items(rule)
|
||||
string_and_stat = True
|
||||
for match in match_list:
|
||||
if match in tmp_data is False:
|
||||
string_and_stat = False
|
||||
break
|
||||
if string_and_stat or (rule['string1'] in tmp_data):
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'string_and_perm':
|
||||
if (rule['perm'] in perms
|
||||
and rule['string1'] in tmp_data):
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
elif rule['match'] == 'string_or_and_perm':
|
||||
match_list = get_list_match_items(rule)
|
||||
string_or_ps = False
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
string_or_ps = True
|
||||
break
|
||||
if (rule['perm'] in perms) and string_or_ps:
|
||||
add_findings(findings, rule[
|
||||
'desc'], file_path, rule)
|
||||
else:
|
||||
logger.error('Code String Rule Match Error\n%s', rule)
|
||||
else:
|
||||
logger.error('Code Rule Error\n%s', rule)
|
||||
except Exception:
|
||||
logger.exception('Error in Code Rule Processing')
|
||||
|
||||
|
||||
def add_apis(api_findings, desc, file_path):
|
||||
"""Add API Findings."""
|
||||
if desc in api_findings:
|
||||
tmp_list = api_findings[desc]['path']
|
||||
if escape(file_path) not in tmp_list:
|
||||
tmp_list.append(escape(file_path))
|
||||
api_findings[desc]['path'] = tmp_list
|
||||
else:
|
||||
api_findings[desc] = {'path': [escape(file_path)]}
|
||||
|
||||
|
||||
def api_rule_matcher(api_findings, perms, data, file_path, api_rules):
|
||||
"""Android API Analysis Rule Matcher."""
|
||||
try:
|
||||
for api in api_rules:
|
||||
|
||||
# CASE CHECK
|
||||
if api['input_case'] == 'lower':
|
||||
tmp_data = data.lower()
|
||||
elif api['input_case'] == 'upper':
|
||||
tmp_data = data.upper()
|
||||
elif api['input_case'] == 'exact':
|
||||
tmp_data = data
|
||||
|
||||
# MATCH TYPE
|
||||
if api['type'] == 'regex':
|
||||
if api['match'] == 'single_regex':
|
||||
if re.findall(api['regex1'], tmp_data):
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'regex_and':
|
||||
and_match_rgx = True
|
||||
match_list = get_list_match_items(api)
|
||||
for match in match_list:
|
||||
if bool(re.findall(match, tmp_data)) is False:
|
||||
and_match_rgx = False
|
||||
break
|
||||
if and_match_rgx:
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'regex_or':
|
||||
match_list = get_list_match_items(api)
|
||||
for match in match_list:
|
||||
if re.findall(match, tmp_data):
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
break
|
||||
elif api['match'] == 'regex_and_perm':
|
||||
if (api['perm'] in perms
|
||||
and re.findall(api['regex1'], tmp_data)):
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
else:
|
||||
logger.error('API Regex Rule Match Error\n %s', api)
|
||||
|
||||
elif api['type'] == 'string':
|
||||
if api['match'] == 'single_string':
|
||||
if api['string1'] in tmp_data:
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'string_and':
|
||||
and_match_str = True
|
||||
match_list = get_list_match_items(api)
|
||||
for match in match_list:
|
||||
if (match in tmp_data) is False:
|
||||
and_match_str = False
|
||||
break
|
||||
if and_match_str:
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'string_or':
|
||||
match_list = get_list_match_items(api)
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
break
|
||||
elif api['match'] == 'string_and_or':
|
||||
match_list = get_list_match_items(api)
|
||||
string_or_stat = False
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
string_or_stat = True
|
||||
break
|
||||
if string_or_stat and (api['string1'] in tmp_data):
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'string_or_and':
|
||||
match_list = get_list_match_items(api)
|
||||
string_and_stat = True
|
||||
for match in match_list:
|
||||
if match in tmp_data is False:
|
||||
string_and_stat = False
|
||||
break
|
||||
if string_and_stat or (api['string1'] in tmp_data):
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'string_and_perm':
|
||||
if (api['perm'] in perms) and (api['string1'] in tmp_data):
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
elif api['match'] == 'string_or_and_perm':
|
||||
match_list = get_list_match_items(api)
|
||||
string_or_ps = False
|
||||
for match in match_list:
|
||||
if match in tmp_data:
|
||||
string_or_ps = True
|
||||
break
|
||||
if (api['perm'] in perms) and string_or_ps:
|
||||
add_apis(api_findings, api['desc'], file_path)
|
||||
else:
|
||||
logger.error('API String Rule Match Error\n%s', api)
|
||||
else:
|
||||
logger.error('API Rule Error\n%s', api)
|
||||
except Exception:
|
||||
logger.exception('Error in API Rule Processing')
|
||||
|
||||
|
||||
def url_n_email_extract(dat, relative_path):
|
||||
"""Extract URLs and Emails from Source Code."""
|
||||
urls = []
|
||||
@@ -559,28 +313,16 @@ def score(findings):
|
||||
cvss_scores = []
|
||||
avg_cvss = 0
|
||||
app_score = 100
|
||||
if isinstance(findings, list):
|
||||
for finding in findings:
|
||||
if 'cvss' in finding:
|
||||
if finding['cvss'] != 0:
|
||||
cvss_scores.append(finding['cvss'])
|
||||
if finding['level'] == 'high':
|
||||
app_score = app_score - 15
|
||||
elif finding['level'] == 'warning':
|
||||
app_score = app_score - 10
|
||||
elif finding['level'] == 'good':
|
||||
app_score = app_score + 5
|
||||
else:
|
||||
for _, finding in findings.items():
|
||||
if 'cvss' in finding:
|
||||
if finding['cvss'] != 0:
|
||||
cvss_scores.append(finding['cvss'])
|
||||
if finding['level'] == 'high':
|
||||
app_score = app_score - 15
|
||||
elif finding['level'] == 'warning':
|
||||
app_score = app_score - 10
|
||||
elif finding['level'] == 'good':
|
||||
app_score = app_score + 5
|
||||
for _, finding in findings.items():
|
||||
if 'cvss' in finding:
|
||||
if finding['cvss'] != 0:
|
||||
cvss_scores.append(finding['cvss'])
|
||||
if finding['level'] == Level.high.value:
|
||||
app_score = app_score - 15
|
||||
elif finding['level'] == Level.warning.value:
|
||||
app_score = app_score - 10
|
||||
elif finding['level'] == Level.good.value:
|
||||
app_score = app_score + 5
|
||||
if cvss_scores:
|
||||
avg_cvss = round(sum(cvss_scores) / len(cvss_scores), 1)
|
||||
if app_score < 0:
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
"""Standards Supported by MobSF."""
|
||||
|
||||
|
||||
# OWASP Mobile Top 10 Risk 2016
|
||||
# https://owasp.org/www-project-mobile-top-10/
|
||||
OWASP = {
|
||||
'm1': 'M1: Improper Platform Usage',
|
||||
'm2': 'M2: Insecure Data Storage',
|
||||
'm3': 'M3: Insecure Communication',
|
||||
'm4': 'M4: Insecure Authentication',
|
||||
'm5': 'M5: Insufficient Cryptography',
|
||||
'm6': 'M6: Insecure Authorization',
|
||||
'm7': 'M7: Client Code Quality',
|
||||
'm8': 'M8: Code Tampering',
|
||||
'm9': 'M9: Reverse Engineering',
|
||||
'm10': 'M10: Extraneous Functionality',
|
||||
}
|
||||
|
||||
# OWASP Mobile AppSec Verification Standard (MAVS)
|
||||
# https://mobile-security.gitbook.io/masvs/security-requirements/
|
||||
OWASP_MSTG = {
|
||||
# Architecture, Design and Threat Modeling Requirements
|
||||
'arch-1': 'MSTG-ARCH-1',
|
||||
'arch-2': 'MSTG-ARCH-2',
|
||||
'arch-3': 'MSTG-ARCH-3',
|
||||
'arch-4': 'MSTG-ARCH-4',
|
||||
'arch-5': 'MSTG-ARCH-5',
|
||||
'arch-6': 'MSTG-ARCH-6',
|
||||
'arch-7': 'MSTG-ARCH-7',
|
||||
'arch-8': 'MSTG-ARCH-8',
|
||||
'arch-9': 'MSTG-ARCH-9',
|
||||
'arch-10': 'MSTG-ARCH-10',
|
||||
'arch-11': 'MSTG-ARCH-11',
|
||||
'arch-12': 'MSTG-ARCH-12',
|
||||
# Data Storage and Privacy Requirements
|
||||
'storage-1': 'MSTG-STORAGE-1',
|
||||
'storage-2': 'MSTG-STORAGE-2',
|
||||
'storage-3': 'MSTG-STORAGE-3',
|
||||
'storage-4': 'MSTG-STORAGE-4',
|
||||
'storage-5': 'MSTG-STORAGE-5',
|
||||
'storage-6': 'MSTG-STORAGE-6',
|
||||
'storage-7': 'MSTG-STORAGE-7',
|
||||
'storage-8': 'MSTG-STORAGE-8',
|
||||
'storage-9': 'MSTG-STORAGE-9',
|
||||
'storage-10': 'MSTG-STORAGE-10',
|
||||
'storage-11': 'MSTG-STORAGE-11',
|
||||
'storage-12': 'MSTG-STORAGE-12',
|
||||
'storage-13': 'MSTG-STORAGE-13',
|
||||
'storage-14': 'MSTG-STORAGE-14',
|
||||
'storage-15': 'MSTG-STORAGE-15',
|
||||
# Cryptography Requirements
|
||||
'crypto-1': 'MSTG-CRYPTO-1',
|
||||
'crypto-2': 'MSTG-CRYPTO-2',
|
||||
'crypto-3': 'MSTG-CRYPTO-3',
|
||||
'crypto-4': 'MSTG-CRYPTO-4',
|
||||
'crypto-5': 'MSTG-CRYPTO-5',
|
||||
'crypto-6': 'MSTG-CRYPTO-6',
|
||||
# Authentication and Session Management Requirements
|
||||
'auth-1': 'MSTG-AUTH-1',
|
||||
'auth-2': 'MSTG-AUTH-2',
|
||||
'auth-3': 'MSTG-AUTH-3',
|
||||
'auth-4': 'MSTG-AUTH-4',
|
||||
'auth-5': 'MSTG-AUTH-5',
|
||||
'auth-6': 'MSTG-AUTH-6',
|
||||
'auth-7': 'MSTG-AUTH-7',
|
||||
'auth-8': 'MSTG-AUTH-8',
|
||||
'auth-9': 'MSTG-AUTH-9',
|
||||
'auth-10': 'MSTG-AUTH-10',
|
||||
'auth-11': 'MSTG-AUTH-11',
|
||||
'auth-12': 'MSTG-AUTH-12',
|
||||
# Network Communication Requirements
|
||||
'network-1': 'MSTG-NETWORK-1',
|
||||
'network-2': 'MSTG-NETWORK-2',
|
||||
'network-3': 'MSTG-NETWORK-3',
|
||||
'network-4': 'MSTG-NETWORK-4',
|
||||
'network-5': 'MSTG-NETWORK-5',
|
||||
'network-6': 'MSTG-NETWORK-6',
|
||||
# Platform Interaction Requirements
|
||||
'platform-1': 'MSTG-PLATFORM-1',
|
||||
'platform-2': 'MSTG-PLATFORM-2',
|
||||
'platform-3': 'MSTG-PLATFORM-3',
|
||||
'platform-4': 'MSTG-PLATFORM-4',
|
||||
'platform-5': 'MSTG-PLATFORM-5',
|
||||
'platform-6': 'MSTG-PLATFORM-6',
|
||||
'platform-7': 'MSTG-PLATFORM-7',
|
||||
'platform-8': 'MSTG-PLATFORM-8',
|
||||
'platform-9': 'MSTG-PLATFORM-9',
|
||||
'platform-10': 'MSTG-PLATFORM-10',
|
||||
'platform-11': 'MSTG-PLATFORM-11',
|
||||
# Code Quality and Build Setting Requirements
|
||||
'code-1': 'MSTG-CODE-1',
|
||||
'code-2': 'MSTG-CODE-2',
|
||||
'code-3': 'MSTG-CODE-3',
|
||||
'code-4': 'MSTG-CODE-4',
|
||||
'code-5': 'MSTG-CODE-5',
|
||||
'code-6': 'MSTG-CODE-6',
|
||||
'code-7': 'MSTG-CODE-7',
|
||||
'code-8': 'MSTG-CODE-8',
|
||||
'code-9': 'MSTG-CODE-9',
|
||||
# Resilience Requirements
|
||||
'resilience-1': 'MSTG-RESILIENCE-1',
|
||||
'resilience-2': 'MSTG-RESILIENCE-2',
|
||||
'resilience-3': 'MSTG-RESILIENCE-3',
|
||||
'resilience-4': 'MSTG-RESILIENCE-4',
|
||||
'resilience-5': 'MSTG-RESILIENCE-5',
|
||||
'resilience-6': 'MSTG-RESILIENCE-6',
|
||||
'resilience-7': 'MSTG-RESILIENCE-7',
|
||||
'resilience-8': 'MSTG-RESILIENCE-8',
|
||||
'resilience-9': 'MSTG-RESILIENCE-9',
|
||||
'resilience-10': 'MSTG-RESILIENCE-10',
|
||||
'resilience-11': 'MSTG-RESILIENCE-11',
|
||||
'resilience-12': 'MSTG-RESILIENCE-12',
|
||||
'resilience-13': 'MSTG-RESILIENCE-13',
|
||||
}
|
||||
|
||||
# Common Weakness Enumeration (CWE)
|
||||
# https://cwe.mitre.org/data/index.html
|
||||
CWE = {
|
||||
'CWE-22': ('CWE-22 - Improper Limitation of a Pathname'
|
||||
' to a Restricted Directory (\'Path Traversal\')'),
|
||||
'CWE-89': ('CWE-89 - Improper Neutralization of Special '
|
||||
'Elements used in an SQL Command (\'SQL Injection\')'),
|
||||
'CWE-95': ('CWE-95 - Improper Neutralization of Directives'
|
||||
' in Dynamically Evaluated Code (\'Eval Injection\')'),
|
||||
'CWE-119': ('CWE-119 - Improper Restriction of Operations '
|
||||
'within the Bounds of a Memory Buffer'),
|
||||
'CWE-200': ('CWE-200 - Exposure of Sensitive Information'
|
||||
' to an Unauthorized Actor'),
|
||||
'CWE-250': 'CWE-250 - Execution with Unnecessary Privileges',
|
||||
'CWE-276': 'CWE-276 - Incorrect Default Permissions',
|
||||
'CWE-295': 'CWE-295 - Improper Certificate Validation',
|
||||
'CWE-757': ('CWE-757 - Selection of Less-Secure Algorithm '
|
||||
'During Negotiation (\'Algorithm Downgrade\')'),
|
||||
'CWE-311': 'CWE-311 - Missing Encryption of Sensitive Data',
|
||||
'CWE-312': 'CWE-312 - Cleartext Storage of Sensitive Information',
|
||||
'CWE-327': 'CWE-327 - Use of a Broken or Risky Cryptographic Algorithm',
|
||||
'CWE-329': 'CWE-329 - Not Using a Random IV with CBC Mode',
|
||||
'CWE-330': 'CWE-330 - Use of Insufficiently Random Values',
|
||||
'CWE-502': 'CWE-502 - Deserialization of Untrusted Data',
|
||||
'CWE-532': 'CWE-532 - Insertion of Sensitive Information into Log File',
|
||||
'CWE-676': 'CWE-676 - Use of Potentially Dangerous Function',
|
||||
'CWE-749': 'CWE-749 - Exposed Dangerous Method or Function',
|
||||
'CWE-780': 'CWE-780 - Use of RSA Algorithm without OAEP',
|
||||
'CWE-789': 'CWE-789 - Uncontrolled Memory Allocation',
|
||||
'CWE-919': 'CWE-919 - Weaknesses in Mobile Applications',
|
||||
'CWE-939': ('CWE-939 - Improper Authorization in Handler for'
|
||||
' Custom URL Scheme'),
|
||||
}
|
||||
@@ -443,9 +443,7 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -466,20 +464,21 @@
|
||||
<span class="info">info</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><b>
|
||||
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
<span class="danger">{{ details|key:"cvss" }}</br> high</span>
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
<span class="info">{{ details|key:"cvss" }}</br> info</span>
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
<span class="warning">{{ details|key:"cvss" }}</br> medium</span>
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
<span class="success">{{ details|key:"cvss" }}</br> low</span>
|
||||
{% endif %}
|
||||
</b></td>
|
||||
<td><b>{{ details|key:"cwe" }}</b></td>
|
||||
<td><b>{{ details|key:"owasp" }}</b></td>
|
||||
<td>
|
||||
<h5>CVSS V2:</h5>
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
<span class="danger">{{ details|key:"cvss" }} (high)</span>
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
<span class="info">{{ details|key:"cvss" }} (info)</span>
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
<span class="warning">{{ details|key:"cvss" }} (medium)</span>
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
<span class="success">{{ details|key:"cvss" }} (low)</span>
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <h5>CWE: </h5>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <h5>OWASP Top 10: </h5>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <h5>OWASP MASVS: </h5>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td style="word-break:break-all;">
|
||||
{% for path in details|key:"path" %}
|
||||
{{ path }}
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>PERMISSIONS</th>
|
||||
<th>STATUS</th>
|
||||
<th>DESCRIPTION</th>
|
||||
<th>REASON IN MANIFEST</th>
|
||||
</tr>
|
||||
@@ -192,6 +193,9 @@
|
||||
<td>
|
||||
{{ perm.name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ perm.status }}
|
||||
</td>
|
||||
<td>
|
||||
{{ perm.description}}
|
||||
</td>
|
||||
@@ -319,47 +323,45 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>DESCRIPTION</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for bin in binary_analysis %}
|
||||
{% for issue, details in binary_analysis.items %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ bin.issue }}
|
||||
{{ issue }}
|
||||
</td>
|
||||
<td>
|
||||
{% if bin.level == 'info' %}
|
||||
<span class='info'>{{bin.level}}</span>
|
||||
{% elif bin.level == 'good' %}
|
||||
<span class='success'>{{bin.level}}</span>
|
||||
{% elif bin.level == 'high' %}
|
||||
<span class='danger'>{{bin.level}}</span>
|
||||
{% elif bin.level == 'warning' %}
|
||||
<span class='warning'>{{bin.level}}</span>
|
||||
{% if details|key:"level" == 'info' %}
|
||||
<span class='info'>info</span>
|
||||
{% elif details|key:"level" == 'good' %}
|
||||
<span class='success'>secure</span>
|
||||
{% elif details|key:"level" == 'high' %}
|
||||
<span class='danger'>high</span>
|
||||
{% elif details|key:"level" == 'warning' %}
|
||||
<span class='warning'>warning</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><strong>
|
||||
{% if bin.cvss > 6 %}
|
||||
<span class="danger">{{ bin.cvss }}</br> high</span>
|
||||
{% elif bin.cvss == 0 %}
|
||||
<span class="info">{{ bin.cvss }}</br> info</span>
|
||||
{% elif bin.cvss >= 4 %}
|
||||
<span class="warning">{{ bin.cvss }}</br> medium</span>
|
||||
{% elif bin.cvss < 4 %}
|
||||
<span class="success">{{ bin.cvss }}</br> low</span>
|
||||
</strong>
|
||||
{% endif %}
|
||||
|
||||
</strong></td>
|
||||
<td><strong>{{bin.cwe}}</strong></td>
|
||||
<td><strong>{{bin.owasp}}</strong></td>
|
||||
<td>
|
||||
{{ bin.description}}
|
||||
<h5>CVSS V2:</h5>
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
<span class="danger">{{ details|key:"cvss" }} (high)</span>
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
<span class="info">{{ details|key:"cvss" }} (info)</span>
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
<span class="warning">{{ details|key:"cvss" }} (medium)</span>
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
<span class="success">{{ details|key:"cvss" }} (low)</span>
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <h5>CWE: </h5>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <h5>OWASP Top 10: </h5>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <h5>OWASP MASVS: </h5>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ details|key:"detailed_desc" }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -374,9 +376,7 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -397,20 +397,21 @@
|
||||
<span class="info">info</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><b>
|
||||
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
<span class="danger">{{ details|key:"cvss" }}</br> high</span>
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
<span class="info">{{ details|key:"cvss" }}</br> info</span>
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
<span class="warning">{{ details|key:"cvss" }}</br> medium</span>
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
<span class="success">{{ details|key:"cvss" }}</br> low</span>
|
||||
{% endif %}
|
||||
</b></td>
|
||||
<td><b>{{ details|key:"cwe" }}</b></td>
|
||||
<td><b>{{ details|key:"owasp" }}</b></td>
|
||||
<td>
|
||||
<h5>CVSS V2:</h5>
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
<span class="danger">{{ details|key:"cvss" }} (high)</span>
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
<span class="info">{{ details|key:"cvss" }} (info)</span>
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
<span class="warning">{{ details|key:"cvss" }} (medium)</span>
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
<span class="success">{{ details|key:"cvss" }} (low)</span>
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <h5>CWE: </h5>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <h5>OWASP Top 10: </h5>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <h5>OWASP MASVS: </h5>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td style="word-break:break-all;">
|
||||
{% for path in details|key:"path" %}
|
||||
{{ path }}
|
||||
|
||||
@@ -961,9 +961,7 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -984,9 +982,22 @@
|
||||
<span class="badge bg-info">info</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><strong>{{ details|key:"cvss" }}</strong></td>
|
||||
<td><strong>{{ details|key:"cwe" }}</strong></td>
|
||||
<td><strong>{{ details|key:"owasp" }}</strong></td>
|
||||
<td>
|
||||
<strong>CVSS V2:</strong>
|
||||
{{ details|key:"cvss" }}
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
(high)
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
(info)
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
(medium)
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
(low)
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <strong>CWE: </strong>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <strong>OWASP Top 10: </strong>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <strong>OWASP MASVS: </strong>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for path in details|key:"path" %}
|
||||
<a href="../ViewSource/?file={{ path }}&md5={{ md5 }}&type={{app_type}}"> {{ path }}</a>
|
||||
|
||||
@@ -752,9 +752,7 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -775,9 +773,22 @@
|
||||
<span class="badge bg-info">info</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><b>{{ details|key:"cvss" }}</b></td>
|
||||
<td><b>{{ details|key:"cwe" }}</b></td>
|
||||
<td><b>{{ details|key:"owasp" }}</b></td>
|
||||
<td>
|
||||
<strong>CVSS V2:</strong>
|
||||
{{ details|key:"cvss" }}
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
(high)
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
(info)
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
(medium)
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
(low)
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <strong>CWE: </strong>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <strong>OWASP Top 10: </strong>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <strong>OWASP MASVS: </strong>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for path in details|key:"path" %}
|
||||
<a href="../ViewSource/?file={{ path }}&md5={{ md5 }}&type={{app_type}}"> {{ path }}</a>
|
||||
|
||||
@@ -448,6 +448,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>PERMISSIONS</th>
|
||||
<th>STATUS</th>
|
||||
<th>DESCRIPTION</th>
|
||||
<th>REASON IN MANIFEST</th>
|
||||
</tr>
|
||||
@@ -459,6 +460,13 @@
|
||||
<td>
|
||||
{{ perm.name }}
|
||||
</td>
|
||||
<td>
|
||||
{% if perm.status == 'dangerous' %}
|
||||
<span class="badge bg-danger">dangerous</span>
|
||||
{% else %}
|
||||
<span class="badge bg-info">normal</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ perm.description}}
|
||||
</td>
|
||||
@@ -566,35 +574,46 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>DESCRIPTION</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for bin in binary_analysis %}
|
||||
{% for issue, details in binary_analysis.items %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ bin.issue }}
|
||||
{{ issue }}
|
||||
</td>
|
||||
<td>
|
||||
{% if bin.level == 'info' %}
|
||||
<span class='badge bg-info'>{{bin.level}}</span>
|
||||
{% elif bin.level == 'good' %}
|
||||
<span class='badge bg-success'>{{bin.level}}</span>
|
||||
{% elif bin.level == 'high' %}
|
||||
<span class='badge bg-danger'>{{bin.level}}</span>
|
||||
{% elif bin.level == 'warning' %}
|
||||
<span class='badge bg-warning'>{{bin.level}}</span>
|
||||
{% if details|key:"level" == 'info' %}
|
||||
<span class='badge bg-info'>info</span>
|
||||
{% elif details|key:"level" == 'good' %}
|
||||
<span class='badge bg-success'>secure</span>
|
||||
{% elif details|key:"level" == 'high' %}
|
||||
<span class='badge bg-danger'>high</span>
|
||||
{% elif details|key:"level" == 'warning' %}
|
||||
<span class='badge bg-warning'>warning</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><strong>{{bin.cvss}}</strong></td>
|
||||
<td><strong>{{bin.cwe}}</strong></td>
|
||||
<td><strong>{{bin.owasp}}</strong></td>
|
||||
<td>
|
||||
{{ bin.description}}
|
||||
<strong>CVSS V2:</strong>
|
||||
{{ details|key:"cvss" }}
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
(high)
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
(info)
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
(medium)
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
(low)
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <strong>CWE: </strong>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <strong>OWASP Top 10: </strong>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <strong>OWASP MASVS: </strong>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ details|key:"detailed_desc" }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -415,6 +415,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>PERMISSIONS</th>
|
||||
<th>STATUS</th>
|
||||
<th>DESCRIPTION</th>
|
||||
<th>REASON IN MANIFEST</th>
|
||||
</tr>
|
||||
@@ -426,6 +427,13 @@
|
||||
<td>
|
||||
{{ perm.name }}
|
||||
</td>
|
||||
<td>
|
||||
{% if perm.status == 'dangerous' %}
|
||||
<span class="badge bg-danger">dangerous</span>
|
||||
{% else %}
|
||||
<span class="badge bg-info">normal</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ perm.description}}
|
||||
</td>
|
||||
@@ -578,9 +586,7 @@
|
||||
<tr>
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>CVSS</th>
|
||||
<th>CWE</th>
|
||||
<th>OWASP</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
|
||||
</tr>
|
||||
@@ -602,9 +608,22 @@
|
||||
<span class="badge bg-info">info</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><strong>{{ details|key:"cvss" }}</strong></td>
|
||||
<td><strong>{{ details|key:"cwe" }}</strong></td>
|
||||
<td><strong>{{ details|key:"owasp" }}</strong></td>
|
||||
<td>
|
||||
<strong>CVSS V2:</strong>
|
||||
{{ details|key:"cvss" }}
|
||||
{% if details|key:"cvss" > 6 %}
|
||||
(high)
|
||||
{% elif details|key:"cvss" == 0 %}
|
||||
(info)
|
||||
{% elif details|key:"cvss" >= 4 %}
|
||||
(medium)
|
||||
{% elif details|key:"cvss" < 4 %}
|
||||
(low)
|
||||
{% endif %}
|
||||
{% if details|key:"cwe" %}</br> <strong>CWE: </strong>{{ details|key:"cwe" }}{% endif %}
|
||||
{% if details|key:"owasp" %}</br> <strong>OWASP Top 10: </strong>{{ details|key:"owasp" }}{% endif %}
|
||||
{% if details|key:"owasp-mstg" %}</br> <strong>OWASP MASVS: </strong>{{ details|key:"owasp-mstg" }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for path in details|key:"path" %}
|
||||
<a href="../ViewFile/?file={{ path }}&md5={{ md5 }}&type=ios"> {{ path }}</a>
|
||||
|
||||
Reference in New Issue
Block a user