Android Accessibility點擊劫持攻防
作者:瘦蛟舞@小米安全
0x00 快手互粉劫持事件
此文章源於一起Accessibility模擬點擊劫持.
補刀小視頻和快手互為競品應用,目標群體類似.而快手用戶量級明顯多於補刀.快手很多用戶有互粉需求,於是補刀小視頻開發了快手互粉助手來吸引快手用戶安裝.互粉助手這個功能主要是利用Accessibility.
之前接觸Android輔助功能AccessibilityService模擬點擊都是用於諸如應用市場的免root自動安裝功能或者紅包助手自動搶紅包功能,另外還有一些惡意軟體會使用這個特性.
此次用於劫持其他App達到推廣自身的目的倒是令人感到好奇於是分析了一下寫出此文.供以後有類似場景需求的做參考.
劫持男豬腳補刀小視頻利用Android模擬點擊的介面做了一個快手互粉的功能,下面先分析一下補刀APP是如何完成此功能的.
互粉功能入口com.yy.budao/.ui.tools.AddFansWebActivity
AccessbilityService輔助功能的授權需要用戶手動去完成.(通過一些Android系統漏洞可以繞過此步驟)
通過快手的scheme偽協議kwai://profile/uid啟動到需要互粉用戶的個人界面
08-14 10:29:03.869 893-3614/? I/ActivityManager: START u0 {act=android.intent.action.VIEW dat=kwai://profile/18070291 pkg=com.smile.gifmaker cmp=com.smile.gifmaker/com.yxcorp.gifshow.activity.ProfileActivity} from pid 1198
08-14 10:29:03.989 893-917/? I/ActivityManager: Displayed com.smile.gifmaker/com.yxcorp.gifshow.activity.ProfileActivity: +106ms
adb手動驗證一下adb shell am start -n com.smile.gifmaker/com.yxcorp.gifshow.activity.ProfileActivity -d kwai://profile/18070291
最後由輔助功能完成模擬點擊關注
補刀APP對快手APP的Activity和VIEW相關信息提取
0x0010即是輔助功能的點擊事件AccessibilityAction#ACTION_CLICK
快手個人信息展示頁ProfileActivity中的View.
APP遇到這種劫持通常想到的解決方法有兩種選擇:
- 不導出被劫持啟動的Activity,但是快手這裡確實需要導出給正常APP如微信打開以提升用戶體驗.
- 通過申明permission保護Activity,但是如果級別為dangerous劫持者同樣可以申明此permission,級別為signature又與微信簽名不同不能實現.
下圖為微信分享的快手個人主頁
所以現在有兩個防禦思路三個方案來解決此問題.
- 阻止輔助功能模擬點擊
- 方案零:重寫View類的performAccessibilityAction方法或者設置AccessibilityDelegate,過濾掉AccessibilityNodeInfo.ACTION_CLICK 和 AccessibilityNodeInfo.ACTION_LONG_CLICK等事件.如果不考慮視覺障礙用戶可以過濾掉全部AccessibilityNodeInfo事件來完全禁止AccessibilityService對app內view的管控.
- 阻止補刀小視頻啟動快手導出的ProfileActivity.也就是進行Activity發起方的身份認證.
- 方案一:Referrer檢測,通過反射拿到mReferrer即調用方包名再驗證簽名.
- 方案二:Service中轉,通過bindService的導出方法拿到調用方uid,再通過uid獲取待驗證的包名和簽名.
從安全性來看方案二較好,就快手此例的業務切合度來看結合方案零和方案一比較合理.
0x01 方案零:重寫performAccessibilityAction
方案利弊:
- 兼容全版本android手機(泛指API14+)
- 不需要正常Activity調用方(比如微信微博瀏覽器)做改動
- 有被繞過可能,劫持者只需要單獨將點擊事件剔除整個自動互粉流程,讓點擊關注由用戶完成即可.補刀APP主要負責啟動快手個人用戶界面ProfileActivity以及監控關注動作是否完成.
重寫performAccessibilityAction方法,忽略AccessibilityService傳來的事件.讓模擬點擊失效.
- 重寫View類代碼
2.為View設置AccessibilityDelegate
example
0x02 方案一:Referrer檢測
方案利弊:
- 僅支持android5.1以及更高版本android手機.
- 不需要Activity正常調用方做改動.
- 可以繞過,Referrer本質不可信.
- 通過反射或者 hook 操作自身進程內的 ContextWrapper / ContextImpl關於packageName的Method和Field.
- 劫持者可以結合Accessbility 或者 URL scheme通過瀏覽器中轉一次,從而以瀏覽器的Referrer啟動ProfileActivity.
送分姿勢1:getCallingPackage()
Activity自帶的getCallingPackage()是可以獲取調用方包名的,但是此法只限調用方執行的是startActivityForResult(),如果執行的是startActivity()得到的結果將是null.
這裡無法限制調用方執行何種方法,所以行不通.
送分姿勢2:getReferrer()
上圖getReferrer()有三個return Referrer的調用,谷歌確把相對可靠一點的放在最後,應該是為了更高的可用性..
API 22也就是Android 5.1開始支持getReferrer()方法,通過getReferrer()得到的uri即是調用者的身份.但是前提是調用方沒有使用
intent.putExtra(Intent.EXTRA_REFERRER,Uri.parse("android-app://mi.bbbbbbbb"));nnintent.putExtra(Intent.EXTRA_REFERRER_NAME, "android-app://mi.ccccccc");nn@Overridennpublic Uri onProvideReferrer() {nn super.onProvideReferrer();nn Uri uri = Uri.parse("android-app://mi.aaaaaaaaa");nn return uri;nn}n
也就是說getReferrer()得到的值是可以被偽造的不是安全可靠的功能不可信,谷歌API里也提示了這點.
從代碼中看來getReferrer()本質也是intent操作,只不過由系統隱藏完成.所以調用再次執行putExtra操作即可覆蓋之前EXTRA_REFERRER_NAME.
送分姿勢3:通過反射拿到Field mReferrer
此法解決了前面提到的Referrer被偽造的問題,但是並不能解決Referrer不可信的本質.
關鍵代碼如下
demo app 效果如下
mReferrer賦值依賴調起方傳入的參數,所以也是能偽造的,只是偽造相對前兩種姿勢要麻煩一點.通過反射或者 hook 操作自身進程內的 ContextWrapper / ContextImpl 關於packageName的Method和Field.
0x03 方案二:Service中轉
方案利弊:
- 支持全版本android手機
- 安全性較好,難被繞過
- 需要Activity正常調用方做改動,由startActivity改為bindService
因為Intent並不直接攜帶身份信息,所以無法通過startActivity所傳的Intent直接驗證調用方身份.而Bound service可以通過Binder的getCallingUid得知調用方uid,再通過PMS拿到uid對應的包名和應用簽名.所以可以通過service中轉一下完成身份認證這個需求,將Activity不導出轉而導出Sevice,再service中完成包名和簽名的黑白名單驗證後再決定是否啟動相關Activity.即完成了身份驗證.
關鍵代碼如下
0x04 demo代碼
https://github.com/WooyunDota/StartActivityCheck
0x05 延展攻擊Android手機
Accessibility既然可以用來攻擊競品APP,那麼攻擊Android手機也可以的,這裡以華為手機本地備份舉例.
華為手機可以本地備份的數據有:
- 通訊錄
- 多媒體數據
- 相機照片
- 相機視頻
- 錄音
- 應用及數據
- 微信
- 微博
- …..
- 系統數據
- 簡訊記錄
- 通話記錄
- 日曆日程
- 備忘錄
- 鬧鐘
- WIFI密碼
- 瀏覽器數錢
- ….
這就意味著我們通過Accessbility模擬點擊竊取備份文件的話就可以得到以上數據.
如果不慎中招意味著幾乎將手機上所有數據拱手送人.
攻擊流程:
- 檢測/sdcard/HuaweiBackup/backupFiles是否有用戶自己完成的歷史無加密備份可用(另外一個目錄backupFiles1為加密備份).
- 誘導用戶獲取Accessbility許可權,從快手互粉/自動搶紅包/免root安裝這些需求來看這個攻擊條件達成的難度並不高.
- 可以利用overlay攻擊(CVE-2017-0752)來獲取Accessbility許可權.
- 也可以利用比如」華為wifi密碼查看器」這類功能引誘用戶開啟許可權.
- 檢測空閑,檢測屏幕狀態,採集陀螺儀/加速度感測器.減少用戶對模擬點擊的感知.
- 利用輔助功能模擬點擊完成無加密備份.開始備份後切換到後台減少感知.
- 從sdcard中竊取無加密的備份數據.
攻擊場景分兩種:
1.惡意應用,獲取機主隱私數據,比如wifi密碼,通信錄,簡訊等數據.對應無設備鎖檢測的app甚至可以直接利用其備份數據登錄app.對於有設備檢測的app則需要進一步繞過利用,比如微信的聊天記錄需要單獨再次解密.
2.接觸手機,繞過如沙箱保護/應用鎖等限制獲取數據.比如拿到其公司wifi密碼登錄內網.
做幾個demo
- 查看wifi密碼
2.以微信數據為例,惡意APP可以通過此方式突破沙箱限制獲取微信內的數據.接觸手機的人也可以繞過應用鎖查看微信聊天記錄
既然華為拿了微信的數據,那麼解密肯定不是問題.
微信的聊天記錄存儲在EnMicroMsg.db中
加密的秘鑰為(手機IMEI + 微信uin)取MD5的前7位小寫.
華為存儲備份文件的方式,記錄文件路徑和File_index索引
再將索引對應的大文件進行拆分存儲.
根據file_index拼接處完整EnMicroMsg.db. 在從shared_prefs中檢索出uin得到db的解密秘鑰.即可查看聊天記錄.
獲取IMEI的文件,從shared_prefs中拿IMEI有兩個好處1.不用考慮雙卡問題,2.不用申請READ_PHONE_STATE許可權.
demoGIF
以上問題均已提前告知相關廠商,廠商回復以及相關進度如下:
2017-09-11 通知華為PSIRT
2017-12-13 華為致謝修復漏洞
0x06 參考
https://stackoverflow.com/questions/15723797/android-prevent-talkback-from-announcing-textview-title-aloud
https://stackoverflow.com/questions/5383401/android-inject-events-permission
http://blog.csdn.net/alan480/article/details/52223920
http://blog.csdn.net/u013553529/article/details/53856800
http://www.jianshu.com/p/ea38d4370703
登錄 安全客 - 有思想的安全新媒體 來獲取更多資訊~
推薦閱讀: