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:
Ajin Abraham
2020-03-13 12:25:25 -04:00
committed by GitHub
parent b6a88e3e4c
commit 7198c9ca34
30 changed files with 2907 additions and 1817 deletions
+3 -3
View File
@@ -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
View File
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
MOBSF_VER = 'v3.0.4 Beta'
MOBSF_VER = 'v3.0.5 Beta'
BANNER = """
__ __ _ ____ _____ _____ ___
+45 -40
View File
@@ -13,89 +13,94 @@ Made with ![Love](https://cloud.githubusercontent.com/assets/4301109/16754758/82
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=MobSF_Mobile-Security-Framework-MobSF&metric=alert_status)](https://sonarcloud.io/dashboard?id=MobSF_Mobile-Security-Framework-MobSF)
[![Build Status](https://travis-ci.com/MobSF/Mobile-Security-Framework-MobSF.svg?branch=master)](https://travis-ci.com/MobSF/Mobile-Security-Framework-MobSF)
[![Requirements Status](https://pyup.io/repos/github/MobSF/Mobile-Security-Framework-MobSF/shield.svg)](https://pyup.io/repos/github/MobSF/Mobile-Security-Framework-MobSF/)
[![ToolsWatch Best Security Tools 2017](https://img.shields.io/badge/ToolsWatch-Rank%209%20%7C%20Year%202017-red.svg)](http://www.toolswatch.org/2018/01/black-hat-arsenal-top-10-security-tools/)
[![ToolsWatch Best Security Tools 2016](https://img.shields.io/badge/ToolsWatch-Rank%205%20%7C%20Year%202016-red.svg)](http://www.toolswatch.org/2017/02/2016-top-security-tools-as-voted-by-toolswatch-org-readers/)
[![Blackhat Arsenal Asia 2018](https://img.shields.io/badge/Black%20Hat%20Arsenal-Asia%202018-blue.svg)](https://www.blackhat.com/asia-18/arsenal.html#mobile-security-framework-mobsf)
[![ToolsWatch Best Security Tools 2017](https://img.shields.io/badge/ToolsWatch-Rank%209%20%7C%20Year%202017-red.svg)](http://www.toolswatch.org/2018/01/black-hat-arsenal-top-10-security-tools/)
[![Blackhat Arsenal Asia 2015](https://img.shields.io/badge/Black%20Hat%20Arsenal-Asia%202015-blue.svg)](https://www.blackhat.com/asia-15/arsenal.html#yso-mobile-security-framework)
[![Blackhat Arsenal Asia 2018](https://img.shields.io/badge/Black%20Hat%20Arsenal-Asia%202018-blue.svg)](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:* [![Donate via Paypal](https://user-images.githubusercontent.com/4301109/28491754-14774f54-6f14-11e7-9975-8a5faeda7e30.gif)](https://mobsf.github.io/Mobile-Security-Framework-MobSF/paypal.html) *Send Bitcoins:* [![Donate Bitcoin](https://user-images.githubusercontent.com/4301109/30631105-cb8063c8-9e00-11e7-95df-43c20b840e52.png)](https://mobsf.github.io/Mobile-Security-Framework-MobSF/donate.html)
**Donate via Paypal:** [![Donate via Paypal](https://user-images.githubusercontent.com/4301109/76471686-c43b0500-63c9-11ea-8225-2a305efb3d87.gif)](https://paypal.me/ajinabraham)
**Send Bitcoins:** [![Donate Bitcoin](https://user-images.githubusercontent.com/4301109/30631105-cb8063c8-9e00-11e7-95df-43c20b840e52.png)](https://mobsf.github.io/Mobile-Security-Framework-MobSF/donate.html)
## Documentation
[![See MobSF Documentation](https://user-images.githubusercontent.com/4301109/70686099-3855f780-1c79-11ea-8141-899e39459da2.png)](https://github.com/MobSF/Mobile-Security-Framework-MobSF/wiki/1.-Documentation)
[![See MobSF Documentation](https://user-images.githubusercontent.com/4301109/70686099-3855f780-1c79-11ea-8141-899e39459da2.png)](https://mobsf.github.io/docs)
## Try MobSF Static Analyzer Online
[![Try in PWD](https://raw.githubusercontent.com/play-with-docker/stacks/master/assets/images/button.png)](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:
[![Try in PWD](https://user-images.githubusercontent.com/4301109/76351696-494bee80-62e4-11ea-894a-cb1cd07c86fc.png)](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) ![india](https://user-images.githubusercontent.com/4301109/37564171-6549d678-2ab6-11e8-9b9d-21327c7f5d5b.png) | [Dominik Schlecht](https://github.com/DominikSchlecht) ![germany](https://user-images.githubusercontent.com/4301109/37564176-743238ba-2ab6-11e8-9666-5d98f0a1d127.png) | [Magaofei](https://github.com/magaofei) ![china](https://user-images.githubusercontent.com/4301109/44515364-00bbe880-a6e0-11e8-944d-5b48a86427da.png) | [Matan Dobrushin](https://github.com/matandobr) ![israel](https://user-images.githubusercontent.com/4301109/37564177-782f1758-2ab6-11e8-91e5-c76bde37b330.png) | [Vincent Nadal](https://github.com/superpoussin22) ![france](https://user-images.githubusercontent.com/4301109/37564175-71d6d92c-2ab6-11e8-89d7-d21f5aa0bda8.png)
[Ajin Abraham](https://in.linkedin.com/in/ajinabraham) ![india](https://user-images.githubusercontent.com/4301109/37564171-6549d678-2ab6-11e8-9b9d-21327c7f5d5b.png) | [Dominik Schlecht](https://github.com/sn0b4ll) ![germany](https://user-images.githubusercontent.com/4301109/37564176-743238ba-2ab6-11e8-9666-5d98f0a1d127.png) | [Magaofei](https://github.com/magaofei) ![china](https://user-images.githubusercontent.com/4301109/44515364-00bbe880-a6e0-11e8-944d-5b48a86427da.png) | [Matan Dobrushin](https://github.com/matandobr) ![israel](https://user-images.githubusercontent.com/4301109/37564177-782f1758-2ab6-11e8-91e5-c76bde37b330.png) | [Vincent Nadal](https://github.com/superpoussin22) ![france](https://user-images.githubusercontent.com/4301109/37564175-71d6d92c-2ab6-11e8-89d7-d21f5aa0bda8.png)
## 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/)
![MobSF Course](https://user-images.githubusercontent.com/4301109/76344880-ad68b580-62d8-11ea-8cde-9e3475fc92f6.png) [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 Course](https://user-images.githubusercontent.com/4301109/76344939-c709fd00-62d8-11ea-8208-774f1d5a7c52.png) [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 Channel](https://user-images.githubusercontent.com/4301109/76471928-6e1a9180-63ca-11ea-88fb-b43d75153f74.png) [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
![android-static-analysis-apk](https://user-images.githubusercontent.com/4301109/70381680-c732df00-191c-11ea-86dc-fc2ce93af9df.png)
![android-static-analysis-apk2](https://user-images.githubusercontent.com/4301109/70381695-095c2080-191d-11ea-8254-e2a0c3eef708.png)
![compare-result](https://user-images.githubusercontent.com/4301109/70381729-92735780-191d-11ea-8671-c72f54f3a4be.png)
![android-static-analysis-apk](https://user-images.githubusercontent.com/4301109/76472502-1f6df700-63cc-11ea-9ac0-fca99327e47d.png)
![android-static-analysis-apk2](https://user-images.githubusercontent.com/4301109/76472562-4cbaa500-63cc-11ea-8fbe-b92ea57a8c6f.png)
![compare-result](https://user-images.githubusercontent.com/4301109/76473496-0286f300-63cf-11ea-91b6-5bb267c7e80b.png)
### Static Analysis - iOS
![ios-static-analysis-ipa](https://user-images.githubusercontent.com/4301109/70666043-dd51df80-1c3b-11ea-9b24-4048fad552fb.png)
![ios-static-analysis-source](https://user-images.githubusercontent.com/4301109/70381767-5d1b3980-191e-11ea-8adc-20f54554bf5b.png)
![ios-static-analysis-ipa](https://user-images.githubusercontent.com/4301109/76475349-eede8b00-63d4-11ea-9843-360ffa63cefa.png)
![ios-binary-analysis-ipa](https://user-images.githubusercontent.com/4301109/76473161-0ebe8080-63ce-11ea-9427-4ddbfb41c2ab.png)
![ios-static-analysis-source](https://user-images.githubusercontent.com/4301109/76473316-783e8f00-63ce-11ea-8b30-df35fb06e2bd.png)
### Dynamic Analysis - Android APK
![android-dynamic-analysis](https://user-images.githubusercontent.com/4301109/70381806-03673f00-191f-11ea-87e4-dee316212101.png)
![android-dynamic-frida-live](https://user-images.githubusercontent.com/4301109/70381835-72dd2e80-191f-11ea-8f94-2255c9f605d9.png)
![android-dynamic-report](https://user-images.githubusercontent.com/4301109/70381853-c18ac880-191f-11ea-8cf4-2ce44521509c.png)
![android-dynamic-analysis](https://user-images.githubusercontent.com/4301109/76473773-ea63a380-63cf-11ea-927d-730726ae495b.png)
![android-dynamic-frida-live-api-monitor](https://user-images.githubusercontent.com/4301109/76473831-14b56100-63d0-11ea-83cc-20693d929236.png)
![android-dynamic-report](https://user-images.githubusercontent.com/4301109/76474288-8641df00-63d1-11ea-8953-ec7adc706f05.png)
### Web API Viewer
![android-dynamic-http-tools](https://user-images.githubusercontent.com/4301109/65378797-57c53000-dcdb-11e9-84e9-d5acf887f3aa.png)
## 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://user-images.githubusercontent.com/4301109/76340877-a3dc4f00-62d2-11ea-8631-b4cc8d9e42ed.png) [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
View File
@@ -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 -4
View File
@@ -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__)
@@ -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,
},
]
@@ -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'],
}]
+22 -76
View File
@@ -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
+105
View File
@@ -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
+67 -34
View File
@@ -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')
-102
View File
@@ -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',
},
]
-375
View File
@@ -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': '',
},
]
+23 -327
View File
@@ -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
+82 -108
View File
@@ -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'],
},
]
+137
View File
@@ -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,
},
]
+374
View File
@@ -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'],
},
]
+323
View File
@@ -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'],
},
]
+264
View File
@@ -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')
+75
View File
@@ -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'
+13 -271
View File
@@ -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:
+148
View File
@@ -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'),
}
+16 -17
View File
@@ -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 }}
+47 -46
View File
@@ -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 }}&amp;md5={{ md5 }}&amp;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 }}&amp;md5={{ md5 }}&amp;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 }}&amp;md5={{ md5 }}&amp;type=ios"> {{ path }}</a>