Skip to content

MASTG-DEMO-0096: HTML Injection in a Local WebView Leading to Local File Access

Download MASTG-DEMO-0096 IPA Open MASTG-DEMO-0096 Folder Build MASTG-DEMO-0096 IPA

Sample

This sample demonstrates how overly broad file read access in a WebView can increase the impact of a separate HTML injection flaw. The app loads a trusted local HTML file and grants the WebView read access to the entire Documents directory by calling loadFileURL(_:allowingReadAccessTo:).

By itself, that broad read access is not enough to expose files. The issue becomes exploitable because the page also reads the username parameter from the URL and inserts it into the DOM using innerHTML. Since attacker-controlled input is treated as HTML, an attacker can inject markup that loads other local files from the same directory. See Attacker-Controlled Input in a WebView Leading to Unintended Navigation for more details on the HTML injection aspect of the vulnerability.

Because the WebView can read the full Documents directory, injected elements such as an <iframe> can load sibling files like secret.txt. This shows how overly broad local file access can turn a separate WebView injection bug into a local file disclosure issue.

To exploit the demo and load the secret file stored at <container>/Documents/secret.txt, you can enter the following payload as input:

  • <iframe src="./secret.txt"></iframe>
  • <object data="./secret.txt" type="text/plain"></object>
  • <embed src="./secret.txt" type="text/plain">

Summary of steps leading to this vulnerability.

  1. The app loads a local HTML file into a WKWebView.
  2. The WebView is granted read access to the entire Documents directory.
  3. The page reads attacker controlled input from the username query parameter.
  4. That value is inserted into the DOM using innerHTML.
  5. The attacker injects markup that loads another local file, such as secret.txt.
  6. As a result, local content becomes readable inside the WebView.

The vulnerable code path is the combination of untrusted input being inserted with innerHTML and broader local file read access than necessary. Together, they allow attacker controlled markup to access local files that should not be exposed.

 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
import SwiftUI
import UIKit
import WebKit

struct MastgTest {

    private static let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    private static let fileURL = docDir.appendingPathComponent("index.html")
    private static let secretURL = docDir.appendingPathComponent("secret.txt")

    public static func mastgTest(completion: @escaping (String) -> Void) {
        createSecretFile()
        createHtmlFile()

        DispatchQueue.main.async {
            showUserInput { userInput in
                completion(#"Filling user input into the webview... "\#(userInput)""#)
                  showHtmlRegistrationView(username: userInput, completion: completion)

            }
        }
    }

    private static func showHtmlRegistrationView(username: String, completion: @escaping (String) -> Void) {
        DispatchQueue.main.async {
          let urlWithUsername = URL(string: "\(fileURL.absoluteString)?username=\(username)")
          guard let urlWithUsername = urlWithUsername else{
            completion("Failed create URL object.")
            return
          }
          let webView = WKWebView()
          webView.loadFileURL(urlWithUsername, allowingReadAccessTo: docDir)

            let vc = UIViewController()
            vc.view = webView

            guard let presenter = topViewController() else {
                completion("Failed to present: no view controller.")
                return
            }

            presenter.present(vc, animated: true)
        }
    }

    static func createHtmlFile() {
        let htmlContent = """
        <!DOCTYPE html>
        <html>
        <head>
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <style>
                body { font-family: -apple-system, sans-serif; text-align: center; padding-top: 50px; }
                h1 { color: #007AFF; }
            </style>
        </head>
        <body>
            <h1>Registration</h1>
            <p><b>First Name:</b></p>
            <p id="username"></p>
        </body>
        </html>
        <script>
            const name = new URLSearchParams(window.location.search).get('username');
            document.getElementById('username').innerHTML = name;
        </script>
        """
        try? htmlContent.write(to: fileURL, atomically: true, encoding: .utf8)
    }


    static func createSecretFile() {
        try? "MY SECRET CONTENT".write(to: secretURL, atomically: true, encoding: .utf8)
    }

    private static func showUserInput(completion: @escaping (String) -> Void) {
        let alert = UIAlertController(title: "Enter your username", message: nil, preferredStyle: .alert)
        alert.addTextField { $0.placeholder = "Name" }
        alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
            completion(alert.textFields?.first?.text ?? "")
        })

        topViewController()?.present(alert, animated: true)
    }

    private static func topViewController(base: UIViewController? = nil) -> UIViewController? {
        let root = base ?? UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }.flatMap { $0.windows }
            .first { $0.isKeyWindow }?.rootViewController

        if let nav = root as? UINavigationController { return topViewController(base: nav.visibleViewController) }
        if let tab = root as? UITabBarController { return topViewController(base: tab.selectedViewController) }
        if let presented = root?.presentedViewController { return topViewController(base: presented) }
        return root
    }
}
 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
// AI-assisted reconstruction
// Derived from showWebView.asm, docDir-init.asm and fileURL-init.asm. May be inaccurate; the assembly is the authoritative source.

import Foundation
import UIKit
import WebKit

struct MastgTest {

    private static let docDir: URL = {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    }()

    private static let fileURL: URL = {
        docDir.appendingPathComponent("index.html")
    }()

    private static let secretURL: URL = {
        docDir.appendingPathComponent("secret.txt")
    }()

    public static func mastgTest(completion: @escaping (String) -> Void) {
        showHtmlRegistrationView(username: "user", completion: completion)
    }

    public static func showHtmlRegistrationView(username: String, completion: @escaping (String) -> Void) {
        let urlString = fileURL.absoluteString + "?username=" + username

        guard let url = URL(string: urlString) else {
            completion("Failed create URL object.")
            return
        }

        let webView = WKWebView()
        webView.loadFileURL(url, allowingReadAccessTo: docDir)

        let viewController = UIViewController()
        viewController.view = webView

        guard let topViewController = topViewController(base: nil) else {
            completion("Failed to present: no view controller.")
            return
        }

        topViewController.present(viewController, animated: true, completion: nil)
    }

