MASTG-DEMO-0153: Universal Link Handler Without Input Validation
Download MASTG-DEMO-0153 IPA Open MASTG-DEMO-0153 Folder Build MASTG-DEMO-0153 IPA
Sample¶
The app declares the Associated Domains entitlement for applinks:demo.mas.owasp.org, so iOS routes verified universal links for that domain to the app and delivers them through onContinueUserActivity(NSUserActivityTypeBrowsingWeb).
The handler reads the amount query parameter from the universal link's webpageURL for the /transfer path and uses it directly as a string, without converting it to a numeric type or checking its value against any bounds. The domain is verified by the OS, but the path and query parameters are caller-controlled: anyone can send the user a link such as https://demo.mas.owasp.org/transfer?amount=9999999, and the handler will accept the supplied value and use it in the transfer flow without validating that it is a valid, bounded amount.
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
Because the OS only routes the link after verifying the domain, triggering a real universal link requires the Apple App Site Association file to be reachable for demo.mas.owasp.org, which isn't the case for this demo. But you can still exercise the same handler by constructing an NSUserActivity with a crafted webpageURL and invoking the continuation entry point with Frida (iOS), as described in Opening Deep Links. Either way, the handler returns the attacker-controlled value unchanged:
Transferring 9999999 units
Repeating with a non-numeric value (amount=not-a-number) returns Transferring not-a-number units, confirming at runtime that the handler accepts arbitrary universal link input without numeric conversion or bounds checking.
Steps¶
- Use Exploring the App Package to extract the relevant binaries from the app package, which in this case is
./Payload/MASTestApp.app/MASTestApp. - Use Extracting Entitlements from MachO Binaries with rabin2 (
rabin2 -OC) to extract the entitlements embedded in the signed binary and confirm the app declares universal link support viacom.apple.developer.associated-domains. - Use Static Analysis on iOS to locate the universal link handler and check for input validation. Run the r2 script with the
-ioption.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
1 2 3 4 5 6 | |
Observation¶
The extracted entitlements confirm the app is associated with the demo.mas.owasp.org domain, so iOS routes its verified universal links to the app:
| entitlements_reversed.plist | |
|---|---|
1 2 3 4 5 6 7 8 9 10 | |
The r2 output shows the handleUniversalLink method symbol, an empty result for Int conversion references, the onContinueUserActivity registration, the webpageURL reference, and focused disassembly of the handler covering URL parsing, path comparison, and query value extraction:
| output.txt | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
Evaluation¶
The test case fails because the handler uses the universal link's query value directly without any type conversion or bounds checking. The disassembly reveals the following flow:
- At
0x1000069ac, the handler callsURLComponents.init(url:resolvingAgainstBaseURL:)to parse the verified universal link URL read fromwebpageURL. - At
0x100006a44, it callsURL.pathto read the URL path, which represents the action, and compares it against the"/transfer"string literal loaded at0x100006a5c. - At
0x100006bc4, it callsURLQueryItem.valueto extract the rawString?value of theamountquery item. - At
0x100006cd0, the value flows intoDefaultStringInterpolationto build the"Transferring ... units"output string, with the"Transferring"literal loaded at0x100006cec.
Between URLQueryItem.value (0x100006bc4) and DefaultStringInterpolation (0x100006cd0) there is no call to Int.init or any other visible type conversion or validation function. This is further confirmed by the empty === References to Int conversion (input validation) === section. The handler therefore accepts an arbitrary string value from the universal link query and uses it directly in the transfer-related output, instead of validating that the value is numeric and within an expected range.