Updating docs
This commit is contained in:
parent
a75bf965cc
commit
ac755b81f1
@ -1,23 +1,30 @@
|
||||
==================
|
||||
Frederic's Exploit
|
||||
==================
|
||||
Frederic published a blogpost on reversing the `USB stack of the Exynos BootROM <https://fredericb.info/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html#reverse-engineer-usb-stack-of-exynos-bootrom>`_ and a blogpost on exploiting the `Exynos 8890 BootROM <https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html#exynos-usbdl-unsigned-code-loader-for-exynos-bootrom>`_. Here we will discuss the exploit in more detail.
|
||||
|
||||
USB Stack in BootROM
|
||||
--------------------
|
||||
The bootRom is the first code that runs on the Exynos SoC. It is responsible for initializing the hardware and loading the first stage bootloader from storage. The BootROM is stored in a read-only memory and cannot be modified (making vulnerabilities in this permanent/non-patcheable). The BootROM is responsible for initializing the USB controller and receiving the first stage bootloader from the USB host. It will be waiting for a package in the following format (dldata == download data):
|
||||
|
||||
dldata
|
||||
^^^^^^
|
||||
For uploading a stage to boot, a custom protocol is used. The dldata that has to be send is the following:
|
||||
A key For uploading a stage to boot, a custom protocol is used. The dldata that has to be send is 512 bytes long, and has the following format:
|
||||
|
||||
.. figure:: images/dl_packet.drawio.svg
|
||||
:align: center
|
||||
|
||||
The dldata packet is used to send data to the BootROM.
|
||||
The dldata packet is used to send data to the BootROM.
|
||||
|
||||
The size in the packet is the total size of the packet, including header and footer.
|
||||
The size in the packet is the total size of the packet, including header and footer. If we modify this, we will have a payload size of around 502 bytes.
|
||||
|
||||
.. info::
|
||||
.. caution::
|
||||
|
||||
This protocol remains *mostly* the same for newer Exynos SoCs.
|
||||
|
||||
USB Stack
|
||||
---------
|
||||
This information is largely based on the blogpost of Frederic on reversing the `USB stack of the Exynos BootROM <https://fredericb.info/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html#reverse-engineer-usb-stack-of-exynos-bootrom>`_. We're looking at the proprietary USB protocol used by the Exynos BootROM.
|
||||
USB Controller / DWC3
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
The Exynos 8890 uses the Synopsys DesignWare USB 3.0 controller. Much of the code is shared with the DWC3 driver in the Linux kernel, except that the ROM does not do any scheduling and a lot of features have been removed(OTG handling, etc).
|
||||
|
||||
The base address of the usb controller (dwusb3) is mapped at 0x1540000, with a size of 0x10000: (can be found at: `Exynos8890 dtsi <https://github.com/LineageOS/android_kernel_samsung_universal8890/tree/lineage-18.1/arch/arm64/boot/dts>`_).
|
||||
|
||||
@ -66,6 +73,7 @@ The USB host sends a USB_REQ_SET_ADDRESS, `'0x05' <https://asf.microchip.com/doc
|
||||
Ghidra shows `DWC3_DCFG & 0xfffffc00 | DWC3_DCFG & 7 | (param_1 & 0x7f) << 3;`, essentially preserves bits 0-2 and 10-31, and sets bits 3-9 to the value of param_1, which is then likely the address of the device.
|
||||
|
||||
.. figure:: images/ghidra_dwc3_dcfg_devaddr.png
|
||||
:align: center
|
||||
|
||||
bootrom exynos 8890 dwc3_dcfg_devaddr
|
||||
|
||||
@ -74,20 +82,23 @@ Other general device descriptors are also sent from the device to the host (to d
|
||||
Data is transferred via Transfer Request Blocks (TRB), dwc3_depcmd_starttransfer is used. The TRB then contains a buffer address, where transferred data from the host is written to. The buffer allocation is done by 'usb_setup_event_buffer', which sets bufferHigh (DWC3_GEVNTADRLO), bufferLow (DWC3_GEVNTADRHI) and bufferSize (DWC3_GEVNTSIZ).
|
||||
|
||||
Bug 1 (Integer overflow)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Originally described in this `blogpost <https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html#exynos-usbdl-unsigned-code-loader-for-exynos-bootrom>`_.
|
||||
========================
|
||||
Originally described in this `blogpost <https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html#exynos-usbdl-unsigned-code-loader-for-exynos-bootrom>`_. Our if-statement is written a bit different, but boils down to the same thing.
|
||||
|
||||
The exynos bootrom uses a simple USB protocol to receive a bootloader binary from a USB host. The binary sent is called 'dldata'. In Ghidra, at 21518, we can see that it consists of unit32_t: ready?, uint32: size, ? : data, uint16: footer. The contents of this data are checked before being being written.
|
||||
|
||||
.. figure:: images/usb_setup_ready_to_0.png
|
||||
:align: center
|
||||
|
||||
The ready flag is set to 0 in the Exynos 8890 BootROM in an earlier function on pdVar1->size (pdVar1.size)
|
||||
|
||||
.. figure:: images/dl_data_struct.png
|
||||
:align: center
|
||||
|
||||
The dldata struct in the Exynos 8890 BootROM
|
||||
|
||||
.. code:: c
|
||||
|
||||
if ((pdVar1->size < 0x206ffff) && (0x206ffff < pdVar1->size + remaining)) {
|
||||
*(undefined *)&pdVar1->ready = 2;
|
||||
}
|
||||
@ -101,12 +112,14 @@ If both conditions are met, the payload will NOT be loaded. But this makes sense
|
||||
The bug is however, that the check that the check is done on a uint32_t (2^32 = 4294967296), but the max value that can be checked by a uint32 is 0xFDFDE7FF = 4294967295. So a value of 0xFDFDE7FF + 1 = 0xFDFDE800 = 4294967296, which is larger than the max value of a uint32. So if a payload of this size or more is used, which is much larger than the max requested value 0x206ffff, the check will pass and the payload will still be loaded.
|
||||
|
||||
.. figure:: images/usb_payload_size_check.jpeg
|
||||
:align: center
|
||||
|
||||
The size check in the Exynos 8890 BootROM
|
||||
|
||||
Sending such large amounts of data can cause a memory overflow, and will cause the target to crash. Not interesting for exploitation in this context. However, the USB packages that are sent, are split into smaller packages with a size of 0xFFFE00.
|
||||
|
||||
.. figure:: images/max_allowed_chunck_size.jpeg
|
||||
:align: center
|
||||
|
||||
The max allowed chunk size, after which the payload is split.
|
||||
|
||||
@ -124,25 +137,15 @@ The trick then becomes, to get the pointer to an address we would like to exploi
|
||||
} dldata;
|
||||
|
||||
Bug 2
|
||||
^^^^^
|
||||
=====
|
||||
|
||||
.. caution::
|
||||
|
||||
Might be a 0/N-day if exploitable
|
||||
|
||||
@ELHER
|
||||
|
||||
There is a bug(unpatched?) in receiving the last packet of the usb image:
|
||||
|
||||
.. figure:: images/underflow_bug.png
|
||||
|
||||
The bug is an integer underflow in the calculation of the remaining size of the image.
|
||||
|
||||
DWC3
|
||||
^^^^
|
||||
The Exynos 8890 uses the Synopsys DesignWare USB 3.0 controller.
|
||||
Much of the code is shared with the DWC3 driver in the Linux kernel, except that the ROM does not do any scheduling and a lot of features have been removed(OTG handling, etc).
|
||||
|
||||
Gupje
|
||||
^^^^^
|
||||
In order to run the debugger, a small amount of the bootROM was reversed in order to implement send/recv functionality.
|
||||
|
@ -1,64 +1,37 @@
|
||||
.. _boot-chain-label:
|
||||
==================
|
||||
Exploit boot chain
|
||||
==================
|
||||
This part describes the boot chain of the ``Exynos 8890`` SoC.
|
||||
|
||||
.. important::
|
||||
|
||||
This is all still under development and will change.
|
||||
|
||||
Memory overview
|
||||
===============
|
||||
---------------
|
||||
Keep this overview in mind when reading through the boot chain exploit.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<iframe src="../_static/stack_and_functions.html" width="100%" height="1000px" frameborder="0" float='center'></iframe>
|
||||
|
||||
|
||||
Exploitation
|
||||
============
|
||||
------------
|
||||
After exploitation the goal is to fully boot the device. We're trying to use Frederic's exploit to load a debugger and then boot the device. The debugger needs to be kept 'alive' through the boot chain, so we can patch the boot chain at the right time. In order to run the debugger, a small amount of the bootROM was reversed in order to implement send/recv functionality.
|
||||
|
||||
After exploitation the goal is to fully boot the device. The following part describes the current boot chain
|
||||
.. topic:: gupje
|
||||
|
||||
.. important::
|
||||
Gupje is the debugger we'll be loading onto the device and will be replacing throughout the bootchain.
|
||||
|
||||
This is under development and will still change.
|
||||
Loading the debugger
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
The debugger is based on `Gupje <https://git.eminjenv.nl/nfi-exploitdev/gupje>`_.
|
||||
|
||||
The first stage is downloading BL1, authenticating it and patching it after authentication.
|
||||
This is done by overwriting the USB return address pointer and jumping back to the debugger.
|
||||
In the debugger we can authenticate BL1, patch it and boot it. An overview of this process is shown below:
|
||||
TODO
|
||||
|
||||
Booting an authenticated and patched BL1:
|
||||
|
||||
.. figure:: images/boot_chain_bl1.drawio.svg
|
||||
:align: center
|
||||
|
||||
Boot chain
|
||||
|
||||
.. note::
|
||||
|
||||
git commit 8cb5f2e1 fully boots, you can use this commit to patch bl1 only.
|
||||
|
||||
Next up is BL31, which is loaded by BL1.
|
||||
|
||||
``BL31`` is the secure monitor. The monitor uses memory that is also being used by the debugger, so we will have to relocate it to keep code exeuction.
|
||||
|
||||
.. figure:: images/bl31_debugger_memory_example.png
|
||||
:align: center
|
||||
|
||||
Example of BL31 using debugger memory.
|
||||
|
||||
BL31 also configures the VBAR_EL3 and MMU so the memory mapping will probably change after this stage (preparation for trustzone?).
|
||||
|
||||
It would be nice to patch BL31 before it is being executed. However the current exploit boot flow does not allow this because the ROM function downloads the next stage.
|
||||
|
||||
Notes
|
||||
-----
|
||||
As done by Frederic, the bootrom can be dumped using his provided scripts, and can the be split into different boots:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./exynos-usbdl e payloads/Exynos8890_dump_bootrom.bin dumped_sboot.bin
|
||||
scripts/split-sboot-8890.sh dumped_sboot.bin
|
||||
|
||||
This results in the following files:
|
||||
Boot stages/payloads
|
||||
--------------------
|
||||
Get the correct payloads for the bootROM stages from samsung firmware files, or from `Exynos8890 usbdl-recovery images/firmwares <https://github.com/ananjaser1211/exynos8890-exynos-usbdl-recovery>`_.
|
||||
|
||||
.. list-table:: bootrom stages
|
||||
:header-rows: 1
|
||||
@ -79,8 +52,14 @@ This results in the following files:
|
||||
- Contains more textual information, and references to post BL2 boot, and android information
|
||||
- Kernel boot/BL33?
|
||||
|
||||
After loading the stage1 (entry.S - Frederic's exploit), we're allowed to send custom payloads to the device. The first payload that is then sent, is the debugger.
|
||||
Stage0/entry
|
||||
============
|
||||
After loading the stage0 (entry.S - Frederic's exploit), we're allowed to send custom payloads to the device. The first payload that is then sent, is the debugger.
|
||||
|
||||
TODO
|
||||
|
||||
Initial debugger
|
||||
================
|
||||
Debugger
|
||||
--------
|
||||
The initial debugger is written to ``0x2069000``, with debugger_stack and _storage at ``0x0206b000`` and ``0x0206d000`` respectively.
|
||||
@ -127,15 +106,47 @@ The processor state reported then is:
|
||||
X21 : 0x0 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 |
|
||||
X28 : 0x0 | X29 : 0x2020f00 | LR/X30 : 0x20c0000 | SP/X31 : 0x2020ef0
|
||||
|
||||
Stage1/BL1
|
||||
==========
|
||||
The first stage is downloading BL1, authenticating it and patching it after authentication. This is done by overwriting the USB return address pointer and jumping back to the debugger. In the debugger we can authenticate BL1, patch it and boot it. An overview of this process is shown below:
|
||||
|
||||
Booting an authenticated and patched BL1:
|
||||
|
||||
.. figure:: images/boot_chain_bl1.drawio.svg
|
||||
:align: center
|
||||
|
||||
Boot chain
|
||||
|
||||
.. note::
|
||||
|
||||
git commit 8cb5f2e1 fully boots, you can use this commit to patch bl1 only.
|
||||
|
||||
Stage2/BL31
|
||||
===========
|
||||
Next up is BL31, which is loaded by BL1. BL31 is written at ``0x02024000`` with the entry point at ``0x02024010``, it ends at ``0x02048000``. ``BL31`` is the secure monitor. The monitor uses memory that is also being used by the debugger, so we will have to relocate it to keep code exeuction.
|
||||
|
||||
.. figure:: images/bl31_debugger_memory_example.png
|
||||
:align: center
|
||||
|
||||
Example of BL31 using debugger memory.
|
||||
|
||||
BL31 also configures the VBAR_EL3 and MMU so the memory mapping will probably change after this stage (preparation for trustzone?).
|
||||
|
||||
It would be nice to patch BL31 before it is being executed. However the current exploit boot flow does not allow this because the ROM function downloads the next stage.
|
||||
|
||||
Initial boot function (BL1)
|
||||
---------------------
|
||||
---------------------------
|
||||
|
||||
.. figure:: images/initial_boot_function.png
|
||||
:align: center
|
||||
|
||||
Overview of the initial boot function in the exynos 8890
|
||||
|
||||
BL1 needs to be authenticated. BL1 loads at address ``0x02024000`` and contains some form of header (ramdump). There seems to be a samsung header format, where the first 4 bytes define the entry point of the binary. In this case this entry is ``+0x10`` so we jump to ``0x02024010``. Authentication seems to be done at ``0x00012848``. Initially we thought that 0x0 indicated a verified boot state (as is plausible when reading the decompiled code in Ghidra). But after modifying BL1 in the header and contents, this value did not change.
|
||||
.. caution::
|
||||
|
||||
This part needs to be rewritten (contains lies)
|
||||
|
||||
BL1 needs to be authenticated. BL31 loads at address ``0x02022000`` and contains some form of header (ramdump). There seems to be a samsung header format, where the first 4 bytes define the entry point of the binary. In this case this entry is ``+0x10`` so we jump to ``0x02024010``. Authentication seems to be done at ``0x00012848``. Initially we thought that 0x0 indicated a verified boot state (as is plausible when reading the decompiled code in Ghidra). But after modifying BL1 in the header and contents, this value did not change.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -209,7 +220,7 @@ purpose
|
||||
^^^^^^^
|
||||
bl1 interacts with several pheriperals, from the DTB these are:
|
||||
|
||||
.. code-block:: dtsi
|
||||
.. code-block:: bash
|
||||
|
||||
/* FSYS0 */
|
||||
pinctrl_5: pinctrl@10E60000 {
|
||||
@ -247,14 +258,6 @@ bl1 interacts with several pheriperals, from the DTB these are:
|
||||
|
||||
Probably the only thing it does is set some clocks and prepare for BL31.
|
||||
|
||||
BL31
|
||||
----
|
||||
BL31 is written at ``0x02024000`` with the entry point at ``0x02024010``.
|
||||
|
||||
BL2
|
||||
---
|
||||
0x02e8dc mentions 'Onyx-OPR6-8511R1', which is likely sboot.
|
||||
|
||||
OLD
|
||||
---
|
||||
|
||||
@ -306,4 +309,3 @@ Replacing this function with our debugger makes us jump back:
|
||||
X28 : 0x0 | X29 : 0x2020ed8 | LR/X30 : 0x202419c | SP/X31 : 0x2020ec0
|
||||
|
||||
However this does not fully run bl1, so we will have to dig a bit deeper to see the puropose and when to jump back to the debugger.
|
||||
|
||||
|
@ -4,10 +4,10 @@ Notes
|
||||
General notes on interesting/peculiar things found on the S7 USB recovery boot process
|
||||
|
||||
Emulator
|
||||
========
|
||||
--------
|
||||
What is interesting about the ROM is that it starts by checking MPIDR_EL1 register and doing a conditional branch to 0x20e0000.
|
||||
|
||||
.. code-block:: ghidra
|
||||
.. code-block:: bash
|
||||
|
||||
undefined w0:1 <RETURN>
|
||||
Reset XREF[1]: Entry Point(*)
|
||||
@ -18,7 +18,7 @@ What is interesting about the ROM is that it starts by checking MPIDR_EL1 regist
|
||||
00000010 fc 7f 83 14 b LAB_020e0000
|
||||
|
||||
Week 35 - 2024
|
||||
===============
|
||||
--------------
|
||||
After booting BL31, the MMU seems to be set up, and we're unable to do get any data off of spaces we're not 'allowed' to access. Patching the if-statement at 0x020244e8, disables the bit that says that the MMU is setup, but booting into recovery is possible (meaning the MMU is setup). Additionally, the memory at 0x02035600 is still not dumpable. At 0x02048000 is still accessible.
|
||||
|
||||
Weird space found at 0x105c2400. Seems to contain references to usb buffer (about 48-64 bytes).
|
||||
@ -26,11 +26,12 @@ Weird space found at 0x105c2400. Seems to contain references to usb buffer (abou
|
||||
Also space at 0x020307f0
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
self.cd.memdump_region(0x105c2400, 0x40).hex()
|
||||
'0f0f00000f0008002100000000000000ffffffffffffffffffffffffffffffff0f0f00000f0008002101000000000000ffffffffffffffffffffffffffffffff'
|
||||
|
||||
Week 36 - 2024
|
||||
===============
|
||||
--------------
|
||||
Interesting links:
|
||||
- `Heap overflow <https://highaltitudehacks.com/2020/09/05/arm64-reversing-and-exploitation-part-1-arm-instruction-set-heap-overflow.html>`_
|
||||
- `UART on S8 <https://grimler.se/posts/exynos-uart/>`_
|
||||
|
Loading…
Reference in New Issue
Block a user