    private static func topViewController(base: UIViewController?) -> UIViewController? {
        let baseVC: UIViewController?

        if let base = base {
            baseVC = base
        } else {
            baseVC = UIApplication.shared.connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .flatMap { $0.windows }
                .first(where: { $0.isKeyWindow })?
                .rootViewController
        }

        if let nav = baseVC as? UINavigationController {
            return topViewController(base: nav.visibleViewController)
        }

        if let tab = baseVC as? UITabBarController {
            return topViewController(base: tab.selectedViewController)
        }

        if let presented = baseVC?.presentedViewController {
            return topViewController(base: presented)
        }

        return baseVC
    }
}

Steps

  1. Unzip the app package and locate the main binary file as described in Exploring the App Package. In this case, the binary is ./Payload/MASTestApp.app/MASTestApp.
  2. Open the app binary with radare2 (iOS) and inspect the code paths that create and load the WKWebView.
  3. Identify any calls that load content into the WebView, such as load(_:) or loadFileURL(_:allowingReadAccessTo:).
  4. Obtain the code that updates the page content after loading, to verify whether attacker-controlled input is inserted into the DOM using innerHTML.
 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
e asm.bytes=false
e scr.color=false
e asm.var=false

?e URLsForDirectory:inDomains: selector (API anchor for the allowingReadAccessTo initializer):
f~URLsForDirectory

?e

?e xrefs to URLsForDirectory:inDomains: (finds the allowingReadAccessTo lazy initializer):
axt @ 0x100018130

?e

?e appendingPathComponent selector (API anchor for the fileURL initializer):
f~appendingPathComponent

?e

?e xrefs to appendingPathComponent import stub (finds the fileURL materializer ZTm_):
axt @ 0x10000a738

?e

?e xrefs to ZTm_ (finds the swift_once guard Z_ that encodes the filename as inline constants):
axt @ 0x10000418c

?e

?e HTML template string (contains innerHTML injection):
/ innerHTML

pdf @ 0x100004094 > docDir-init.asm
pdf @ 0x100004164 > fileURL-init.asm
pdf @ 0x100004240 >> fileURL-init.asm
1
2
#!/bin/bash
r2 -q -i load_webview.r2 -A ../MASTG-DEMO-0095/MASTestApp > output.txt

Observation

The output shows the loadFileURL:allowingReadAccessToURL: call site, the docDir and fileURL lazy initializers, and the innerHTML assignment in the embedded HTML template.

  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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
            ; CODE XREF from func.1000062e4 @ 0x1000062ec(x) ; sym.MASTestApp.MastgTest.showHtmlRegistrationView._6E8AB2C58CE173A727EF27CB85DF8CD8.username.completion_...A_
 792: E8AB2C.CE173A727EF27CB85DF8CD8.username.completion (int64_t arg1, int64_t arg2, int64_t arg3, int64_t arg4, int64_t arg_10h, int64_t arg_20h, int64_t arg_30h, int64_t arg_40h, int64_t arg_50h);
