UIWebView/WKWebView存在控件跨域訪問漏洞(CNNVD-201801-515)
漏洞描述
? 國家信息安全漏洞庫(CNNVD)收錄,iOS 平臺WebView組件漏洞(UIWebView/ WKWebView)跨域訪問漏洞(CNNVD-201801-515):成成功利用該漏洞的攻擊者可以遠程獲取手機應用沙盒內所有本地文件系統內容,包括瀏覽器的Cookies、用戶的配置文件、文檔等敏感信息。iOS平臺使用了WebView組件的應用可能均受影響。
漏洞介紹
? WebView是iOS用于顯示網頁的控件,是一個基于Webkit引擎、展現web頁面的控件。WebView控件功能除了具有一般View的屬性和設置外,還可對URL請求、頁面加載、渲染、頁面交互進行處理。
? iOS平臺的WebView組件(UIWebView/WKWebView)存在控件跨域訪問漏洞(CNNVD-201801-515),漏洞源于UIWebView 默認允許“file://” 域發起跨域請求,而WKWebView可通過手動設置允許“file://”域發起跨域請求。攻擊者可利用App文件下載機制將惡意文件寫入沙盒內并誘導用戶打開,當用戶打開惡意文件時,其中的惡意代碼可通過AJAX向“file://”域發起請求,從而遠程獲取App沙盒內所有的本地敏感數據。
漏洞等級
根據CNNVD漏洞分級規范,該漏洞的危害評級為高危。
漏洞驗證
UIWebView
做iOS開發經常用到UIWebView,大多時候是加載外部地址,但是有一些時候也會用來加載本地的html文件。
UIWebView加載外部地址的時候遵循了“同源”策略,而加載本地網頁的時候卻繞夠了“同源”策略,導致可以訪問系統任意路徑。
這就是UIWebView中存在的UXSS漏洞。
惡意網頁
以下是HTML 代碼快。
<!DOCTYPE html>
<html>
<body>
<script>
// 這個可以是?手機任意?一個?文件地址,如/etc/hosts、/etc/passwd
var localfile = "file:///etc/passwd"
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var txt = xhr.responseText;
alert(txt);
}
}
try {
xhr.open("GET", localfile, true);
xhr.send();
} catch (ex) {
alert(ex.message);
}
</script>
</body>
</html>
iOS源碼(部分)
本文通過APP中UIWebView去讀取本地index.html文件方式。
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSURL *url = [[NSURL alloc] initWithString:filePath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
運行結果
測試結果
UIWebView 允許“file://” 域發起跨域請求,通過 JavaScript可以訪問沙箱內的文件,甚?至可以靜默上傳文件到遠端,默認存在此漏洞。
WKWebView
從 iOS 8.0 和 OS X 10.10 開始,在你的APP中使用 WKWebView 添加網頁內容,不要使用 UIWebView 或 WebView。
WKWebView可將網頁處理限制在App的網頁視圖中,從而確保不安全的網站內容不會影響到App的其他部分。此外,iOS、macOS和Mac Catalyst均支持WKWebView。
WKWebView 可通過手動設置允許“file://”域發起跨域請求。
WKWebView 默認 allowFileAccessFromFileURLs 和 allowUniversalAccessFromFileURLs 選項為 false。
惡意網頁
以下是HTML 代碼快。
<!DOCTYPE html>
<html>
<body>
<script>
// 這個可以是?手機任意?一個?文件地址,如/etc/hosts、/etc/passwd
var localfile = "file:///etc/passwd"
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var txt = xhr.responseText;
alert(txt);
}
}
try {
xhr.open("GET", localfile, true);
xhr.send();
} catch (ex) {
alert(ex.message);
}
</script>
</body>
</html>
iOS源碼
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.preferences.javaScriptEnabled = YES;
configuration.preferences.javaScriptCanOpenWindowsAutomatically = YES;
configuration.suppressesIncrementalRendering = YES; // 是否支持記憶讀取
[configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];
if (@available(iOS 10.0, *)) {
[configuration setValue:@YES forKey:@"allowUniversalAccessFromFileURLs"];
}
WKWebView *wkweb = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
wkweb.center = self.view.center;
wkweb.UIDelegate = self;//代理,需要實現alert
[self.view addSubview:wkweb];
wkweb.backgroundColor = UIColor.redColor;
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *filePath =[resourcePath stringByAppendingPathComponent:@"index.html"];
NSMutableString *htmlstring=[[NSMutableString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSURL *baseUrl=[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
[wkweb loadHTMLString:htmlstring baseURL:baseUrl];
運行結果
無信息展示。
測試結果
無法復現漏洞。
漏洞總結
? 市面上大多數使用HTML5技術開發的應用均使用WebView進行HTML5頁面的展示。除了從遠程服務器加載Web頁面,WebView還可以通過修改特定配置,從文件中進行HTML5頁面的加載。在未正確配置WebView的情況下,導致WebView同源策略失效,導致HTTP協議、file協議跨源攻擊。該漏洞導致WebView能夠訪問當前應用內部數據,如果WebView加載了來源不明的HTML文件,可能導致當前應用內部數據被攻擊者竊取,如身份認證信息、加密密鑰、用戶隱私信息等。
? 成功利用該漏洞的攻擊者,可以遠程獲取手機應用沙盒內所有本地文件系統內容,包括瀏覽器的Cookies、用戶的配置文件、文檔等敏感信息。iOS平臺的應用如果在使用WebView組件時,未考慮上述情況,則會受到漏洞影響。
附件
建議
- 避免開啟文件域的全局訪問;
- 避免在WebView中加載來自外部傳入的“file://”域頁面;
- 對于敏感信息數據,建議進行加密處理后存儲,或使用iOS平臺推薦的KeyChain服務進行存儲,可緩解漏洞可能造成的破壞;
- WKWebView 檢查 allowingReadAccessToURL 路徑是否安全,WKWebView 默認不允許“file://”域發起跨域請求。
- WKWebView 默認 allowFileAccessFromFileURLs 和 allowUniversalAccessFromFileURLs 選項為 false。
官方警告
蘋果表示,App仍在使用已棄用的UIWebView API嵌入網絡內容的開發者,應盡快更新為WKWebView以提升安全性和穩定性。
WKWebView可將網頁處理限制在App的網頁視圖中,從而確保不安全的網站內容不會影響到App的其他部分。此外,iOS、macOS和Mac Catalyst均支持WKWebView。
蘋果提醒稱,2020年4月起App Store將不再接受使用UIWebView的新App,2020年12月起將不再接受使用UIWebView的App更新。
修復方案
- 禁用從外部打開HTML文件;(切斷攻擊入口)
- 針對本地HTML文件中腳本做一些權限限制;(初步防范措施)
- 新增一個NSURLProtocol, 專門用來處理本地網頁的加載,根據同源策略來安全地加載本地文件。(徹底的解決方案)
方案3:新增一個NSURLProtocol, 專門用來處理本地網頁的加載,根據同源策略來安全地加載本地文件。
我們知道IOS中對于各種協議(http,https, ftp, file)的處理都是通過NSURLProtocol來實現的,
每一種對應了一個NSURLProtocol,有以下幾個重要的方法:
+ (BOOL)registerClass:(Class)protocolClass
注冊NSURLProtocol,
+ (void)unregisterClass:(Class)protocolClass
反注冊NSURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
表示是否走該NSURLProtocol的處理邏輯,返回YES,表示走,NO 表示不走,
- (void)startLoading
表示開始加載請求,由系統調用該方法,我們只需在該方法內部做網絡數據請求就可以
- (void)stopLoading
表示停止加載請求,由系統調用該方法,我們只需在該方法內部做一些取消請求操作
我們新建一個類派生自NSURLProtocol, 暫且命名為SeMobSandBoxFileProtocol
全局使用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 注冊我們的協議
[NSURLProtocol registerClass:[SeMobSandBoxFileProtocol class]];
return YES;
}
局部使用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 注冊我們的協議
[NSURLProtocol registerClass:[SeMobSandBoxFileProtocol class]];
}
SeMobSandBoxFileProtocol.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SeMobSandBoxFileProtocol : NSURLProtocol
@end
NS_ASSUME_NONNULL_END
SeMobSandBoxFileProtocol.m
#import "SeMobSandBoxFileProtocol.h"
@implementation SeMobSandBoxFileProtocol
+ (NSArray *)supportedScheme {
return [NSArray arrayWithObjects:@"file", nil];
}
/// 表示是否走該NSURLProtocol的處理邏輯
/// 返回:YES 表示走。
/// 返回:NO 表示不走。
/// @param request 請求
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
NSURL* url=[request URL];
NSUInteger index = [[self supportedScheme] indexOfObject:[url scheme]];
if (index != NSNotFound) {
NSURL* baseURL = [[request mainDocumentURL] URLByDeletingLastPathComponent];
//得到主資源的路徑
NSString* baseString = [[baseURL absoluteString] lowercaseString];
NSRange sharpRange = [baseString rangeOfString:@"#"];
if (sharpRange.length) {
// 路徑過濾處理,去掉#號以及#號后面的內容
baseString = [baseString substringToIndex:sharpRange.location];
}
if([baseURL isFileURL]) {
// 判斷子資源路徑是否包含主資源路徑前綴
BOOL ok = ![[[url absoluteString] lowercaseString] hasPrefix:baseString];
return ok;
}
else {
return baseString.length>0;
}
}
return NO;
}
// 表示停止加載請求,由系統調用該方法,我們只需在該方法內部做一些取消請求操作
- (void)stopLoading {
}
// 表示開始加載請求,由系統調用該方法,我們只需在該方法內部做網絡數據請求就可以
-(void)startLoading {
NSError * _Nonnull error = [NSError errorWithDomain:@"CFNetwork"
code:kCFURLErrorUnknown
userInfo:@{@"NSErrorFailingURLKey":self.request.URL}];
[[self client] URLProtocol:self didFailWithError:error];
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
@end
代碼分析
總體思路是根據主資源與子資源的文件路徑判斷它們是不是父子目錄關系,如果是的話,就允許訪問子資源,否則就不允許,這樣就阻止了子資源訪問主資源對應目錄以外的目錄,因為判斷是否為父子目錄關系,是根據是否包含目錄前綴來判斷的,所以需要對路徑進行過濾處理,把路徑中#號后面的內容連同#一起過濾掉。