Hello and happy Halloween fellow Exploitee.rs. Today we’re excited to be bringing you something we’ve been working on for the last few months. Today, we’re introducing you to FireFU. FireFU is an exploit chain we’ve created to allow users to unlock (and root) their FireTV Cube and FireTV Pendant.

WARNING

Exploitee.rs would like to remind users that any flashing of unofficial firmware or usage of provided tools is done at your own risk and will likely void your device’s warranty.

DFU Mode

This exploit chain relies on two primitives, the first being a read/write primitive leveraged from the DFU mode accessible from the Amlogic S905Z SoC. DFU mode can be accessed on these devices by utilizing HDMI’s I2C bus and sending a specific string (“[email protected]”) to the device during boot. An adapter can be made which enters DFU by cutting open a HDMI cable or simply purchasing an HDMI header, then connecting to the appropriate I2C pins from the HDMI to the I2C pins on an Arduino or compatible board. We have provided an arduino “sketch” that can be compiled and loaded onto an arduino then used to perform the software side of entering DFU.

Upon accessing DFU mode, we are given access to read and write portions of the FireTV’s memory. Through this we target the hardware registers for the eMMC controller giving us the new primitive of being able to read and write to the device’s eMMC flash. However, due to both devices having secure boot enabled, we are unable to directly leverage the primitives we currently have to run unsigned code. We however did discover another vulnerability that we can use.

U-Boot Heap Overflow