; MASTestApp.MastgTest.showHtmlRegistrationView._6E8AB2C58CE173A727EF2
; 7CB85DF8CD8.username.completion
           0x100004d24      stp x28, x27, [sp, -0x60]!
           0x100004d28      stp x26, x25, [var_60h]
           0x100004d2c      stp x24, x23, [arg_50h]
           0x100004d30      stp x22, x21, [arg_40h]
           0x100004d34      stp x20, x19, [arg_30h]
           0x100004d38      stp x29, x30, [arg_20h]
           0x100004d3c      add x29, sp, 0x50
           0x100004d40      sub sp, sp, 0x10
           0x100004d44      mov x24, x3                               ; arg4
           0x100004d48      mov x23, x2                               ; arg3
           0x100004d4c      mov x26, x1                               ; arg2
           0x100004d50      mov x27, x0                               ; arg1
           0x100004d54      mov x0, 0
           0x100004d58      bl sym.imp.Foundation.URL...VMa
           0x100004d5c      mov x19, x0
           0x100004d60      ldur x28, [x0, -8]
           0x100004d64      ldr x8, [x28, 0x40]
           0x100004d68      mov x9, x8
           0x100004d6c      adrp x16, segment.__DATA_CONST            ; 0x100014000
           0x100004d70      ldr x16, [x16, 0x50]                      ; [0x100014050:4]=9 ; "\t"
           0x100004d74      blr x16
           0x100004d78      mov x9, sp
           0x100004d7c      add x8, x8, 0xf
           0x100004d80      and x8, x8, 0xfffffffffffffff0
           0x100004d84      sub x22, x9, x8
           0x100004d88      mov sp, x22
           0x100004d8c      adrp x0, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x100004d90      add x0, x0, 0x2a8                         ; int64_t arg1
           0x100004d94      bl sym.___swift_instantiateConcreteTypeFromMangledName
           0x100004d98      ldur x8, [x0, -8]                         ; [0x1000182a0:4]=0
                                                                      ; sym....sSo11UITextFieldCML
                                                                      ...sSo11UITextFieldCML
           0x100004d9c      ldr x8, [x8, 0x40]
           0x100004da0      mov x9, x8
           0x100004da4      adrp x16, segment.__DATA_CONST            ; 0x100014000
           0x100004da8      ldr x16, [x16, 0x50]                      ; [0x100014050:4]=9 ; "\t"
           0x100004dac      blr x16
           0x100004db0      mov x9, sp
           0x100004db4      add x10, x8, 0xf
           0x100004db8      and x12, x10, 0xfffffffffffffff0
           0x100004dbc      sub x25, x9, x12
           0x100004dc0      mov sp, x25
           0x100004dc4      mov x9, x8
           0x100004dc8      adrp x16, segment.__DATA_CONST            ; 0x100014000
           0x100004dcc      ldr x16, [x16, 0x50]                      ; [0x100014050:4]=9 ; "\t"
           0x100004dd0      blr x16
           0x100004dd4      mov x8, sp
           0x100004dd8      sub x21, x8, x12
           0x100004ddc      mov sp, x21
           0x100004de0      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x100004de4      ldr x8, [x8, 0x1d0]                       ; [0x1000181d0:4]=0
                                                                      ; sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...z_
                                                                      MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8(...z)
           0x100004de8      cmn x8, 1
       ┌─< 0x100004dec      b.ne 0x10000500c
          ; CODE XREF from func.100004d24 @ 0x100005020(x)
      ┌──> 0x100004df0      adrp x1, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
      ╎│   0x100004df4      add x1, x1, 0x1d8                         ; int64_t arg2
      ╎│   0x100004df8      mov x0, x19                               ; int64_t arg1
      ╎│   0x100004dfc      bl sym.___swift_project_value_buffer
      ╎│   0x100004e00      mov x20, x0
      ╎│   0x100004e04      bl sym.imp.Foundation.URL.absoluteString_...vg_
      ╎│   0x100004e08      stp x0, x1, [x29, -0x60]
      ╎│   0x100004e0c      mov x0, 0x753f                            ; '?u'
      ╎│   0x100004e10      movk x0, 0x6573, lsl 16                   ; 'se'
      ╎│   0x100004e14      movk x0, 0x6e72, lsl 32                   ; 'rn'
      ╎│   0x100004e18      movk x0, 0x6d61, lsl 48                   ; 'am'
      ╎│   0x100004e1c      sub x20, x29, 0x60
      ╎│   0x100004e20      mov x1, 0x3d65                            ; 'e='
      ╎│   0x100004e24      movk x1, 0xea00, lsl 48
      ╎│   0x100004e28      bl sym.imp.append_...ySSF_                ; append(...ySSF)
      ╎│   0x100004e2c      mov x0, x27
      ╎│   0x100004e30      mov x1, x26
      ╎│   0x100004e34      bl sym.imp.append_...ySSF_                ; append(...ySSF)
      ╎│   0x100004e38      ldp x0, x20, [x29, -0x60]
      ╎│   0x100004e3c      mov x8, x21
      ╎│   0x100004e40      mov x1, x20
      ╎│   0x100004e44      bl sym.imp.Foundation.URL.string_...cfC_  ; Foundation.URL.string(...cfC)
      ╎│   0x100004e48      mov x0, x20                               ; void *arg0
      ╎│   0x100004e4c      bl sym.imp.swift_bridgeObjectRelease      ; void swift_bridgeObjectRelease(void *arg0)
      ╎│   0x100004e50      mov x0, x21                               ; int64_t arg1
      ╎│   0x100004e54      mov x1, x25                               ; int64_t arg2
      ╎│   0x100004e58      bl sym.Foundation.URL:_GenericAccessorW.bool____GenericAccessor ; func.1000062f0
      ╎│   0x100004e5c      ldr x8, [x28, 0x30]
      ╎│   0x100004e60      mov x0, x25
      ╎│   0x100004e64      mov w1, 1
      ╎│   0x100004e68      mov x2, x19
      ╎│   0x100004e6c      blr x8
      ╎│   0x100004e70      cmp w0, 1
     ┌───< 0x100004e74      b.ne 0x100004ea4
     │╎│   0x100004e78      mov x0, x25                               ; int64_t arg1
     │╎│   0x100004e7c      bl sym.Foundation.URL:_GenericAccessorW.bool____GenericAccessor__1 ; func.100006338
     │╎│   0x100004e80      adrp x8, 0x10000b000
     │╎│   0x100004e84      add x8, x8, 0x3d0                         ; 0x10000b3d0 ; "Failed create URL object."
     │╎│   0x100004e88      sub x8, x8, 0x20
     │╎│   0x100004e8c      orr x1, x8, 0x8000000000000000
     │╎│   0x100004e90      mov x0, 0x19
     │╎│   0x100004e94      movk x0, 0xd000, lsl 48
     │╎│   0x100004e98      mov x20, x24
     │╎│   0x100004e9c      blr x23
    ┌────< 0x100004ea0      b 0x100004fe4
    ││╎│   ; CODE XREF from func.100004d24 @ 0x100004e74(x)
    │└───> 0x100004ea4      ldr x8, [x28, 0x20]
     ╎│   0x100004ea8      mov x0, x22
     ╎│   0x100004eac      mov x1, x25
     ╎│   0x100004eb0      mov x2, x19
     ╎│   0x100004eb4      blr x8
     ╎│   0x100004eb8      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
     ╎│   0x100004ebc      ldr x0, [x8, 0x190]                       ; [0x100018190:4]=252
     ╎│                                                              ; reloc.WKWebView ; void *arg0
     ╎│   0x100004ec0      bl sym.imp.objc_allocWithZone             ; void *objc_allocWithZone(void *arg0)
     ╎│   0x100004ec4      adrp x27, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
     ╎│   0x100004ec8      ldr x1, [x27, 0x110]                      ; [0x100018110:4]=0xc0ee ; reloc.fixup.init ; char *selector
     ╎│   0x100004ecc      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
     ╎│   0x100004ed0      mov x25, x0
     ╎│   0x100004ed4      mov x20, x22
     ╎│   0x100004ed8      bl sym.imp.Foundation.URL._bridgeToObjectiveC.NSURL_...F_ ; Foundation.URL._bridgeToObjectiveC.NSURL(...F)
     ╎│   0x100004edc      mov x26, x0
     ╎│   0x100004ee0      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
     ╎│   0x100004ee4      ldr x8, [x8, 0x1b0]                       ; [0x1000181b0:4]=0
     ╎│                                                              ; sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...z_
     ╎│                                                              [24] -rw- section size 929 named 24.__DATA.__data
     ╎│   0x100004ee8      cmn x8, 1
    │┌───< 0x100004eec      b.ne 0x100005024
    ││╎│   ; CODE XREF from func.100004d24 @ 0x100005038(x)
   ┌─────> 0x100004ef0      adrp x1, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
   ╎││╎│   0x100004ef4      add x1, x1, 0x1b8                         ; int64_t arg2
   ╎││╎│   0x100004ef8      mov x0, x19                               ; int64_t arg1
   ╎││╎│   0x100004efc      bl sym.___swift_project_value_buffer
   ╎││╎│   0x100004f00      mov x20, x0
   ╎││╎│   0x100004f04      bl sym.imp.Foundation.URL._bridgeToObjectiveC.NSURL_...F_ ; Foundation.URL._bridgeToObjectiveC.NSURL(...F)
   ╎││╎│   0x100004f08      mov x20, x0
   ╎││╎│   0x100004f0c      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
   ╎││╎│   0x100004f10      ldr x1, [x8, 0x118]                       ; [0x100018118:4]=0xc0ff ; reloc.fixup.loadFileURL:allowingReadAccessT ; char *selector
   ╎││╎│   0x100004f14      mov x0, x25                               ; void *instance
   ╎││╎│   0x100004f18      mov x2, x26
   ╎││╎│   0x100004f1c      mov x3, x20
   ╎││╎│   0x100004f20      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
   ╎││╎│   0x100004f24      mov x29, x29
   ╎││╎│   0x100004f28      bl sym.imp.objc_retainAutoreleasedReturnValue ; void objc_retainAutoreleasedReturnValue(void *instance)
   ╎││╎│   0x100004f2c      bl sym.imp.objc_release                   ; void objc_release(void *instance)
   ╎││╎│   0x100004f30      mov x0, x26                               ; void *instance
   ╎││╎│   0x100004f34      bl sym.imp.objc_release                   ; void objc_release(void *instance)
   ╎││╎│   0x100004f38      mov x0, x20                               ; void *instance
   ╎││╎│   0x100004f3c      bl sym.imp.objc_release                   ; void objc_release(void *instance)
   ╎││╎│   0x100004f40      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
   ╎││╎│   0x100004f44      ldr x0, [x8, 0x198]                       ; [0x100018198:4]=253
   ╎││╎│                                                              ; reloc.UIViewController ; void *arg0
   ╎││╎│   0x100004f48      bl sym.imp.objc_allocWithZone             ; void *objc_allocWithZone(void *arg0)
   ╎││╎│   0x100004f4c      ldr x1, [x27, 0x110]                      ; [0x100018110:4]=0xc0ee ; reloc.fixup.init ; char *selector
   ╎││╎│   0x100004f50      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
   ╎││╎│   0x100004f54      mov x26, x0
   ╎││╎│   0x100004f58      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
   ╎││╎│   0x100004f5c      ldr x1, [x8, 0x120]                       ; [0x100018120:4]=0xc191 ; reloc.fixup.setView: ; char *selector
   ╎││╎│   0x100004f60      mov x2, x25
   ╎││╎│   0x100004f64      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
   ╎││╎│   0x100004f68      mov x0, 0                                 ; int64_t arg1
   ╎││╎│   0x100004f6c      bl sym.MASTestApp.MastgTest.topViewController._6E8AB2C58CE173A727EF27CB85DF8CD8.base.UIView...G0CSgAI_tFZ ; func.10000503c
  ┌──────< 0x100004f70      cbz x0, 0x100004fa0
  │╎││╎│   0x100004f74      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
  │╎││╎│   0x100004f78      ldr x1, [x8, 0xb0]                        ; [0x1000180b0:4]=0xc124 ; "$\xc1" ; char *selector
  │╎││╎│   0x100004f7c      mov x20, x0
  │╎││╎│   0x100004f80      mov x2, x26
  │╎││╎│   0x100004f84      mov w3, 1
  │╎││╎│   0x100004f88      mov x4, 0
  │╎││╎│   0x100004f8c      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
  │╎││╎│   0x100004f90      mov x0, x25                               ; void *instance
  │╎││╎│   0x100004f94      bl sym.imp.objc_release                   ; void objc_release(void *instance)
  │╎││╎│   0x100004f98      mov x0, x20
 ┌───────< 0x100004f9c      b 0x100004fc8
 ││╎││╎│   ; CODE XREF from func.100004d24 @ 0x100004f70(x)
 │└──────> 0x100004fa0      adrp x8, 0x10000b000
  ╎││╎│   0x100004fa4      add x8, x8, 0x3f0                         ; 0x10000b3f0 ; "Failed to present: no view controller."
  ╎││╎│   0x100004fa8      sub x8, x8, 0x20
  ╎││╎│   0x100004fac      mov x9, 0x19
  ╎││╎│   0x100004fb0      movk x9, 0xd000, lsl 48
  ╎││╎│   0x100004fb4      add x0, x9, 0xd
  ╎││╎│   0x100004fb8      orr x1, x8, 0x8000000000000000
  ╎││╎│   0x100004fbc      mov x20, x24
  ╎││╎│   0x100004fc0      blr x23
  ╎││╎│   0x100004fc4      mov x0, x25
  ╎││╎│   ; CODE XREF from func.100004d24 @ 0x100004f9c(x)
 └───────> 0x100004fc8      bl sym.imp.objc_release                   ; void objc_release(void *instance)
   ╎││╎│   0x100004fcc      mov x0, x26                               ; void *instance
   ╎││╎│   0x100004fd0      bl sym.imp.objc_release                   ; void objc_release(void *instance)
   ╎││╎│   0x100004fd4      ldr x8, [x28, 8]
   ╎││╎│   0x100004fd8      mov x0, x22
   ╎││╎│   0x100004fdc      mov x1, x19
   ╎││╎│   0x100004fe0      blr x8
   ╎││╎│   ; CODE XREF from func.100004d24 @ 0x100004ea0(x)
   ╎└────> 0x100004fe4      mov x0, x21                               ; int64_t arg1
    │╎│   0x100004fe8      bl sym.Foundation.URL:_GenericAccessorW.bool____GenericAccessor__1 ; func.100006338
    │╎│   0x100004fec      sub sp, x29, 0x50
    │╎│   0x100004ff0      ldp x29, x30, [arg_20h]
    │╎│   0x100004ff4      ldp x20, x19, [arg_30h]
    │╎│   0x100004ff8      ldp x22, x21, [arg_40h]
    │╎│   0x100004ffc      ldp x24, x23, [arg_50h]
    │╎│   ; DATA XREFS from func.1000049b4 @ 0x100004a30(r), 0x100004ad4(r)
    │╎│   ; DATA XREFS from func.1000056d8 @ 0x1000058e0(r), 0x100005940(r)
    │╎│   ; DATA XREF from func.100005ab0 @ 0x100005b18(r)
    │╎│   0x100005000      ldp x26, x25, [var_60h]
    │╎│   0x100005004      ldp x28, x27, [sp], 0x60
    │╎│   0x100005008      ret
    │╎│   ; CODE XREF from func.100004d24 @ 0x100004dec(x)
    │╎└─> 0x10000500c      adrp x0, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
    │╎    0x100005010      add x0, x0, 0x1d0
    │╎    0x100005014      adrp x1, sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_ ; 0x100004000
    │╎    0x100005018      add x1, x1, 0x144
    │╎    0x10000501c      bl sym.imp.swift_once
    │└──< 0x100005020      b 0x100004df0
         ; CODE XREF from func.100004d24 @ 0x100004eec(x)
    └───> 0x100005024      adrp x0, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
          0x100005028      add x0, x0, 0x1b0
          0x10000502c      adrp x1, sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_ ; 0x100004000
          0x100005030      add x1, x1, 0
          0x100005034      bl sym.imp.swift_once
   └─────< 0x100005038      b 0x100004ef0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
