Changes

Tutorial A7 Glitch Buffer Attacks

5,241 bytes added, 21:00, 4 October 2018
no edit summary
|capture hardware = CW-Lite
|Target Device =
|Target Architecture = XMEGA/Arm
|Hardware Crypto = No
|Purchase Hardware =
=== Disassembly ===
As a final step, let's check the assembly code to see exactly what we're trying to glitch through. Run In the same folder as the hex file you built, open the command<precode>avr-objdump -m avr -D bootloader*.hex > disassembly.txtlss</precode>and open file that corresponds to your target (for example, <code>disassemblybootloader-CWLITEARM.txtlss</code>). If you know what This is called a listing file, and it contains a bunch of debug and assembly information. Most importantly, it will allow us to look easily match the source code to what's in our hex file. Search the file forsomething close to the vulnerable loop, such as <code>state == RESPOND</code>.  ==== XMEGA Disassembly ====After searching the file, you should find a snippet that looks see something likethis:<pre>
376: 89 91 ld r24, Y+
378: 0e 94 06 02 call 0x40c ; 0x40c
* Call the function in location <code>0x40c</code>. Presumably, this is the location of the <code>putch()</code> function.
* Compare <code>r28</code> and <code>r29</code> to <code>0x7F</code> and <code>0x20</code>. Unless they're equal, go back to the top of the loop.
There's one quirk to notice in this code. In the C source, the for loop checks whether <code>i < ascii_idx</code>. However, in the assembly code, the check is effectively whether <code>i == ascii_idx</code>! This is even easier to glitch - as long as we can break past the <code>brne</code> instruction ''once'', we'll get to the data buffer.
 
