diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ce7c79b81e..85efa23fd4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -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. diff --git a/MobSF/settings.py b/MobSF/settings.py index dbb86c3918..537cec103f 100755 --- a/MobSF/settings.py +++ b/MobSF/settings.py @@ -19,7 +19,7 @@ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -MOBSF_VER = 'v3.0.4 Beta' +MOBSF_VER = 'v3.0.5 Beta' BANNER = """ __ __ _ ____ _____ _____ ___ diff --git a/README.md b/README.md index 91d8b9e370..5cdd4fe377 100644 --- a/README.md +++ b/README.md @@ -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/76471686-c43b0500-63c9-11ea-8225-2a305efb3d87.gif)](https://paypal.me/ajinabraham) -*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) +**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/) + +![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/) -## 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) +## MobSF Support -## 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) +* **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 diff --git a/StaticAnalyzer/test_files/ios_swift_src.zip b/StaticAnalyzer/test_files/ios_swift_src.zip new file mode 100644 index 0000000000..1b8241bb93 Binary files /dev/null and b/StaticAnalyzer/test_files/ios_swift_src.zip differ diff --git a/StaticAnalyzer/tests.py b/StaticAnalyzer/tests.py index a60f7f2328..9eaba7f56c 100755 --- a/StaticAnalyzer/tests.py +++ b/StaticAnalyzer/tests.py @@ -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( diff --git a/StaticAnalyzer/views/android/code_analysis.py b/StaticAnalyzer/views/android/code_analysis.py index 2855dd2297..8d591eaab8 100755 --- a/StaticAnalyzer/views/android/code_analysis.py +++ b/StaticAnalyzer/views/android/code_analysis.py @@ -11,10 +11,17 @@ 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__) diff --git a/StaticAnalyzer/views/android/android_apis.py b/StaticAnalyzer/views/android/rules/android_apis.py similarity index 60% rename from StaticAnalyzer/views/android/android_apis.py rename to StaticAnalyzer/views/android/rules/android_apis.py index 3c236bc454..80455e5d4e 100755 --- a/StaticAnalyzer/views/android/android_apis.py +++ b/StaticAnalyzer/views/android/rules/android_apis.py @@ -36,387 +36,394 @@ 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, }, ] diff --git a/StaticAnalyzer/views/android/android_rules.py b/StaticAnalyzer/views/android/rules/android_rules.py similarity index 57% rename from StaticAnalyzer/views/android/android_rules.py rename to StaticAnalyzer/views/android/rules/android_rules.py index be546e3ef5..4a4690c368 100755 --- a/StaticAnalyzer/views/android/android_rules.py +++ b/StaticAnalyzer/views/android/rules/android_rules.py @@ -42,80 +42,97 @@ 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 @@ 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 @@ '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 @@ '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'], }] diff --git a/StaticAnalyzer/views/ios/binary_analysis.py b/StaticAnalyzer/views/ios/binary_analysis.py index 91329b2eee..7727cb804c 100755 --- a/StaticAnalyzer/views/ios/binary_analysis.py +++ b/StaticAnalyzer/views/ios/binary_analysis.py @@ -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 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 diff --git a/StaticAnalyzer/views/ios/classdump.py b/StaticAnalyzer/views/ios/classdump.py new file mode 100644 index 0000000000..f95a742c14 --- /dev/null +++ b/StaticAnalyzer/views/ios/classdump.py @@ -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 diff --git a/StaticAnalyzer/views/ios/code_analysis.py b/StaticAnalyzer/views/ios/code_analysis.py index 54f583990a..0ef70a074d 100755 --- a/StaticAnalyzer/views/ios/code_analysis.py +++ b/StaticAnalyzer/views/ios/code_analysis.py @@ -2,62 +2,95 @@ 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() - - # 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) + 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 + + 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 + urls_list = list(set(url_list)) # Domain Extraction and Malware Check logger.info('Performing Malware Check on extracted Domains') diff --git a/StaticAnalyzer/views/ios/ios_apis.py b/StaticAnalyzer/views/ios/ios_apis.py deleted file mode 100755 index 239a1e9e32..0000000000 --- a/StaticAnalyzer/views/ios/ios_apis.py +++ /dev/null @@ -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 - string1, string2, string3, string_or1, string_and1 - b. regex - 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', - }, -] diff --git a/StaticAnalyzer/views/ios/ios_rules.py b/StaticAnalyzer/views/ios/ios_rules.py deleted file mode 100755 index 9e5468eb04..0000000000 --- a/StaticAnalyzer/views/ios/ios_rules.py +++ /dev/null @@ -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 - string1, string2, string3, string_or1, string_and1 - b. regex - 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': '', - }, -] diff --git a/StaticAnalyzer/views/ios/otool_analysis.py b/StaticAnalyzer/views/ios/otool_analysis.py index 89370ba2ee..225cc78111 100644 --- a/StaticAnalyzer/views/ios/otool_analysis.py +++ b/StaticAnalyzer/views/ios/otool_analysis.py @@ -1,6 +1,5 @@ import logging import os -import re import platform import stat import subprocess @@ -11,11 +10,8 @@ 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 diff --git a/StaticAnalyzer/views/ios/permission_analysis.py b/StaticAnalyzer/views/ios/permission_analysis.py index 9cd54da00c..fd1489392b 100644 --- a/StaticAnalyzer/views/ios/permission_analysis.py +++ b/StaticAnalyzer/views/ios/permission_analysis.py @@ -2,117 +2,91 @@ 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 diff --git a/StaticAnalyzer/views/ios/rules/common_rules.py b/StaticAnalyzer/views/ios/rules/common_rules.py new file mode 100644 index 0000000000..23d481377f --- /dev/null +++ b/StaticAnalyzer/views/ios/rules/common_rules.py @@ -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 - string1, string2, string3, string_or1, string_and1 + b. regex - 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'], + }, +] diff --git a/StaticAnalyzer/views/ios/rules/ios_apis.py b/StaticAnalyzer/views/ios/rules/ios_apis.py new file mode 100755 index 0000000000..02834144ee --- /dev/null +++ b/StaticAnalyzer/views/ios/rules/ios_apis.py @@ -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 - string1, string2, string3, string_or1, string_and1 + b. regex - 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, + }, +] diff --git a/StaticAnalyzer/views/ios/rules/ipa_rules.py b/StaticAnalyzer/views/ios/rules/ipa_rules.py new file mode 100644 index 0000000000..c137f3eb2d --- /dev/null +++ b/StaticAnalyzer/views/ios/rules/ipa_rules.py @@ -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'], + }, +] diff --git a/StaticAnalyzer/views/ios/rules/objc_rules.py b/StaticAnalyzer/views/ios/rules/objc_rules.py new file mode 100755 index 0000000000..cf18f5250e --- /dev/null +++ b/StaticAnalyzer/views/ios/rules/objc_rules.py @@ -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 - string1, string2, string3, string_or1, string_and1 + b. regex - 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'], + }, +] diff --git a/StaticAnalyzer/views/ios/rules/swift_rules.py b/StaticAnalyzer/views/ios/rules/swift_rules.py new file mode 100644 index 0000000000..9737d42262 --- /dev/null +++ b/StaticAnalyzer/views/ios/rules/swift_rules.py @@ -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 - string1, string2, string3, string_or1, string_and1 + b. regex - 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'], + }, +] diff --git a/StaticAnalyzer/views/rule_matchers.py b/StaticAnalyzer/views/rule_matchers.py new file mode 100644 index 0000000000..dfdbb69b4e --- /dev/null +++ b/StaticAnalyzer/views/rule_matchers.py @@ -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') diff --git a/StaticAnalyzer/views/rules_properties.py b/StaticAnalyzer/views/rules_properties.py new file mode 100644 index 0000000000..bd39472288 --- /dev/null +++ b/StaticAnalyzer/views/rules_properties.py @@ -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' diff --git a/StaticAnalyzer/views/shared_func.py b/StaticAnalyzer/views/shared_func.py index 6afdebb846..08e19885eb 100755 --- a/StaticAnalyzer/views/shared_func.py +++ b/StaticAnalyzer/views/shared_func.py @@ -40,6 +40,9 @@ 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: diff --git a/StaticAnalyzer/views/standards.py b/StaticAnalyzer/views/standards.py new file mode 100644 index 0000000000..a243bead02 --- /dev/null +++ b/StaticAnalyzer/views/standards.py @@ -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'), +} diff --git a/templates/pdf/android_report.html b/templates/pdf/android_report.html index 315a780251..9fa81d7980 100755 --- a/templates/pdf/android_report.html +++ b/templates/pdf/android_report.html @@ -443,9 +443,7 @@

CODE ANALYSIS

ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS FILES @@ -466,20 +464,21 @@

CODE ANALYSIS

info {% endif %} - - - {% if details|key:"cvss" > 6 %} - {{ details|key:"cvss" }}
high
- {% elif details|key:"cvss" == 0 %} - {{ details|key:"cvss" }}
info
- {% elif details|key:"cvss" >= 4 %} - {{ details|key:"cvss" }}
medium
- {% elif details|key:"cvss" < 4 %} - {{ details|key:"cvss" }}
low
- {% endif %} -
- {{ details|key:"cwe" }} - {{ details|key:"owasp" }} + +
CVSS V2:
+ {% if details|key:"cvss" > 6 %} + {{ details|key:"cvss" }} (high) + {% elif details|key:"cvss" == 0 %} + {{ details|key:"cvss" }} (info) + {% elif details|key:"cvss" >= 4 %} + {{ details|key:"cvss" }} (medium) + {% elif details|key:"cvss" < 4 %} + {{ details|key:"cvss" }} (low) + {% endif %} + {% if details|key:"cwe" %}
CWE:
{{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10:
{{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS:
{{ details|key:"owasp-mstg" }}{% endif %} + {% for path in details|key:"path" %} {{ path }} diff --git a/templates/pdf/ios_report.html b/templates/pdf/ios_report.html index 0ef37a33b8..12ab64bb52 100644 --- a/templates/pdf/ios_report.html +++ b/templates/pdf/ios_report.html @@ -182,6 +182,7 @@

APPLICATION PERMISSIONS

PERMISSIONS + STATUS DESCRIPTION REASON IN MANIFEST @@ -192,6 +193,9 @@

APPLICATION PERMISSIONS

{{ perm.name }} + + {{ perm.status }} + {{ perm.description}} @@ -319,47 +323,45 @@

IPA BINARY ANALYSIS

ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS DESCRIPTION - {% for bin in binary_analysis %} + {% for issue, details in binary_analysis.items %} - {{ bin.issue }} + {{ issue }} - {% if bin.level == 'info' %} - {{bin.level}} - {% elif bin.level == 'good' %} - {{bin.level}} - {% elif bin.level == 'high' %} - {{bin.level}} - {% elif bin.level == 'warning' %} - {{bin.level}} + {% if details|key:"level" == 'info' %} + info + {% elif details|key:"level" == 'good' %} + secure + {% elif details|key:"level" == 'high' %} + high + {% elif details|key:"level" == 'warning' %} + warning {% endif %} - - {% if bin.cvss > 6 %} - {{ bin.cvss }}
high
- {% elif bin.cvss == 0 %} - {{ bin.cvss }}
info
- {% elif bin.cvss >= 4 %} - {{ bin.cvss }}
medium
- {% elif bin.cvss < 4 %} - {{ bin.cvss }}
low
-
- {% endif %} - - - {{bin.cwe}} - {{bin.owasp}} - {{ bin.description}} +
CVSS V2:
+ {% if details|key:"cvss" > 6 %} + {{ details|key:"cvss" }} (high) + {% elif details|key:"cvss" == 0 %} + {{ details|key:"cvss" }} (info) + {% elif details|key:"cvss" >= 4 %} + {{ details|key:"cvss" }} (medium) + {% elif details|key:"cvss" < 4 %} + {{ details|key:"cvss" }} (low) + {% endif %} + {% if details|key:"cwe" %}
CWE:
{{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10:
{{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS:
{{ details|key:"owasp-mstg" }}{% endif %} + + + {{ details|key:"detailed_desc" }} {% endfor %} @@ -374,9 +376,7 @@

CODE ANALYSIS

ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS FILES @@ -397,20 +397,21 @@

CODE ANALYSIS

info {% endif %} - - - {% if details|key:"cvss" > 6 %} - {{ details|key:"cvss" }}
high
- {% elif details|key:"cvss" == 0 %} - {{ details|key:"cvss" }}
info
- {% elif details|key:"cvss" >= 4 %} - {{ details|key:"cvss" }}
medium
- {% elif details|key:"cvss" < 4 %} - {{ details|key:"cvss" }}
low
- {% endif %} -
- {{ details|key:"cwe" }} - {{ details|key:"owasp" }} + +
CVSS V2:
+ {% if details|key:"cvss" > 6 %} + {{ details|key:"cvss" }} (high) + {% elif details|key:"cvss" == 0 %} + {{ details|key:"cvss" }} (info) + {% elif details|key:"cvss" >= 4 %} + {{ details|key:"cvss" }} (medium) + {% elif details|key:"cvss" < 4 %} + {{ details|key:"cvss" }} (low) + {% endif %} + {% if details|key:"cwe" %}
CWE:
{{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10:
{{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS:
{{ details|key:"owasp-mstg" }}{% endif %} + {% for path in details|key:"path" %} {{ path }} diff --git a/templates/static_analysis/android_binary_analysis.html b/templates/static_analysis/android_binary_analysis.html index 8f99213341..98568efed4 100755 --- a/templates/static_analysis/android_binary_analysis.html +++ b/templates/static_analysis/android_binary_analysis.html @@ -961,9 +961,7 @@

{{ providers | length }}

ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS FILES @@ -984,9 +982,22 @@

{{ providers | length }}

info {% endif %} - {{ details|key:"cvss" }} - {{ details|key:"cwe" }} - {{ details|key:"owasp" }} + + CVSS V2: + {{ 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" %}
CWE: {{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10: {{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS: {{ details|key:"owasp-mstg" }}{% endif %} + {% for path in details|key:"path" %} {{ path }} diff --git a/templates/static_analysis/android_source_analysis.html b/templates/static_analysis/android_source_analysis.html index a5994f4559..20f3a3acb2 100755 --- a/templates/static_analysis/android_source_analysis.html +++ b/templates/static_analysis/android_source_analysis.html @@ -752,9 +752,7 @@

{{ providers | length }}

ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS FILES @@ -775,9 +773,22 @@

{{ providers | length }}

info {% endif %} - {{ details|key:"cvss" }} - {{ details|key:"cwe" }} - {{ details|key:"owasp" }} + + CVSS V2: + {{ 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" %}
CWE: {{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10: {{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS: {{ details|key:"owasp-mstg" }}{% endif %} + {% for path in details|key:"path" %} {{ path }} diff --git a/templates/static_analysis/ios_binary_analysis.html b/templates/static_analysis/ios_binary_analysis.html index 51f28af8e9..fc54f48186 100755 --- a/templates/static_analysis/ios_binary_analysis.html +++ b/templates/static_analysis/ios_binary_analysis.html @@ -448,6 +448,7 @@
PERMISSIONS + STATUS DESCRIPTION REASON IN MANIFEST @@ -459,6 +460,13 @@
{{ perm.name }} + + {% if perm.status == 'dangerous' %} + dangerous + {% else %} + normal + {% endif %} + {{ perm.description}} @@ -566,35 +574,46 @@
ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS DESCRIPTION - {% for bin in binary_analysis %} + {% for issue, details in binary_analysis.items %} - {{ bin.issue }} + {{ issue }} + + + {% if details|key:"level" == 'info' %} + info + {% elif details|key:"level" == 'good' %} + secure + {% elif details|key:"level" == 'high' %} + high + {% elif details|key:"level" == 'warning' %} + warning + {% endif %} - {% if bin.level == 'info' %} - {{bin.level}} - {% elif bin.level == 'good' %} - {{bin.level}} - {% elif bin.level == 'high' %} - {{bin.level}} - {% elif bin.level == 'warning' %} - {{bin.level}} + CVSS V2: + {{ 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" %}
CWE: {{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10: {{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS: {{ details|key:"owasp-mstg" }}{% endif %} - {{bin.cvss}} - {{bin.cwe}} - {{bin.owasp}} - {{ bin.description}} + {{ details|key:"detailed_desc" }} {% endfor %} diff --git a/templates/static_analysis/ios_source_analysis.html b/templates/static_analysis/ios_source_analysis.html index 994ec68681..3d0c2089eb 100755 --- a/templates/static_analysis/ios_source_analysis.html +++ b/templates/static_analysis/ios_source_analysis.html @@ -415,6 +415,7 @@
PERMISSIONS + STATUS DESCRIPTION REASON IN MANIFEST @@ -426,6 +427,13 @@
{{ perm.name }} + + {% if perm.status == 'dangerous' %} + dangerous + {% else %} + normal + {% endif %} + {{ perm.description}} @@ -578,9 +586,7 @@
ISSUE SEVERITY - CVSS - CWE - OWASP + STANDARDS FILES @@ -602,9 +608,22 @@
info {% endif %} - {{ details|key:"cvss" }} - {{ details|key:"cwe" }} - {{ details|key:"owasp" }} + + CVSS V2: + {{ 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" %}
CWE: {{ details|key:"cwe" }}{% endif %} + {% if details|key:"owasp" %}
OWASP Top 10: {{ details|key:"owasp" }}{% endif %} + {% if details|key:"owasp-mstg" %}
OWASP MASVS: {{ details|key:"owasp-mstg" }}{% endif %} + {% for path in details|key:"path" %} {{ path }}