URLsForDirectory:inDomains: selector (API anchor for the allowingReadAccessTo initializer):
0x10000c043 28 str.URLsForDirectory:inDomains:
0x100018130 8 reloc.fixup.URLsForDirectory:inDomains:

xrefs to URLsForDirectory:inDomains: (finds the allowingReadAccessTo lazy initializer):
sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_ 0x100004094 [DATA:r--] ldr x1, reloc.fixup.URLsForDirectory:inDomains:

appendingPathComponent selector (API anchor for the fileURL initializer):
0x10000a738 12 sym.imp.Foundation.URL.appendingPathComponent_...CSSF_
0x1000146b8 8 reloc.Foundation.URL.appendingPathComponent_...CSSF_

xrefs to appendingPathComponent import stub (finds the fileURL materializer ZTm_):
sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...ZTm_ 0x100004240 [CALL:--x] bl sym.imp.Foundation.URL.appendingPathComponent_...CSSF_

xrefs to ZTm_ (finds the swift_once guard Z_ that encodes the filename as inline constants):
sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_ 0x100004164 [CODE:--x] b sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...ZTm_
sym.MASTestApp.MastgTest.secretURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_ 0x100004188 [CODE:--x] b sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...ZTm_

HTML template string (contains innerHTML injection):
0x10000b261 hit4_0 "yId('username').innerHTML = name;</scrip"
 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
            ;-- section.0.__TEXT.__text:
            ; XREFS: 0x1000000d0  ICOD 0x100004274  ICOD 0x100004278  
            ; XREFS: ICOD 0x1000044c8  ICOD 0x1000044ec  ICOD 0x1000045e8  
            ; XREFS: ICOD 0x100004600  ICOD 0x1000046a4  ICOD 0x1000048bc  
            ; XREFS: ICOD 0x100005014  ICOD 0x10000502c  ICOD 0x100005030  
 324: E8AB2C.CE173A727EF27CB85DF8CD8_...Z_ ();
           0x100004000      stp x24, x23, [sp, -0x40]!                ; [00] -r-x section size 26400 named 0.__TEXT.__text
           0x100004004      stp x22, x21, [var_10h]
           0x100004008      stp x20, x19, [var_20h]
           0x10000400c      stp x29, x30, [var_30h]
           0x100004010      add x29, sp, 0x30
           0x100004014      mov x0, 0
           0x100004018      bl sym.imp.Foundation.URL...VMa
           0x10000401c      mov x19, x0
           0x100004020      ldur x24, [x0, -8]
           0x100004024      ldr x8, [x24, 0x40]
           0x100004028      mov x9, x8
           0x10000402c      adrp x16, segment.__DATA_CONST            ; 0x100014000
           0x100004030      ldr x16, [x16, 0x50]                      ; [0x100014050:4]=9 ; "\t"
           0x100004034      blr x16
           0x100004038      mov x9, sp
           0x10000403c      add x8, x8, 0xf
           0x100004040      and x8, x8, 0xfffffffffffffff0
           0x100004044      sub x20, x9, x8
           0x100004048      mov sp, x20
           0x10000404c      adrp x21, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x100004050      add x21, x21, 0x1b8
           0x100004054      mov x1, x21                               ; int64_t arg2
           0x100004058      bl sym.___swift_allocate_value_buffer
           0x10000405c      mov x0, x19                               ; int64_t arg1
           0x100004060      mov x1, x21                               ; int64_t arg2
           0x100004064      bl sym.___swift_project_value_buffer
           0x100004068      mov x21, x0
           0x10000406c      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x100004070      ldr x0, [x8, 0x1a0]                       ; [0x1000181a0:4]=254
                                                                      ; reloc.NSFileManager ; void *arg0
           0x100004074      bl sym.imp.objc_opt_self                  ; void *objc_opt_self(void *arg0)
           0x100004078      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x10000407c      ldr x1, [x8, 0x128]                       ; [0x100018128:4]=0xc0df ; reloc.fixup.defaultManager ; char *selector
           0x100004080      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x100004084      mov x29, x29
           0x100004088      bl sym.imp.objc_retainAutoreleasedReturnValue ; void objc_retainAutoreleasedReturnValue(void *instance)
           0x10000408c      mov x22, x0
           0x100004090      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x100004094      ldr x1, [x8, 0x130]                       ; [0x100018130:4]=0xc043 ; "C\xc0" ; char *selector
           0x100004098      mov w2, 9
           0x10000409c      mov w3, 1
           0x1000040a0      bl sym.imp.objc_msgSend                   ; void *objc_msgSend(void *instance, char *selector)
           0x1000040a4      mov x29, x29
           0x1000040a8      bl sym.imp.objc_retainAutoreleasedReturnValue ; void objc_retainAutoreleasedReturnValue(void *instance)
           0x1000040ac      mov x23, x0
           0x1000040b0      mov x0, x22                               ; void *instance
           0x1000040b4      bl sym.imp.objc_release                   ; void objc_release(void *instance)
           0x1000040b8      mov x0, x23
           0x1000040bc      mov x1, x19
           0x1000040c0      bl sym.imp.Foundation_...nconditionallyBridgeFromObjectiveCySayxGSo7NSArrayCSgFZ_ ; Foundation(...nconditionallyBridgeFromObjectiveCySayxGSo7NSArrayCSgFZ)
           0x1000040c4      mov x22, x0
           0x1000040c8      ldr x8, [x0, 0x10]
       ┌─< 0x1000040cc      cbz x8, 0x100004138
          0x1000040d0      mov x0, x23                               ; void *instance
          0x1000040d4      bl sym.imp.objc_release                   ; void objc_release(void *instance)
          0x1000040d8      ldr x8, [x22, 0x10]
      ┌──< 0x1000040dc      cbz x8, 0x100004134
      ││   0x1000040e0      ldrb w8, [x24, 0x50]
      ││   0x1000040e4      add x9, x8, 0x20
      ││   0x1000040e8      bic x8, x9, x8
      ││   0x1000040ec      ldr x9, [x24, 0x10]
      ││   0x1000040f0      add x1, x22, x8
      ││   0x1000040f4      mov x0, x20
      ││   0x1000040f8      mov x2, x19
      ││   0x1000040fc      blr x9
      ││   0x100004100      mov x0, x22                               ; void *arg0
      ││   0x100004104      bl sym.imp.swift_bridgeObjectRelease      ; void swift_bridgeObjectRelease(void *arg0)
      ││   0x100004108      ldr x8, [x24, 0x20]
      ││   0x10000410c      mov x0, x21
      ││   0x100004110      mov x1, x20
      ││   0x100004114      mov x2, x19
      ││   0x100004118      blr x8
      ││   0x10000411c      sub sp, x29, 0x30
      ││   0x100004120      ldp x29, x30, [var_30h]
      ││   0x100004124      ldp x20, x19, [var_20h]
      ││   0x100004128      ldp x22, x21, [var_10h]
      ││   0x10000412c      ldp x24, x23, [sp], 0x40
      ││   0x100004130      ret
      ││   ; CODE XREF from func.100004000 @ 0x1000040dc(x)
      └──> 0x100004134      brk 1
          ; CODE XREF from func.100004000 @ 0x1000040cc(x)
       └─> 0x100004138      mov x0, x22                               ; void *arg0
           0x10000413c      bl sym.imp.swift_bridgeObjectRelease      ; void swift_bridgeObjectRelease(void *arg0)
           0x100004140      brk 1
 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
            ; ICOD XREF from func.100004284 @ 0x100004604(r) ; sym.MASTestApp.MastgTest.mastg.completion_...FZ_
            ; ICOD XREF from func.100004d24 @ 0x100005018(r) ; sym.MASTestApp.MastgTest.showHtmlRegistrationView._6E8AB2C58CE173A727EF27CB85DF8CD8.username.completion
 36: E8AB2C.CE173A727EF27CB85DF8CD8_...Z_ ();
           0x100004144      adrp x1, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000 ; MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8(...Z)
           0x100004148      add x1, x1, 0x1d8
           0x10000414c      mov x2, 0x6e69                            ; 'in'
           0x100004150      movk x2, 0x6564, lsl 16                   ; 'de'
           0x100004154      movk x2, 0x2e78, lsl 32                   ; 'x.'
           0x100004158      movk x2, 0x7468, lsl 48                   ; 'ht'
           0x10000415c      mov x3, 0x6c6d                            ; 'ml'
           0x100004160      movk x3, 0xea00, lsl 48
       ┌─< 0x100004164      b sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...ZTm_ ; func.10000418c
            ; CODE XREF from func.100004144 @ 0x100004164(x) ; sym.MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_
            ; CODE XREF from func.100004168 @ 0x100004188(x) ; sym.MASTestApp.MastgTest.secretURL._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_
 248: E8AB2C.CE173A727EF27CB85DF8CD8_...ZTm_ (int64_t arg1, int64_t arg2, int64_t arg3);
; MASTestApp.MastgTest.fileURL._6E8AB2C58CE173A727EF27CB85DF8CD8(...ZT
; m)
           0x10000418c      stp x24, x23, [sp, -0x40]!
           0x100004190      stp x22, x21, [var_10h]
           0x100004194      stp x20, x19, [var_20h]
           0x100004198      stp x29, x30, [var_30h]
           0x10000419c      add x29, sp, 0x30
           0x1000041a0      mov x19, x3                               ; arg4
           0x1000041a4      mov x20, x2                               ; arg3
           0x1000041a8      mov x23, x1                               ; arg2
           0x1000041ac      mov x0, 0
           0x1000041b0      bl sym.imp.Foundation.URL...VMa
           0x1000041b4      mov x21, x0
           0x1000041b8      ldur x24, [x0, -8]
           0x1000041bc      ldr x8, [x24, 0x40]
           0x1000041c0      mov x9, x8
           0x1000041c4      adrp x16, segment.__DATA_CONST            ; 0x100014000
           0x1000041c8      ldr x16, [x16, 0x50]                      ; [0x100014050:4]=9 ; "\t"
           0x1000041cc      blr x16
           0x1000041d0      mov x9, sp
           0x1000041d4      add x8, x8, 0xf
           0x1000041d8      and x8, x8, 0xfffffffffffffff0
           0x1000041dc      sub x22, x9, x8
           0x1000041e0      mov sp, x22
           0x1000041e4      mov x1, x23                               ; int64_t arg2
           0x1000041e8      bl sym.___swift_allocate_value_buffer
           0x1000041ec      mov x0, x21                               ; int64_t arg1
           0x1000041f0      mov x1, x23                               ; int64_t arg2
           0x1000041f4      bl sym.___swift_project_value_buffer
           0x1000041f8      mov x23, x0
           0x1000041fc      adrp x8, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
           0x100004200      ldr x8, [x8, 0x1b0]                       ; [0x1000181b0:4]=0
                                                                      ; sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...z_
                                                                      [24] -rw- section size 929 named 24.__DATA.__data
           0x100004204      cmn x8, 1
       ┌─< 0x100004208      b.ne 0x10000426c
          ; CODE XREF from func.10000418c @ 0x100004280(x)
      ┌──> 0x10000420c      adrp x1, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
      ╎│   0x100004210      add x1, x1, 0x1b8                         ; int64_t arg2
      ╎│   0x100004214      mov x0, x21                               ; int64_t arg1
      ╎│   0x100004218      bl sym.___swift_project_value_buffer
      ╎│   0x10000421c      mov x1, x0
      ╎│   0x100004220      ldr x8, [x24, 0x10]
      ╎│   0x100004224      mov x0, x22
      ╎│   0x100004228      mov x2, x21
      ╎│   0x10000422c      blr x8
      ╎│   0x100004230      mov x8, x23
      ╎│   0x100004234      mov x0, x20
      ╎│   0x100004238      mov x1, x19
      ╎│   0x10000423c      mov x20, x22
      ╎│   0x100004240      bl sym.imp.Foundation.URL.appendingPathComponent_...CSSF_ ; Foundation.URL.appendingPathComponent(...CSSF)
      ╎│   0x100004244      ldr x8, [x24, 8]
      ╎│   0x100004248      mov x0, x22
      ╎│   0x10000424c      mov x1, x21
      ╎│   0x100004250      blr x8
      ╎│   0x100004254      sub sp, x29, 0x30
      ╎│   0x100004258      ldp x29, x30, [var_30h]
      ╎│   0x10000425c      ldp x20, x19, [var_20h]
      ╎│   0x100004260      ldp x22, x21, [var_10h]
      ╎│   0x100004264      ldp x24, x23, [sp], 0x40
      ╎│   0x100004268      ret
      ╎│   ; CODE XREF from func.10000418c @ 0x100004208(x)
      ╎└─> 0x10000426c      adrp x0, sym.__METACLASS_DATA__TtC10MASTestAppP33_9471609302C95FC8EC1D59DD4CF2A2DB19ResourceBundleClass ; 0x100018000
          0x100004270      add x0, x0, 0x1b0
          0x100004274      adrp x1, sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...Z_ ; 0x100004000
          0x100004278      add x1, x1, 0
          0x10000427c      bl sym.imp.swift_once
      └──< 0x100004280      b 0x10000420c

