Lab 9.3 — Security Audit

Goal: Perform a complete internal security audit on a deliberately vulnerable iOS app — find 8 OWASP Mobile Top 10 violations, categorize them by M-number, and produce a prioritized remediation plan.

Time: ~4 hours

Prerequisites:

  • All of Phase 9
  • mitmproxy, objection, MobSF (Docker is easiest), semgrep
  • A jailbroken test device OR the iOS simulator (lab works mostly on simulator; jailbreak-specific findings are documented but not directly exploitable)

Setup

git clone https://github.com/bl9/swift-engineer-labs.git
cd swift-engineer-labs/09-security/security-audit/starter
open VulnerableBankApp.xcodeproj

The starter is a fake “banking” app — login, balance, transfers. It contains 8 deliberate OWASP violations. Your job is to find them all by category, fix each with the correct primitive, and write the audit report.

Tasks

Task 1 — Static analysis

Run static analysis tools and triage findings:

# semgrep
semgrep --config "p/swift" --config "p/security-audit" VulnerableBankApp/
# Note any high-confidence findings.

# strings scan
strings build/Release-iphonesimulator/VulnerableBankApp.app/VulnerableBankApp \
  | grep -iE "(api_key|password|secret|token|sk_live|http://)"

# MobSF (Docker)
docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
# Upload the .ipa via http://localhost:8000

Record every finding in audit-report.md with file:line references.

Task 2 — Dynamic analysis

# mitmproxy on port 8080, install CA on simulator
mitmproxy --listen-port 8080

In simulator: System Settings → Network → set HTTP Proxy to 127.0.0.1:8080; install the mitmproxy CA per Lab 9.2.

Run the app, log in, view balance, attempt a transfer. Inspect every request in mitmproxy.

Then run objection (requires Frida-injectable build — provided Debug-Frida config in the starter):

objection --gadget "VulnerableBankApp" explore
# Inside REPL:
ios keychain dump
ios nsuserdefaults get
ios cookies get
memory list modules

Task 3 — Find the 8 violations

Without spoiling: each violation maps to a specific M-category. They cover roughly: credential storage, authentication, network communication, input validation, privacy, cryptography, binary protections, security misconfiguration. Use the OWASP Mobile Top 10 mapping from 9.1 as your scoring rubric.

For each finding, document:

  • File / line reference
  • M-category and brief description
  • Severity (Critical / High / Medium / Low) per the rubric in 9.10
  • Reproduction steps (concrete: “log in, observe X in mitmproxy”)
  • Recommended fix with the correct iOS primitive
  • Estimated effort (S/M/L)

Task 4 — Apply the fixes

In the fixed/ directory, implement each remediation:

  • Move secrets from UserDefaults to Keychain with appropriate accessibility
  • Replace plain HTTP with HTTPS + pinning
  • Sanitize NSPredicate format strings with %@ placeholders
  • Remove hardcoded API keys; replace with a server-issued short-lived token
  • Add the missing PrivacyInfo.xcprivacy and accurate nutrition-label declarations
  • Replace ECB-mode CommonCrypto with AES.GCM from CryptoKit
  • Strip Swift symbols in release; add the missing build-script audit
  • Fix the os_log %{public}@ token leak
  • Upgrade weak Keychain accessibility (AccessibleAlwaysAccessibleAfterFirstUnlockThisDeviceOnly at minimum)

After each fix, re-run the relevant static/dynamic check to verify the finding is closed.

Task 5 — Write the audit report

Produce audit-report.md with:

  1. Executive summary — total findings by severity, key risk areas, headline conclusion
  2. Findings — one entry per violation with all fields above
  3. Remediation plan — prioritized by severity, with owners and estimated dates
  4. Verification matrix — table mapping each finding to the tool/test that confirmed the fix
  5. Residual risk — what’s still exposed even after all fixes (e.g., reverse engineering, server-side attacks)

This is what real security reports look like. Practice the format; you’ll see it again.

Task 6 — CI gates to prevent regressions

For each fix, add a CI check that fails the build if the regression recurs:

  • SwiftLint custom rule for UserDefaults secret writes
  • semgrep ruleset committed and enforced
  • Build script asserting no NSAllowsArbitraryLoads in release Info.plist
  • Build script asserting no STRIP_SWIFT_SYMBOLS = NO in release xcconfig
  • Strings-based scan that fails on hardcoded sk_live_ or Bearer literals

Demonstrate each check failing on a deliberate regression, then passing once reverted.

Stretch goals

  1. App Attest integration — wire the transfer endpoint to require an attestation assertion. Use a mock server.
  2. Threat model document — beyond the findings, write a one-pager threat model: actors, motivations, attack surfaces, mitigations. The kind of document a security review asks for.
  3. Coordinated-disclosure simulation — pretend you found these in a third-party app. Draft the disclosure email with timeline.
  4. Comparison study — run MobSF against a real shipping app (your own past projects, or a non-production sample). Note what shipping apps actually look like under the same scanner.

Notes

  • Don’t run mitmproxy or trust user-installed CAs on your personal device after the lab. Reset the CA trust in Settings.
  • The lab uses fake data and a mock server. Don’t point any of these techniques at real banking apps without explicit authorization — that’s a CFAA violation in the US (and equivalents elsewhere).
  • The “8 violations” count is intentional. If you find more, congratulations — note them in your report as bonus findings.

This concludes Phase 9. Continue to Phase 10 — Deployment & CI/CD.