==== Arm Disassembly ====
After searching the file, you should find:<syntaxhighlight lang="asm">
if(state == RESPOND)
{
// Send the ascii buffer back
trigger_high();
8000258: f000 f8da bl 8000410 <trigger_high>
 
int i;
for(i = 0; i < ascii_idx; i++)
{
putch(ascii_buffer[i]);
800025c: 7828 ldrb r0, [r5, #0]
800025e: f000 f8f7 bl 8000450 <putch>
8000262: 7868 ldrb r0, [r5, #1]
8000264: f000 f8f4 bl 8000450 <putch>
8000268: 78a8 ldrb r0, [r5, #2]
800026a: f000 f8f1 bl 8000450 <putch>
}
</syntaxhighlight>This is our printing "loop", but as we can see, it's not really a loop at all! As it turns out, the compiler has unrolled our loop, which typically speeds up execution at the expense of code size. It will be very difficult to prevent this with such a small loop, so instead we'll increase the loop size by adding some extra newlines at the end of the message:<syntaxhighlight lang="c">
ascii_buffer[ascii_idx++] = '\n';
ascii_buffer[ascii_idx++] = '\n';
ascii_buffer[ascii_idx++] = '\n';
ascii_buffer[ascii_idx++] = '\n';
ascii_buffer[ascii_idx++] = '\n';
</syntaxhighlight>If you want to use the script at the bottom, you should add the 5 newlines above, but other values will work fine with different glitch parameters. Recompile and take another look at the listing file. You should see that our printing loop is actually a loop now:<syntaxhighlight lang="c">
if(state == RESPOND)
{
// Send the ascii buffer back
trigger_high();
8000262: f000 f8d7 bl 8000414 <trigger_high>
 
int i;
for(i = 0; i < ascii_idx; i++)
8000266: 2400 movs r4, #0
{
putch(ascii_buffer[i]);
8000268: 5d28 ldrb r0, [r5, r4]
for(i = 0; i < ascii_idx; i++)
800026a: 3401 adds r4, #1
putch(ascii_buffer[i]);
800026c: f000 f8f2 bl 8000454 <putch>
for(i = 0; i < ascii_idx; i++)
8000270: 2c08 cmp r4, #8
8000272: d1f9 bne.n 8000268 <main+0x64>
</syntaxhighlight>We can break this assembly down into the following steps (starting with address 0x8000268):
* Load the character we want to print into <code>r0</code> (<code>r5</code> contains the address of <code>ascii_buffer</code>, while <code>r4</code> is our loop index <code>i</code>)
* Add 1 to <code>r4</code> (<code>i</code>)
* Call <code>putch</code>
* Compare <code>r4</code> to 8 (8 is always the value of <code>ascii_idx</code>)
* Branch back to the beginning of the loop if <code>r4</code> and 8 aren't equal
There's one quirk to notice in this code. In the C source, the for loop checks whether <code>i < ascii_idx</code>. However, in the assembly code, the check is effectively whether <code>i == ascii_idx</code>! This is even easier to glitch - as long as we can break past the <code>brne</code> instruction ''once'', we'll get to the data buffer.
== Attack Script & Results ==
 
=== XMEGA Results ===
To speed up the tutorial, the script in [[#Appendix: Setup Script]] will open the ChipWhisperer Capture software and fill in all of the appropriate settings. Copy this code into a Python script and run it. Then, open the serial terminal and connect to the target, using the ASCII with Hex display mode. If everything is set up correctly, the Capture 1 button should cause the text <code>r0</code> to appear in the terminal. This is the bootloader's response to a block of ciphertext.
If you can't get this to work, remember that glitching is a very sensitive operation - one glitch timing will probably not work for every board on every day. Try using the glitch explorer to attack different ''Glitch Width''s, ''Glitch Offset''s, and ''Ext Trigger Offset''s. The built-in Glitch Explorer will be very useful here - take a read through [[Tutorial A2 Introduction to Glitch Attacks (including Glitch Explorer)]] if you need a refresher.
 
=== Arm Results ===
If you added the same number of newlines as shown (5), you should be able to run the script at the end of this page from inside ChipWhisperer. Then, open the serial terminal and connect to the target, using the ASCII with Hex display mode. If everything is set up correctly, the Capture 1 button should cause the text <code>r0</code> to appear in the terminal. This is the bootloader's response to a block of ciphertext.
 
Once this is set up, connect the glitch module's output to the target's clock. Do this by changing the <code>Target HS IO-Out</code> to <code>Glitch Module</code>. Try to Capture 1 again and watch the serial terminal. If you're lucky, a very large amount of text will appear in this window:
 
[[File:MILK.PNG|frameless|994x994px]]
 
Near the beginning of this output, the plaintext is clearly visible! The data buffer has successfully been printed to the serial port, allowing us to see the decrypted text with no knowledge of the algorithm.
 
This might take a few attempts. If the target stops responding, you'll need to reset it manually. You may also want to setup a module to iterate over some glitch parameters (ext_offset should be the most important one) like in [[Tutorial A2 Introduction to Glitch Attacks (including Glitch Explorer)]].
== Ideas ==
The following script is used to set up the ChipWhisperer-Lite with all of the necessary settings:
<pre>
 
# GUI compatibility
try:
target.key_cmd = ""
target.output_cmd = ""
</pre>
== Appendix: Arm Setup Script ==
<pre>
# GUI compatibility
try:
scope = self.scope
target = self.target
except NameError:
pass
 
scope.glitch.clk_src = 'clkgen'
scope.glitch.ext_offset = 16993
scope.glitch.width = -10
scope.glitch.offset = -40
scope.glitch.repeat = 2
scope.glitch.trigger_src = "ext_single"
 
scope.gain.gain = 45
scope.adc.samples = 500
scope.adc.offset = 0
scope.adc.basic_mode = "rising_edge"
scope.clock.clkgen_freq = 7370000
scope.clock.adc_src = "clkgen_x4"
scope.trigger.triggers = "tio4"
scope.io.tio1 = "serial_rx"
scope.io.tio2 = "serial_tx"
scope.io.hs2 = "glitch"
 
target.go_cmd = "p516261276720736265747267206762206f686c207a76797821\\n"
target.key_cmd = ""
target.output_cmd = ""
</pre>
Approved_users, administrator
366
edits