Evaluation

The test fails because the app grants the WebView read access to the entire Documents directory using loadFileURL(_:allowingReadAccessTo:). docDir points to the app's Documents directory, and it's passed directly as allowingReadAccessTo in webView.loadFileURL(url, allowingReadAccessTo: docDir). This grants the WebView read access to the entire Documents directory, which also contains secret.txt (written in createSecretFile). An attacker can inject <iframe src='./secret.txt'></iframe> as their name to expose this file.

The attack succeeds because of the combination of this overly broad read access and the fact that attacker controlled input is inserted into the page. The decompiled code and local HTML show that attacker-controlled input (the username value) is inserted into the page by assigning it directly to innerHTML via JavaScript. Because this value is not HTML-escaped, tags such as <iframe>, <img>, and <script> are interpreted as markup, allowing the attacker's payload to be rendered as HTML. See Attacker-Controlled Input in a WebView Leading to Unintended Navigation for more details on the HTML injection aspect of the vulnerability.

The following analysis complements the one in Attacker-Controlled Input in a WebView Leading to Unintended Navigation by focusing on the overly broad file access aspect of the vulnerability.

AI-Decompiled Code Analysis

About ai-decompiled.swift

The ai-decompiled.swift file is an AI-assisted reconstruction derived from showWebView.asm, docDir-init.asm, and fileURL-init.asm and is provided only as a convenience for understanding the logic. It may be inaccurate or incomplete; the assembly and the original binary are the authoritative sources for analysis.

  1. On lines 10–12, docDir is initialized to FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!, which resolves to the app's Documents directory — the same directory that contains secret.txt.
  2. On lines 14–16, fileURL is built by appending "index.html" directly to docDir, so the loaded HTML file and any sensitive sibling files share the same parent directory.
  3. On line 35, webView.loadFileURL(url, allowingReadAccessTo: docDir) passes docDir as the read-access root. Because docDir is the entire Documents directory, the WebView can reach any file inside it, including secret.txt.