In a secure boot environment, each portion of the boot process checks and sets up the following. From the SoC ROM all the way to the kernel and some cases even the kernel modules. In order to run unsigned code, a weakness needs to be found in some portion of the secure boots “chain of trust”. After a bit of research, we stumbled onto the perfect vulnerability we could leverage to break the chain. This vulnerability consisted of a heap overflow within U-Boot triggered when reading the RSV info within the devices partition table. This overflow can be seen in the code below.

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 436 /* get ptbl from rsv area from emmc */ 437 static int get_ptbl_rsv ( struct mmc * mmc, struct _iptbl * rsv ) 438 { 439 struct ptbl_rsv * ptbl_rsv = NULL ; 440 uchar * buffer = NULL ; 441 ulong size, offset ; 442 int checksum, version, ret = 0 ; 443 struct virtual_partition * vpart = aml_get_virtual_partition_by_name ( MMC_TABLE_NAME ) ; 444 445 size = ( sizeof ( struct ptbl_rsv ) + 511 ) / 512 * 512 ; 446 if ( vpart - > size < size ) { 447 apt_err ( "too much partitons

" ) ; 448 ret = - 1 ; 449 goto _out ; 450 } 451 buffer = malloc ( size ) ; 452 if ( NULL == buffer ) { 453 apt_err ( "no enough memory for ptbl rsv

" ) ; 454 ret = - 2 ; 455 goto _out ; 456 } 457 /* read it from emmc. */ 458 offset = _get_inherent_offset ( MMC_RESERVED_NAME ) + vpart - > offset ; 459 if ( size ! = _mmc_rsv_read ( mmc, offset, size, buffer ) ) { 460 apt_err ( "read ptbl from rsv failed

" ) ; 461 ret = - 3 ; 462 goto _out ; 463 } 464 465 ptbl_rsv = ( struct ptbl_rsv * ) buffer ; 466 apt_info ( "magic %s, version %s, checksum %x

" , ptbl_rsv - > magic, 467 ptbl_rsv - > version, ptbl_rsv - > checksum ) ; 468 /* fixme, check magic ?*/ 469 if ( strcmp ( ptbl_rsv - > magic, MMC_PARTITIONS_MAGIC ) ) { 470 apt_err ( "magic faild %s, %s

" , MMC_PARTITIONS_MAGIC, ptbl_rsv - > magic ) ; 471 ret = - 4 ; 472 goto _out ; 473 } 474 /* check version*/ 475 version = _get_version ( ptbl_rsv - > version ) ; 476 if ( version < 0 ) { 477 apt_err ( "version faild %s, %s

" , MMC_PARTITIONS_MAGIC, ptbl_rsv - > magic ) ; 478 ret = - 5 ; 479 goto _out ; 480 } 481 /* check sum */ 482 checksum = _calc_iptbl_check ( ptbl_rsv - > partitions, ptbl_rsv - > count, version ) ; 483 if ( checksum ! = ptbl_rsv - > checksum ) { 484 apt_err ( "checksum faild 0x%x, 0x%x

" , ptbl_rsv - > checksum, checksum ) ; 485 ret = - 6 ; 486 goto _out ; 487 } 488 489 rsv - > count = ptbl_rsv - > count ; 490 memcpy ( rsv - > partitions, ptbl_rsv - > partitions, rsv - > count * sizeof ( struct partitions ) ) ; 491 492 _out : 493 if ( buffer ) 494 free ( buffer ) ; 495 return ret ; 496 } 436 /* get ptbl from rsv area from emmc */ 437 static int get_ptbl_rsv(struct mmc *mmc, struct _iptbl *rsv) 438 { 439 struct ptbl_rsv * ptbl_rsv = NULL; 440 uchar * buffer = NULL; 441 ulong size, offset; 442 int checksum, version, ret = 0; 443 struct virtual_partition *vpart = aml_get_virtual_partition_by_name(MMC_TABLE_NAME); 444 445 size = (sizeof(struct ptbl_rsv) + 511) / 512 * 512; 446 if (vpart->size < size) { 447 apt_err("too much partitons

"); 448 ret = -1; 449 goto _out; 450 } 451 buffer = malloc(size); 452 if (NULL == buffer) { 453 apt_err("no enough memory for ptbl rsv

"); 454 ret = -2; 455 goto _out; 456 } 457 /* read it from emmc. */ 458 offset = _get_inherent_offset(MMC_RESERVED_NAME) + vpart->offset; 459 if (size != _mmc_rsv_read(mmc, offset, size, buffer)) { 460 apt_err("read ptbl from rsv failed

"); 461 ret = -3; 462 goto _out; 463 } 464 465 ptbl_rsv = (struct ptbl_rsv *) buffer; 466 apt_info("magic %s, version %s, checksum %x

", ptbl_rsv->magic, 467 ptbl_rsv->version, ptbl_rsv->checksum); 468 /* fixme, check magic ?*/ 469 if (strcmp(ptbl_rsv->magic, MMC_PARTITIONS_MAGIC)) { 470 apt_err("magic faild %s, %s

", MMC_PARTITIONS_MAGIC, ptbl_rsv->magic); 471 ret = -4; 472 goto _out; 473 } 474 /* check version*/ 475 version = _get_version(ptbl_rsv->version); 476 if (version < 0) { 477 apt_err("version faild %s, %s

", MMC_PARTITIONS_MAGIC, ptbl_rsv->magic); 478 ret = -5; 479 goto _out; 480 } 481 /* check sum */ 482 checksum = _calc_iptbl_check(ptbl_rsv->partitions, ptbl_rsv->count, version); 483 if (checksum != ptbl_rsv->checksum) { 484 apt_err("checksum faild 0x%x, 0x%x

", ptbl_rsv->checksum, checksum); 485 ret = -6; 486 goto _out; 487 } 488 489 rsv->count = ptbl_rsv->count; 490 memcpy(rsv->partitions, ptbl_rsv->partitions, rsv->count * sizeof(struct partitions)); 491 492 _out: 493 if (buffer) 494 free (buffer); 495 return ret; 496 }

Specifically, by providing a high enough value to the number of entries in the RSV table (rsv->count on line 490), we are able to overflow the heap allocation and obtain a new write primitive. Through this primitive (and all within the exploit’s payload) we modify values in memory for U-Boot tricking the device into believing it is unlocked and disabling all signature verification. Due to the exploit being ran during each boot, the U-Boot code needs to only be patched in memory. However, because the exploit is now being stored in the original RSV location in flash, we must move the old RSV values to a new area and fixup any addresses pointing to the previous location. After a reboot and successful exploitation, the Fire TV device will be able to run unsigned code.

Fastboot

To simplify flashing of new images, we’ve chosen to use flashboot to flash a new recovery and boot.img to the device, this new recovery only provides the fixups needed to point to the new RSV location. The boot.img however includes “magisk”, a popular android root application, to facilitate providing the user root access to the device.

Technical Details & Instructions

Technical details for the exploit chain is available on our wiki as well as usage instructions, source code, and information on needed hardware/tools.

Video Demo







Exploitee.rs Wiki – FireFU Exploit

Hack The Planet

-Exploitee.rs