前言
Android在目前的市場(chǎng)上占有率很高扼鞋,用戶數(shù)量龐大,而在該平臺(tái)下的應(yīng)用程序開(kāi)發(fā)成本低愤诱,開(kāi)發(fā)難度低云头,發(fā)布容易,缺少監(jiān)管和審查淫半,導(dǎo)致大量低質(zhì)量App流入市場(chǎng)溃槐,這些App由于開(kāi)發(fā)者缺乏安全編程技能或缺乏測(cè)試和審查,可能存在著一些嚴(yán)重的漏洞科吭,對(duì)用戶的隱私以及財(cái)產(chǎn)安全造成巨大風(fēng)險(xiǎn)昏滴。因此猴鲫,移動(dòng)應(yīng)用尤其是Android平臺(tái)下的應(yīng)用的開(kāi)發(fā)應(yīng)該對(duì)此引起高度重視。
Android常見(jiàn)漏洞
越權(quán)繞過(guò):沒(méi)有對(duì)調(diào)用activity的組件進(jìn)行權(quán)限驗(yàn)證谣殊,就會(huì)造成驗(yàn)證的安全問(wèn)題
釣魚(yú)欺詐:?jiǎn)?dòng)一個(gè)activity時(shí)拂共,加入標(biāo)志位FLAG_ACTIVITY_NEW_TASK,能夠使被啟動(dòng)的activity立馬呈現(xiàn)給用戶姻几,可用于釣魚(yú)欺詐
拒絕服務(wù):本地組件啟動(dòng)時(shí)沒(méi)有對(duì)Intent.getXXXExtra()獲取或處理的數(shù)據(jù)進(jìn)行異常捕獲宜狐,從而導(dǎo)致攻擊者可通過(guò)向受害者應(yīng)用發(fā)送空數(shù)據(jù)、異成甙疲或者畸形數(shù)據(jù)來(lái)使該應(yīng)用crash的目的
權(quán)限提升:當(dāng)一個(gè)具有高權(quán)限的service是被導(dǎo)出的時(shí)肌厨,如果沒(méi)對(duì)調(diào)用這個(gè)Service進(jìn)行權(quán)限限制和調(diào)用者的身份驗(yàn)證時(shí),那么惡意的app將具有調(diào)用高權(quán)限的service的能力來(lái)執(zhí)行高權(quán)限行為等
權(quán)限泄露:主要存在于某些具有高權(quán)限操作的組件被導(dǎo)出豁陆,而系統(tǒng)沒(méi)有進(jìn)行嚴(yán)格的身份驗(yàn)證和權(quán)限控制而導(dǎo)致的其他應(yīng)用可以利用該組件而產(chǎn)生越權(quán)操作的行為柑爸。
Android常見(jiàn)漏洞檢測(cè)方法
靜態(tài)分析
利用apktool、dex2jar盒音、jd-gui表鳍、smali2dex等靜態(tài)分析工具對(duì)應(yīng)用進(jìn)行反編譯,并對(duì)反編譯后的java文件祥诽、xml文件等文件靜態(tài)掃描分析譬圣,通過(guò)關(guān)鍵詞搜索等靜態(tài)方式將具有安全隱患的代碼進(jìn)行摘錄并存入到檢測(cè)平臺(tái)后臺(tái),為后續(xù)的安全檢測(cè)報(bào)告提供數(shù)據(jù)依據(jù)雄坪。動(dòng)態(tài)分析
對(duì)應(yīng)用軟件安裝厘熟、運(yùn)行過(guò)程的行為監(jiān)測(cè)和分析。檢測(cè)的方式包括沙箱模型和虛擬機(jī)方式维哈。虛擬機(jī)方式通過(guò)建立與Android手機(jī)終端軟件運(yùn)行環(huán)境幾乎一樣的虛擬執(zhí)行環(huán)境绳姨,手機(jī)應(yīng)用軟件在其中獨(dú)立運(yùn)行,從外界觀察應(yīng)用程序的執(zhí)行過(guò)程和動(dòng)態(tài)阔挠,進(jìn)而記錄應(yīng)用程序可能表現(xiàn)出來(lái)的惡意行為飘庄。人工分析
專業(yè)安全人員對(duì)待檢測(cè)應(yīng)用,對(duì)其進(jìn)行安裝购撼、運(yùn)行和試用跪削,通過(guò)在試用過(guò)程中,逐步掌握應(yīng)用的特點(diǎn)迂求,并通過(guò)專業(yè)經(jīng)驗(yàn)碾盐,來(lái)圈定檢測(cè)重點(diǎn)。人工專業(yè)檢測(cè)在涵蓋基礎(chǔ)檢測(cè)和深度檢測(cè)的全部檢測(cè)項(xiàng)的同時(shí)揩局,兼顧側(cè)重點(diǎn)檢測(cè)毫玖,給予應(yīng)用更全面、更專業(yè)、更貼合應(yīng)用的量身打造的檢測(cè)服務(wù)孕豹。
模糊測(cè)試
簡(jiǎn)介
模糊測(cè)試(Fuzzing)涩盾,是一種通過(guò)向目標(biāo)系統(tǒng)提供非預(yù)期的輸入并監(jiān)視異常結(jié)果來(lái)發(fā)現(xiàn)軟件漏洞的方法。我個(gè)人理解励背,這是一種隨機(jī)或伴隨機(jī)的測(cè)試方法春霍,與Monkey等隨機(jī)測(cè)試工具有異曲同工之妙,只是其關(guān)注點(diǎn)不同叶眉。模糊測(cè)試的執(zhí)行過(guò)程:
1.測(cè)試工具通過(guò)隨機(jī)或是半隨機(jī)的方式生成大量數(shù)據(jù)址儒;
2.測(cè)試工具將生成的數(shù)據(jù)發(fā)送給被測(cè)試的系統(tǒng)(輸入);
3.測(cè)試工具檢測(cè)被測(cè)系統(tǒng)的狀態(tài)(如是否能夠響應(yīng)衅疙,響應(yīng)是否正確等)莲趣;
4.根據(jù)被測(cè)系統(tǒng)的狀態(tài)判斷是否存在潛在的安全漏洞
模糊測(cè)試工具IntentFuzzer
本文將介紹一種模糊測(cè)試工具,IntentFuzzer饱溢。
簡(jiǎn)介
這個(gè)工具是針對(duì)Intent的Fuzzer喧伞。它通常可以發(fā)現(xiàn)能夠?qū)е孪到y(tǒng)崩潰的bug绩郎,部分安全漏洞潘鲫,以及設(shè)備、應(yīng)用程序或者是定制平臺(tái)的運(yùn)行中的問(wèn)題肋杖。該工具能夠針對(duì)一個(gè)簡(jiǎn)單組件或者是所有安裝組件進(jìn)行fuzz測(cè)試溉仑。它也適用于BroadcastReceiver,但針對(duì)Service只有較少的覆蓋状植,Service通常更加廣泛地應(yīng)用Binder接口而不是針對(duì)IPC的Intent浊竟。原版的工具只能針對(duì)一個(gè)Activity進(jìn)行fuzz測(cè)試,一次不能針對(duì)所有的Activity進(jìn)行測(cè)試津畸。另外振定,也能應(yīng)用這個(gè)接口來(lái)啟動(dòng)Instrumentation,雖然列出了ContentProvider洼畅,但是它們不是一個(gè)基于Intent的IPC機(jī)制吩案,因此并不能應(yīng)用該工具進(jìn)行fuzz測(cè)試。MindMac在此基礎(chǔ)上進(jìn)行了一些修改帝簇,使其能夠針對(duì)一個(gè)應(yīng)用的一個(gè)簡(jiǎn)單組件或者是所有組件進(jìn)行fuzz測(cè)試,同時(shí)具有區(qū)分系統(tǒng)應(yīng)用和非系統(tǒng)應(yīng)用的能力靠益。MindMac修改后的版本僅針對(duì)Activity丧肴、BroadcastReceiver、Service胧后。原理
列舉出系統(tǒng)上所有公開(kāi)的芋浮、能夠從應(yīng)用獲取到的Activity、BroadcastReceiver、Service纸巷、Instrumentation镇草、ContentProvider。工具將通過(guò)Intent嘗試啟動(dòng)所有可以獲取到的組件瘤旨,從而觸發(fā)某些難以發(fā)掘的漏洞梯啤。觸發(fā)一般有兩類漏洞,一類是拒絕服務(wù)存哲,一類的權(quán)限提升因宇。拒絕服務(wù)危害性比較低,更多的只是影響應(yīng)用服務(wù)質(zhì)量祟偷;而權(quán)限提升將使得沒(méi)有該權(quán)限的應(yīng)用可以通過(guò)Intent觸發(fā)擁有該權(quán)限的應(yīng)用察滑,從而幫助其完成越權(quán)行為。如果該工具能夠輕易從外部啟動(dòng)特定應(yīng)用的內(nèi)部組件修肠,尤其是有較高權(quán)限的組件時(shí)贺辰,很可能在此處發(fā)現(xiàn)漏洞。功能
對(duì)某個(gè)組件或某個(gè)應(yīng)用的某類組件發(fā)起Fuzz嵌施,分為Null Fuzz和Serialize
Fuzz饲化,即在Intent中不攜帶參數(shù)和攜帶序列化對(duì)象參數(shù),然后嘗試使用該Intent啟動(dòng)Activity艰管、BroadcastReceiver滓侍、Service。代碼
獲取所有應(yīng)用及其組件
public static List<AppInfo> getPackageInfo(Context context, int type){
List<AppInfo> pkgInfoList = new ArrayList<AppInfo>();
List<PackageInfo> packages = context.getPackageManager().getInstalledPackages(
// PackageManager.GET_DISABLED_COMPONENTS |
PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS
| PackageManager.GET_INSTRUMENTATION
| PackageManager.GET_SERVICES);
for(int i=0;i<packages.size();i++) {
PackageInfo packageInfo = packages.get(i);
if (type == SYSTEM_APPS) {
if((packageInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 1) {
pkgInfoList.add(fillAppInfo(packageInfo, context));
}
}else if(type == NONSYSTEM_APPS){
if((packageInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
pkgInfoList.add(fillAppInfo(packageInfo, context));
}
}else {
pkgInfoList.add(fillAppInfo(packageInfo, context));
}
}
構(gòu)建Intent
private void initView(){
typeSpinner = (Spinner) findViewById(R.id.type_select);
cmpListView = (ListView) findViewById(R.id.cmp_listview);
fuzzAllNullBtn = (Button) findViewById(R.id.fuzz_all_null);
fuzzAllSeBtn = (Button) findViewById(R.id.fuzz_all_se);
cmpListView.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ComponentName toSend = null;
Intent intent = new Intent();
String className = cmpAdapter.getItem(position).toString();
for (ComponentName cmpName : components) {
if (cmpName.getClassName().equals(className)) {
toSend = cmpName;
break;
}
}
intent.setComponent(toSend);
if (sendIntentByType(intent, currentType)) {
Toast.makeText(FuzzerActivity.this, "Sent Null " + intent, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(FuzzerActivity.this, "Send " + intent + " Failed!", Toast.LENGTH_LONG).show();
}
}
});
cmpListView.setOnItemLongClickListener(new OnItemLongClickListener(){
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// TODO Auto-generated method stub
ComponentName toSend = null;
Intent intent = new Intent();
String className = cmpAdapter.getItem(position).toString();
for (ComponentName cmpName : components) {
if (cmpName.getClassName().equals(className)) {
toSend = cmpName;
break;
}
}
intent.setComponent(toSend);
intent.putExtra("test", new SerializableTest());
if (sendIntentByType(intent, currentType)) {
Toast.makeText(FuzzerActivity.this, "Sent Serializeable " + intent, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(FuzzerActivity.this, "Send " + intent + " Failed!", Toast.LENGTH_LONG).show();
}
return true;
}
});
fuzzAllNullBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
for(ComponentName cmpName : components){
Intent intent = new Intent();
intent.setComponent(cmpName);
if (sendIntentByType(intent, currentType)) {
Toast.makeText(FuzzerActivity.this, "Sent Null " + intent, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(FuzzerActivity.this, R.string.send_faild, Toast.LENGTH_LONG).show();
}
}
}
});
fuzzAllSeBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
for(ComponentName cmpName : components){
Intent intent = new Intent();
intent.setComponent(cmpName);
intent.putExtra("test", new SerializableTest());
if (sendIntentByType(intent, currentType)) {
Toast.makeText(FuzzerActivity.this, "Sent Serializeable " + intent, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(FuzzerActivity.this, R.string.send_faild, Toast.LENGTH_LONG).show();
}
}
}
});
}
發(fā)送請(qǐng)求
private boolean sendIntentByType(Intent intent, String type) {
try {
switch (ipcNamesToTypes.get(type)) {
case Utils.ACTIVITIES:
startActivity(intent);
return true;
case Utils.RECEIVERS:
sendBroadcast(intent);
return true;
case Utils.SERVICES:
startService(intent);
return true;
default:
return true;
}
} catch (Exception e) {
//e.printStackTrace();
return false;
}
}