Disassembly Analysis

We can see the call to loadFileURL:allowingReadAccessToURL: at 0x100004f20 (in showWebView.asm). To determine which path is being granted read access, we need to trace both arguments.

Just before the call, the arguments are arranged as follows (from showWebView.asm):

0x100004f10      ldr x1, [x8, 0x118]   ; reloc.fixup.loadFileURL:allowingReadAccessToURL:
0x100004f14      mov x0, x25           ; WKWebView instance
0x100004f18      mov x2, x26           ; urlWithUsername (NSURL)
0x100004f1c      mov x3, x20           ; allowingReadAccessTo (NSURL, = docDir)
0x100004f20      bl sym.imp.objc_msgSend

Tracing x3 (the allowingReadAccessTo argument):

x20 is set at 0x100004f08 by bridging a Swift lazy static URL to an NSURL. Rather than relying on the developer-assigned symbol name, we locate its initializer through the iOS API it calls: axt @ 0x100018130 (the URLsForDirectory:inDomains: reloc) leads directly to func.100004000 (the xref result is in output.txt; the disassembly is in docDir-init.asm).

In func.100004000 (from docDir-init.asm), the key sequence is:

0x100004070      ldr x0, [x8, 0x1a0]   ; reloc.NSFileManager class
0x100004074      bl sym.imp.objc_opt_self
0x10000407c      ldr x1, [x8, 0x128]   ; reloc.fixup.defaultManager selector
0x100004080      bl sym.imp.objc_msgSend
...
0x100004094      ldr x1, [x8, 0x130]   ; URLsForDirectory:inDomains: selector
0x100004098      mov w2, 9
0x10000409c      mov w3, 1
0x1000040a0      bl sym.imp.objc_msgSend

