- 文档
- 教程
- 网络安全
【免费下载链接】mastg
The OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security Weakness Enumeration (MASWE) weaknesses, which are in alignment with the OWASP MASVS.
导读
本文基于 OWASP MASTG 仓库中的 MASTG-DEMO-0041 演示用例,完整还原一个 iOS 安全测试场景:某 App 仅依赖LocalAuthentication框架的LAContext.evaluatePolicy进行生物识别门禁,将敏感 token 的保护押在一个可被逻辑绕过的布尔结果上。读者将学会用 radare2(r2)对主二进制做静态分析,在反汇编层识别objc_msgSend与LAPolicyDeviceOwnerAuthenticationWithBiometrics特征,并与 Keychain 的SecAccessControlCreateWithFlags方案对比,最终掌握该弱点的判定标准(对应 MASTG-TEST-0266)和实际绕过思路。
该 demo 的测试结论是失败的(insecure):因为二进制中只找到生物识别验证的调用,没有任何要求用户在场的 Keychain API 调用,属于可被 MASTG-TECH-0135 绕过生物识别认证 轻易击穿的典型缺陷。
1. 为什么"生物识别验证成功"不等于"敏感数据安全"
Demo 对应的测试用例 MASTG-TEST-0266 给出的判定视角非常清晰,它把两种保护机制放在了对立面:
- LocalAuthentication API(如
LAContext):负责完成 Touch ID / Face ID / 设备密码等用户认证,只返回成功或失败,不负责存储任何秘密,也不执行任何强制安全策略。应用拿到布尔结果后自行决定是否放行——这个"自行决定"正是漏洞温床。 - Keychain API:真正安全地存储敏感数据,并可通过
SecAccessControlCreateWithFlags配置访问控制策略(如要求用户在场的kSecAccessControlUserPresence标志)。认证结果不再是独立的一次性布尔值,而是被嵌入到进程外的安全数据读取路径中,绕过认证的难度显著提高。
用一句话概括:前者是"认证通过 → 才给你看数据",后者是"认证通过 → 系统才把数据交给你"。后者把安全决策从应用代码手中移交给了操作系统安全服务。
2. 样例源码:错误示范的完整代码
Demo 的不安全示例源码位于 MastgTest.swift,完整代码如下:
import Foundation import LocalAuthentication struct MastgTest { static func mastgTest(completion: @escaping (String) -> Void) { let token = "8767086b9f6f976g-a8df76" let context = LAContext() let reason = "Authenticate to access your token" context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in DispatchQueue.main.async { if success { completion("✅ Retrieved token: \(token)") return } // Authentication failed: inspect the error code let message: String if let laError = error as? LAError { switch laError.code { case .userCancel: message = "Authentication was cancelled by the user." case .userFallback: message = "User tapped the fallback button (e.g. entered a password)." case .systemCancel: message = "Authentication was cancelled by the system (e.g. another app came to foreground)." case .passcodeNotSet: message = "Passcode is not set on the device." case .biometryNotAvailable: message = "No biometric authentication is available on this device." case .biometryNotEnrolled: message = "The user has not enrolled any biometrics." case .biometryLockout: message = "Biometry is locked out due to too many failed attempts." default: // For any future or undocumented codes message = laError.localizedDescription } } else { // Some other non-LAError error message = error?.localizedDescription ?? "Unknown authentication error." } completion("❌ \(message)") } } } }分析这段代码的缺陷点:
- 敏感数据与认证结果耦合在同一个进程内:
token是硬编码字符串,认证成功与否只影响是否通过completion回调返回它。攻击者只要让回调以"成功"路径执行即可拿到 token。 - 控制流依赖一个 if 判断:
if success { ... }是典型的可被操纵的逻辑分支。demo 明确指出该方式之所以弱,正是因为依赖 if 语句检查认证是否成功,攻击者可用 MASTG-TECH-0135 等技巧绕过。 - 未使用 Keychain:整个文件中没有任何
SecItemAdd/SecItemCopyMatching/SecAccessControlCreateWithFlags调用,token 完全没有落到系统级安全存储中。 - 错误处理相对完善但无济于事:对
LAError的各类 code(用户取消、回退、系统取消、未设置密码、生物识别不可用/未录入/锁定)都做了中文式消息映射,这只能提升可用性,不能提升安全性。
按测试元数据,该用例关联的弱点是 MASWE-0020(元数据见 MASTG-TEST-0266.md 的 front matter),并关联到 MASTG-KNOW-0056 / MASTG-KNOW-0057 的知识条目。
3. 复现步骤:从 IPA 到反汇编证据
3.1 获取并解包 App
首先按 MASTG-TECH-0058(探索 App 包) 的流程拿到 IPA 并解压。标准方式:
unzip MASTestApp.ipa解压后在Payload目录下得到 Application Bundle(.app)。Demo 明确指出主二进制位于:
./Payload/MASTestApp.app/MASTestApp3.2 编写 r2 自动化脚本
仓库中已提供现成的 radare2 脚本 insecureAuthenticationBiometricsApi.r2,内容如下:
e asm.bytes=false e scr.color=false e asm.var=false ?e Print xrefs to 'Run analysis" aaa ?e Print xrefs to 'evaluatePolicy" f~evaluatePolicy ?e ?e Print xrefs to 0x100010098 axt @ 0x100010098 ?e ?e Print disassembly around "evaluatePolicy" in the function pdf @ 0x100004344 | grep -C 5 "evaluatePolicy:" ?e Print xrefs to 'SecAccessControlCreateWithFlags" f~SecAccessControlCreateWithFlags脚本关键点逐行解读:
| 命令 | 作用 |
|---|---|
e asm.bytes=false | 反汇编时不显示原始机器码字节,聚焦指令语义 |
e scr.color=false | 关闭颜色输出,便于重定向到文件后阅读 |
e asm.var=false | 关闭变量名注释,保持输出与地址/寄存器一一对应 |
aaa | 对二进制执行完整分析(auto analysis),构建函数与引用关系 |
f~evaluatePolicy | 在 flags 空间中过滤包含evaluatePolicy的符号/标志,定位 selector 的字符串与 reloc |
axt @ 0x100010098 | 查看指定地址(evaluatePolicy:localizedReason:的 reloc.fixup 所在处)的交叉引用,找出谁在调用它 |
pdf @ 0x100004344 | 对包含调用的函数做反汇编,并用grep -C 5截取evaluatePolicy:上下文 |
f~SecAccessControlCreateWithFlags | 检查二进制中是否存在 Keychain 访问控制 API 的引用 |
3.3 一键运行
仓库提供的一键脚本 run.sh:
#!/bin/bash r2 -q -i insecureAuthenticationBiometricsApi.r2 -e emu.str=true -A MASTestApp > output.asm参数说明:
-q:安静模式,执行完脚本后直接退出,不进入交互式 shell;-i insecureAuthenticationBiometricsApi.r2:批处理执行脚本中的全部 r2 命令;-e emu.str=true:启用字符串模拟(emu.str),让 r2 在引用字符串时模拟求值,便于在反汇编中直接看到"evaluatePolicy:localizedReason:"等 selector 字符串内容;-A:等价于在脚本开头执行aaa分析(脚本内也显式执行了一次,双保险);> output.asm:全部输出重定向到 output.asm,便于留档与对比。
4. 观察:反汇编输出如何"出卖"了生物识别调用
运行后得到的 output.asm 完整还原如下:
Print xrefs to 'Run analysis" Print xrefs to 'evaluatePolicy" 0x100008297 38 str.evaluatePolicy:localizedReason:reply: 0x100010098 8 reloc.fixup.evaluatePolicy:localizedReason: Print xrefs to 0x100010098 sym.MASTestApp.MastgTest.mastg.completion.nd_n 0x100004344 [DATA:r--] ldr x1, [x8, 0x98] Print disassembly around "evaluatePolicy" in the function │ 0x100004334 bl sym.imp.swift_retain │ 0x100004338 mov x0, x23 ; void *arg0 │ 0x10000433c bl sym.imp.swift_release ; void swift_release(void *arg0) │ ; void swift_release(0) │ 0x100004340 adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100010000 │ 0x100004344 ldr x1, [x8, 0x98] ; [0x100010098:4]=0x8297 ; reloc.fixup.evaluatePolicy:localizedReason: ; char *selector │ 0x100004348 mov x0, x19 ; void *instance │ 0x10000434c mov w2, 1 │ 0x100004350 mov x3, x20 │ 0x100004354 mov x4, x22 │ 0x100004358 bl sym.imp.objc_msgSend ; void *objc_msgSend(void *instance, char *selector) Print xrefs to 'SecAccessControlCreateWithFlags"4.1 两条关键证据链
证据链一:selector 与 reloc 定位
f~evaluatePolicy找到了两处:
0x100008297:字符串str.evaluatePolicy:localizedReason:reply:,即 Objective-C 方法 selector 的运行时字符串,长度 38 字节;0x100010098:reloc.fixup.evaluatePolicy:localizedReason:,即方法 selector 的 relocation 修复项,供ldr x1, [x8, 0x98]加载使用。
证据链二:调用点反汇编
axt @ 0x100010098显示唯一交叉引用来自sym.MASTestApp.MastgTest.mastg.completion.nd_n函数内的地址0x100004344——正是 Swift 源码中mastgTest的编译产物,符号名与 Swift 源码结构一一对应。
4.2 为何反汇编与 Swift 源码"长得不一样"
Demo 明确指出:输出看起来和 MastgTest.swift 不完全一样,因为编译器会把部分函数转换成 Objective-C 的对应物。二进制中等价的 Objective-C 表示形如:
objc_msgSend(void *address, "evaluatePolicy:localizedReason:", LAPolicyDeviceOwnerAuthenticationWithBiometrics, ...)对照反汇编的寄存器约定(ARM64 调用规范:x0= self/instance,x1= selector,x2起为方法参数):
| 行号 | 指令 | 含义 |
|---|---|---|
0x100004344 | ldr x1, [x8, 0x98] | 加载 selector:evaluatePolicy:localizedReason: |
0x100004348 | mov x0, x19 | 第一个参数:LAContext实例(instance) |
0x10000434c | mov w2, 1 | 第二个参数(策略枚举)被设为1 |
0x100004350 | mov x3, x20 | 第三个参数:localizedReason字符串 |
0x100004354 | mov x4, x22 | 第四个参数:reply 回调 block |
0x100004358 | bl sym.imp.objc_msgSend | 发起 Objective-C 消息发送 |
关键判定:objc_msgSend的第三个参数(对应w2寄存器)在函数调用前通过第 17 行(输出中0x10000434c)的mov指令设置为1。这个1正是LAPolicyDeviceOwnerAuthenticationWithBiometrics的枚举值——即只启用生物识别(Face ID / Touch ID)认证,不包含设备密码兜底。
4.3 枚举取值全集:来自 SDK 头文件的事实依据
要确认1的枚举含义,可在装有 Xcode(MASTG-TOOL-0070) 的机器上,直接对 LocalAuthentication 框架头文件做 grep:
grep kLAPolicyDeviceOwnerAuthentication /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/LocalAuthentication.framework/Headers/LAPublicDefines.h预期输出(即该枚举的全部合法取值):
#define kLAPolicyDeviceOwnerAuthenticationWithBiometrics 1 #define kLAPolicyDeviceOwnerAuthentication 2 #define kLAPolicyDeviceOwnerAuthenticationWithWatch 3 #define kLAPolicyDeviceOwnerAuthenticationWithBiometricsOrWatch 4 #define kLAPolicyDeviceOwnerAuthenticationWithWristDetection 5 #define kLAPolicyDeviceOwnerAuthenticationWithCompanion kLAPolicyDeviceOwnerAuthenticationWithWatch #define kLAPolicyDeviceOwnerAuthenticationWithBiometricsOrCompanion kLAPolicyDeviceOwnerAuthenticationWithBiometricsOrWatch这些常量对应 Swift 中LAPolicy枚举:deviceOwnerAuthenticationWithBiometrics(1)、deviceOwnerAuthentication(2)、deviceOwnerAuthenticationWithWatch(3)、deviceOwnerAuthenticationWithBiometricsOrWatch(4)、deviceOwnerAuthenticationWithWristDetection(5)。因此mov w2, 1确凿指向源码中的.deviceOwnerAuthenticationWithBiometrics。
5. 评估:为什么该测试判为失败
对照 MASTG-TEST-0266 的评估标准:对每个值得保护的敏感数据资源,如果同时满足以下两点,测试用例即失败:
LAContext.evaluatePolicy被显式使用;- 没有任何要求用户在场的
SecAccessControlCreateWithFlags调用(即未使用任何要求用户在场的访问控制标志)。
本 demo 的 output.asm 中,最后一行Print xrefs to 'SecAccessControlCreateWithFlags"之后没有任何输出,证明二进制内不存在 Keychain 访问控制 API 的引用。输出仅显示 LocalAuthentication API 的生物识别验证引用,因此:
- ✅ 条件 1 成立:
evaluatePolicy:localizedReason:调用明确存在(0x100004358); - ❌ 条件 2 不满足:
SecAccessControlCreateWithFlags零引用。
结论:测试失败,应用被判定为不安全。该判定方法属于静态代码分析类型(测试元数据中type: [static, code])。
6. 实战佐证:这类弱点如何被真实绕过
Demo 明确提示该方式可被 MASTG-TECH-0135(绕过生物识别认证) 绕过。该技术文档给出的通用判断是:当应用只用if authenticated {...}这样的布尔结果做门禁、而没有用SecAccessControlCreateWithFlags的 Keychain API 要求用户在场时,即可绕过。
越狱设备(或已重签名的非越狱设备)上的绕过流程:
- 在越狱设备上安装并运行 frida-server;
- 使用 objection(MASTG-TOOL-0038)附加目标 App 并执行内置的
biometrics_bypass模块:
objection -n "MASTestApp" start ios ui biometrics_bypass- 该模块通过 hook 篡改认证结果,使
evaluatePolicy回调始终返回成功,从而让if success分支恒为真,token 随即泄露。
绕过效果见 MASTG-TECH-0135 中的实测截图:
非越狱设备上,objection 默认无法附加,需要先将 App 以get-task-allow标志重签名(详见 MASTG-TECH-0084),再走同样的流程。
7. 正确姿势:让认证成为安全读取路径的一部分
本 demo 的真正价值在于给出了正反对照:认证结果本身不该是安全边界,安全边界应落在系统级数据存储上。修复方案是把敏感 token 移入 Keychain,并配置要求用户在场的访问控制:
import Security import LocalAuthentication let accessControl = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.userPresence], // 要求用户在场(生物识别或密码) nil ) var query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "mastg-secret-token", kSecValueData as String: Data(token.utf8), kSecAttrAccessControl as String: accessControl as Any ] let status = SecItemAdd(query as CFDictionary, nil)读取时同样走 Keychain 查询路径,系统会在SecItemCopyMatching时强制执行用户在场认证。此时攻击者即使篡改应用内任意布尔判断,也拿不到数据——因为数据本身从未离开系统安全容器。这正是 MASTG-TEST-0266 要求同时检查SecAccessControlCreateWithFlags引用存在的根本原因。
8. 小结:一次可复用的静态审计流程
从 MASTG-DEMO-0041 可以提炼出一条可复用的 iOS 生物识别安全审计流水线:
- 解包:
unzip获取Payload/*.app,定位主二进制(MASTG-TECH-0058); - 静态扫描:用 r2(或等效工具)检索
evaluatePolicy:localizedReason:selector 与SecAccessControlCreateWithFlags符号; - 反汇编确认:通过
objc_msgSend的参数(w2寄存器的值)确认具体的LAPolicy枚举,排除误报; - 交叉比对:
evaluatePolicy存在但 Keychain 访问控制缺失 → 判为失败(MASTG-TEST-0266); - 动态验证(可选):用 objection 的
ios ui biometrics_bypass实际绕过,证明可利用性(MASTG-TECH-0135)。
该流程涉及的全部产物——demo 文档、源码、r2 脚本、输出证据、运行脚本 以及对应的 测试用例 与 绕过技术——均可直接在仓库中复现与交叉验证,是一份自洽、可审计、可教学的完整安全测试样本。
- 文档
- 教程
- 网络安全
【免费下载链接】mastg
The OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security Weakness Enumeration (MASWE) weaknesses, which are in alignment with the OWASP MASVS.
相关推荐
使用 radare2 静态分析 iOS Keychain 生物认证回退:kSecAccessControlUserPresence 实战解析(MASTG-DEMO-0043)
使用 radare2 静态分析 iOS Keychain 生物认证回退:kSecAccessControlUserPresence 实战解析(MASTG DEM
文档教程网络安全OWASP MASTG 实践指南:使用 CryptoObject 为 Android 生物识别认证建立密码学绑定(MASTG-BEST-0036)
OWASP MASTG 实践指南:使用 CryptoObject 为 Android 生物识别认证建立密码学绑定(MASTG BEST 0036) 导读 本指南
文档教程网络安全MASTG 最佳实践 MASTG-BEST-0038:Android 生物识别认证中的显式用户确认(setConfirmationRequired)安全配置指南
MASTG 最佳实践 MASTG BEST 0038:Android 生物识别认证中的显式用户确认(setConfirmationRequired)安全配置指南
文档教程网络安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考