This is the pattern for calling:

FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)

because:

  • 9 is NSDocumentDirectory
  • 1 is NSUserDomainMask

The function bridges the returned NSArray to a Swift array and stores its first element as the docDir lazy static. This confirms that x3 (the allowingReadAccessTo argument) is the app's Documents directory.

You can find the enum values above in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSPathUtilities.h.

typedef NS_ENUM(NSUInteger, NSSearchPathDirectory) {
    NSApplicationDirectory = 1,             // supported applications (Applications)
    NSDemoApplicationDirectory,             // unsupported applications, demonstration versions (Demos)
    NSDeveloperApplicationDirectory,        // developer applications (Developer/Applications). DEPRECATED - there is no one single Developer directory.
    NSAdminApplicationDirectory,            // system and network administration applications (Administration)
    NSLibraryDirectory,                     // various documentation, support, and configuration files, resources (Library)
    NSDeveloperDirectory,                   // developer resources (Developer) DEPRECATED - there is no one single Developer directory.
    NSUserDirectory,                        // user home directories (Users)
    NSDocumentationDirectory,               // documentation (Documentation)
    NSDocumentDirectory,                    // documents (Documents)
...
typedef NS_OPTIONS(NSUInteger, NSSearchPathDomainMask) {
    NSUserDomainMask = 1,       // user's home directory --- place to install user's personal items (~)
    NSLocalDomainMask = 2,      // local to the current machine --- place to install items available to everyone on this machine (/Library)
    NSNetworkDomainMask = 4,    // publicly available location in the local area network --- place to install items available on the network (/Network)
    NSSystemDomainMask = 8,     // provided by Apple, unmodifiable (/System)
    NSAllDomainsMask = 0x0ffff  // all domains: all of the above and future items
};

Tracing x2 (the file URL argument):

x26 is set at 0x100004edc by bridging a Swift lazy static URL to an NSURL. We locate its initializer via the iOS API it calls: axt @ 0x10000a738 (the appendingPathComponent import stub) leads to func.10000418c (the materializer ZTm_). axt @ 0x10000418c then reveals its swift_once guard func.100004144 (Z_) (all xref results are in output.txt). Z_ encodes the filename "index.html" inline as immediate character constants (from fileURL-init.asm, which also includes ZTm_ appended after):

0x10000414c      mov x2, 0x6e69                            ; 'in'
0x100004150      movk x2, 0x6564, lsl 16                   ; 'de'
0x100004154      movk x2, 0x2e78, lsl 32                   ; 'x.'
0x100004158      movk x2, 0x7468, lsl 48                   ; 'ht'
0x10000415c      mov x3, 0x6c6d                            ; 'ml'

The ZTm_ materializer then loads the docDir lazy static and passes the encoded filename to appendingPathComponent:

0x100004200      ldr x8, [x8, 0x1b0]   ; sym.MASTestApp.MastgTest.docDir._6E8AB2C58CE173A727EF27CB85DF8CD8_...z_
...
0x100004240      bl sym.imp.Foundation.URL.appendingPathComponent_...CSSF_

This confirms that fileURL is built by appending "index.html" to docDir. Therefore, the app loads index.html from the Documents directory and grants the WebView read access to the entire Documents directory.

How to Fix

To prevent this:

  • Move index.html into a dedicated subdirectory (for example, Library/Application Support/webContent/) and pass that subdirectory as allowingReadAccessTo instead of the full Documents directory. This ensures the WebView can't reach secret.txt or any other user data stored in Documents.
  • Replace innerHTML with textContent in the JavaScript injection so that user input is always treated as plain text, not markup.
fix.diff
 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
diff --git a/demos/ios/MASVS-PLATFORM/MASTG-DEMO-0095/MastgTest.swift b/demos/ios/MASVS-PLATFORM/MASTG-DEMO-0095/MastgTest.swift
index a2ff74e9..6151e620 100644
--- a/demos/ios/MASVS-PLATFORM/MASTG-DEMO-0095/MastgTest.swift
+++ b/demos/ios/MASVS-PLATFORM/MASTG-DEMO-0095/MastgTest.swift
@@ -5,7 +5,9 @@ import WebKit
 struct MastgTest {

     private static let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
-    private static let fileURL = docDir.appendingPathComponent("index.html")
+    // PASS: [MASTG-TEST-0333] webDir is a dedicated subdirectory; allowingReadAccessTo is scoped to it, keeping secret.txt out of reach.
+    private static let webDir = docDir.appendingPathComponent("webContent", isDirectory: true)
+    private static let fileURL = webDir.appendingPathComponent("index.html")
     private static let secretURL = docDir.appendingPathComponent("secret.txt")

     public static func mastgTest(completion: @escaping (String) -> Void) {
@@ -29,7 +31,8 @@ struct MastgTest {
             return
           }
           let webView = WKWebView()
-          webView.loadFileURL(urlWithUsername, allowingReadAccessTo: docDir)
+          // PASS: [MASTG-TEST-0333] allowingReadAccessTo is scoped to webDir, not the broader docDir.
+          webView.loadFileURL(urlWithUsername, allowingReadAccessTo: webDir)

             let vc = UIViewController()
             vc.view = webView
@@ -62,9 +65,10 @@ struct MastgTest {
         </html>
         <script>
             const name = new URLSearchParams(window.location.search).get('username');
-            document.getElementById('username').innerHTML = name;
+            document.getElementById('username').textContent = name;
         </script>
         """
+        try? FileManager.default.createDirectory(at: webDir, withIntermediateDirectories: true, attributes: nil)
         try? htmlContent.write(to: fileURL, atomically: true, encoding: .utf8)
     }

To apply the fix from fix.diff to the original MastgTest.swift file, run the following command from this demo directory:

patch ../MASTG-DEMO-0095/MastgTest.swift -o MastgTest-fixed.swift < fix.diff