ПроХостер > блог > Администрација > Преокретање и хаковање Аиго екстерног ХДД диска који се самошифрује. Део 2: Узимање из Ципресс ПСоЦ-а
Преокретање и хаковање Аиго екстерног ХДД диска који се самошифрује. Део 2: Узимање из Ципресс ПСоЦ-а
Ово је други и последњи део чланка о хаковању екстерних самошифрујућих дискова. Да вас подсетим да ми је колега недавно донео Патриот (Аиго) СК8671 хард диск, а ја сам одлучио да га преокренем, а сада делим шта је из њега испало. Пре него што прочитате даље, обавезно прочитајте Први део чланци.
4. Почињемо да узимамо думп са унутрашњег ПСоЦ флеш диска
Дакле, све указује (као што смо установили у [првом делу]()) да је ПИН код ускладиштен у дубинама флеша ПСоЦ-а. Стога, морамо да прочитамо ове дубине блица. Предњи део неопходних радова:
преузети контролу над „комуникацијом“ са микроконтролером;
пронаћи начин да проверите да ли је ова „комуникација“ заштићена од читања споља;
пронаћи начин да заобиђете заштиту.
Постоје два места на којима има смисла тражити важећи ПИН код:
интерна флеш меморија;
СРАМ, где се пин код може сачувати да би се упоредио са пин кодом који је унео корисник.
Гледајући унапред, приметићу да сам ипак успео да направим думп интерног ПСоЦ флеш диска - заобилазећи његов безбедносни систем користећи хардверски напад који се зове „праћење хладног покретања“ – након што сам преокренуо недокументоване могућности ИССП протокола. Ово ми је омогућило да директно избацим стварни ПИН код.
$ ./psoc.py
syncing: KO OK
[...]
PIN: 1 2 3 4 5 6 7 8 9
„Комуникација“ са микроконтролером може значити различите ствари: од „продавца до добављача“ до интеракције помоћу серијског протокола (на пример, ИЦСП за Мицроцхип ПИЦ).
Ципресс има свој сопствени протокол за ово, назван ИССП (протокол серијског програмирања у систему), који је делимично описан у Техничка спецификација. Патент УС7185162 такође даје неке информације. Постоји и ОпенСоурце еквивалент који се зове ХССП (користићемо га мало касније). ИССП ради на следећи начин:
ребоот ПСоЦ;
избаци магични број на пин серијских података овог ПСоЦ-а; за улазак у режим екстерног програмирања;
шаљите команде, које су дуги низови битова који се називају "вектори".
ИССП документација дефинише ове векторе за само мали број команди:
Инитиализе-1
Инитиализе-2
Инитиализе-3 (3В и 5В опције)
ИД-СЕТУП
РЕАД-ИД-ВОРД
СЕТ-БЛОЦК-НУМ: 10011111010дддддддд111, где је дддддддд=блок #
БУЛК ЕРАСЕ
ПРОГРАМ-БЛОК
ВЕРИФИ-СЕТУП
РЕАД-БИТЕ: 10110ааааааЗДДДДДДДДЗ1, где је ДДДДДДДД = излаз података, аааааа = адреса (6 битова)
ВРИТЕ-БИТЕ: 10010ааааааддддддд111, где је дддддддд = подаци у, аааааа = адреса (6 битова)
СЕЦУРЕ
КОНТРОЛНА СУМА-СЕТУП
РЕАД-ЦХЕЦКСУМ: 10111111001ЗДДДДДДДДЗ110111111000ЗДДДДДДДДЗ1, где је ДДДДДДДДДДДДДДДД = излаз података: контролни збир уређаја
Сви вектори имају исту дужину: 22 бита. ХССП документација има неке додатне информације о ИССП-у: „ИССП вектор није ништа друго до низ битова који представља скуп инструкција.“
5.2. Демистифиинг Вецторс
Хајде да схватимо шта се овде дешава. У почетку сам претпоставио да су ти исти вектори сирове верзије М8Ц инструкција, али након провере ове хипотезе, открио сам да се кодови операција не поклапају.
Онда сам прогуглао вектор изнад и наишао ево га студија у којој аутор, иако не улази у детаље, даје неке корисне савете: „Свака инструкција почиње са три бита који одговарају једној од четири мнемонике (читање из РАМ-а, писање у РАМ, читање регистра, писање регистра). Затим постоји 8 битова адресе, затим 8 битова података (читање или писање) и на крају три стоп бита."
Тада сам успео да извучем неке веома корисне информације из одељка Супервисори РОМ (СРОМ). технички приручник. СРОМ је тврдо кодирани РОМ у ПСоЦ-у који пружа помоћне функције (на сличан начин као Сисцалл) за програмски код који се покреће у корисничком простору:
00х:СВБоотРесет
01х: РеадБлоцк
02х: ВритеБлоцк
03х: ЕрасеБлоцк
06х: Табле Реад
07х: ЦхецкСум
08х: Калибрација0
09х: Калибрација1
Упоређивањем имена вектора са СРОМ функцијама, можемо мапирати различите операције које подржава овај протокол у очекиване СРОМ параметре. Захваљујући томе, можемо декодирати прва три бита ИССП вектора:
100 => “врем”
101 => “рдмем”
110 => “врег”
111 => “рдрег”
Међутим, потпуно разумевање процеса на чипу може се добити само директном комуникацијом са ПСоЦ-ом.
5.3. Комуникација са ПСоЦ-ом
Пошто је већ Дирк Петрауцки портед Ципрессов ХССП код на Ардуину, користио сам Ардуино Уно да се повежем на ИССП конектор на тастатури.
Имајте на уму да сам током свог истраживања прилично променио Дирков код. Моју модификацију можете пронаћи на ГитХуб-у: овде и одговарајућу Питхон скрипту за комуникацију са Ардуином, у мом спремишту ципресс_псоц_тоолс.
Дакле, користећи Ардуино, прво сам користио само „званичне“ векторе за „комуникацију“. Покушао сам да прочитам интерни РОМ користећи команду ВЕРИФИ. Као што се очекивало, нисам успео да урадим ово. Вероватно због чињенице да су битови за заштиту од читања активирани унутар флеш диска.
Затим сам направио неколико својих једноставних вектора за писање и читање меморије/регистра. Имајте на уму да можемо прочитати цео СРОМ иако је флеш диск заштићен!
5.4. Идентификација регистара на чипу
Након што сам погледао „растављене“ векторе, открио сам да уређај користи недокументоване регистре (0кФ8-0кФА) за спецификацију М8Ц кодова операција, који се извршавају директно, заобилазећи заштиту. Ово ми је омогућило да покренем различите опкодове као што су "АДД", "МОВ А, Кс", "ПУСХ" или "ЈМП". Захваљујући њима (гледајући нежељене ефекте које имају на регистре) успео сам да утврдим који су од недокументованих регистара заправо регуларни регистри (А, Кс, СП и ПЦ).
Као резултат тога, „растављени“ код који генерише алатка ХССП_дисас.рб изгледа овако (додао сам коментаре ради јасноће):
У овој фази већ могу да комуницирам са ПСоЦ-ом, али још увек немам поуздане информације о безбедносним битовима флеш диска. Био сам веома изненађен чињеницом да Ципресс не пружа кориснику уређаја никакво средство да провери да ли је заштита активирана. Копао сам дубље у Гоогле да бих коначно схватио да је ХССП код који је дао Ципресс ажуриран након што је Дирк објавио своју модификацију. И тако! Појавио се овај нови вектор:
Користећи овај вектор (погледајте реад_сецурити_дата у псоц.пи), добијамо све безбедносне битове у СРАМ-у на 0к80, где постоје два бита по заштићеном блоку.
Резултат је депресиван: све је заштићено у режиму „онемогући екстерно читање и писање“. Дакле, не само да не можемо ништа да читамо са флеш диска, већ не можемо ништа ни да пишемо (на пример, да тамо инсталирамо РОМ дампер). А једини начин да онемогућите заштиту је да потпуно избришете цео чип. 🙁
6. Први (неуспели) напад: РОМКС
Међутим, можемо испробати следећи трик: пошто имамо могућност да извршимо произвољне опкодове, зашто не бисмо извршили РОМКС, који се користи за читање флеш меморије? Овај приступ има добре шансе за успех. Зато што функција РеадБлоцк која чита податке са СРОМ-а (који користе вектори) проверава да ли је позвана из ИССП-а. Међутим, РОМКС опкод вероватно неће имати такву проверу. Дакле, ево Питхон кода (након додавања неколико помоћних класа у Ардуино код):
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
Нажалост, овај код не ради. 🙁 Тачније ради, али на излазу добијамо сопствене кодове (0к28 0к30 0к40)! Не мислим да је одговарајућа функционалност уређаја елемент заштите од читања. Ово више личи на инжењерски трик: када се извршавају екстерни опкодови, РОМ магистрала се преусмерава на привремени бафер.
Ово у суштини позива СРОМ функцију 0к07, као што је представљено у документацији (курзив мој):
Ова функција провера контролне суме. Он израчунава 16-битну контролну суму броја кориснички специфицираних блокова у једној флеш банци, почевши од нуле. Параметар БЛОЦКИД се користи за прослеђивање броја блокова који ће се користити приликом израчунавања контролне суме. Вредност "1" ће израчунати само контролни збир за нулти блок; док "0" ће узроковати да се израчуна укупна контролна сума свих 256 блокова флеш банке. 16-битни контролни збир се враћа преко КЕИ1 и КЕИ2. Параметар КЕИ1 чува 8 битова нижег реда контролне суме, а КЕИ2 параметар чува 8 битова високог реда. За уређаје са неколико флеш банака, функција контролне суме се позива за сваки посебно. Број банке са којом ће радити се поставља регистром ФЛС_ПР1 (постављањем бита у њему који одговара циљној флеш банци).
Имајте на уму да је ово једноставна контролна сума: бајтови се једноставно додају један за другим; нема фенси ЦРЦ-а. Поред тога, знајући да језгро М8Ц има веома мали скуп регистара, претпоставио сам да ће приликом израчунавања контролне суме, међувредности бити забележене у истим варијаблама које ће на крају ићи на излаз: КЕИ1 (0кФ8) / КЕИ2 ( 0кФ9).
Дакле, у теорији мој напад изгледа овако:
Повезујемо се преко ИССП-а.
Почињемо израчунавање контролне суме помоћу вектора ЦХЕЦКСУМ-СЕТУП.
Поново покрећемо процесор након одређеног времена Т.
Читамо РАМ да бисмо добили тренутни контролни збир Ц.
Поновите кораке 3 и 4, сваки пут мало повећавајући Т.
Опорављамо податке са флеш диска одузимањем претходне контролне суме Ц од тренутног.
Међутим, постоји проблем: вектор Инитиализе-1 који морамо да пошаљемо након поновног покретања замењује КЕИ1 и КЕИ2:
Овај код замењује нашу драгоцену контролну суму позивањем Цалибрате1 (СРОМ функција 9)... Можда можемо само да пошаљемо магични број (са почетка кода изнад) да уђемо у режим програмирања, а затим прочитамо СРАМ? И да, ради! Ардуино код који имплементира овај напад је прилично једноставан:
Сачекајте одређени временски период; узимајући у обзир следеће замке:
Губио сам много времена док нисам сазнао шта се испоставило делаиМицросецондс ради исправно само са кашњењима која не прелазе 16383 μс;
а затим поново убио исто толико времена док нисам открио да кашњење микросекунде, ако му се 0 преда као улаз, ради потпуно погрешно!
Поново покрените ПСоЦ у режим програмирања (само шаљемо магични број, без слања вектора иницијализације).
Коначни код у Питхон-у:
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))
Укратко, шта овај код ради:
Поново покреће ПСоЦ (и шаље му магични број).
Шаље пуне векторе иницијализације.
Позива Ардуино функцију Цмнд_СТК_СТАРТ_ЦСУМ (0к85), где се кашњење у микросекундама преноси као параметар.
Чита контролни збир (0кФ8 и 0кФ9) и недокументовани регистар 0кФ1.
Овај код се извршава 10 пута у 1 микросекунди. 0кФ1 је укључен овде јер је то био једини регистар који се променио приликом израчунавања контролне суме. Можда је то нека врста привремене променљиве коју користи аритметичко-логичка јединица. Обратите пажњу на ружан хак који користим да ресетујем Ардуино користећи пицоцом када Ардуино престане да показује знаке живота (немам појма зашто).
7.2. Читање резултата
Резултат Питхон скрипте изгледа овако (поједностављено ради читљивости):
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 μс по бајту, можемо да искористимо ово време да проверимо вредност контролне суме у одговарајуће време. За прве вожње све се очитава прилично лако, пошто је трајање рачунског поступка увек готово исто. Међутим, крај овог думп-а је мање прецизан јер се „мања одступања времена“ при свакој вожњи сабирају и постају значајна:
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. Фласх бинарна реконструкција
Још нисам завршио писање кода који ће у потпуности реконструисати програмски код флеш диска, узимајући у обзир сва временска одступања. Међутим, већ сам вратио почетак овог кода. Да бих био сигуран да сам то урадио исправно, раставио сам га користећи м8цдис:
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 мс након поновног покретања. Онда сам унео погрешан ПИН и урадио исто.
Резултат није био баш пријатан, јер је било много промена. Али на крају сам успео да утврдим да се контролни збир променио негде између 120000 µс и 140000 µс кашњења. Али „пинкод“ који сам тамо приказао био је потпуно нетачан - због артефакта процедуре делаиМицросецондс, која ради чудне ствари када јој се проследи 0.
Затим, након што сам провео скоро 3 сата, сетио сам се да СРОМ системски позив ЦхецкСум прима аргумент као улаз који одређује број блокова за контролни збир! То. лако можемо локализовати адресу складиштења ПИН кода и бројача „нетачних покушаја“, са тачношћу до блока од 64 бајта.
Моје почетне вожње дале су следеће резултате:
Затим сам променио ПИН код из "123456" у "1234567" и добио:
Тако се чини да су ПИН код и бројач погрешних покушаја ускладиштени у блоку број 126.
7.5. Узимање депоније блока бр.126
Блок #126 би требало да се налази негде око 125к64к18 = 144000μс, од почетка израчунавања контролне суме, у мом пуном депонију, и изгледа прилично уверљиво. Затим, након што сам ручно одбацио бројне неважеће депоније (због акумулације „мањих временских одступања“), на крају сам добио ове бајтове (са кашњењем од 145527 μс):
Сасвим је очигледно да се ПИН код чува у нешифрованом облику! Ове вредности, наравно, нису записане у АСЦИИ кодовима, али, како се испоставило, одражавају очитавања са капацитивне тастатуре.
Коначно, извршио сам још неколико тестова да пронађем где је сачуван бројач лоших покушаја. Ево резултата:
0кФФ - значи "15 покушаја" и смањује се са сваким неуспелим покушајем.
Имајте на уму да су вредности кашњења које сам користио вероватно релевантне за један одређени ПСоЦ - онај који сам користио.
8. Шта је следеће?
Дакле, хајде да сумирамо на страни ПСоЦ-а, у контексту нашег Аиго погона:
можемо читати СРАМ чак и ако је заштићен од читања;
Можемо заобићи заштиту против превлачења користећи напад праћења хладног покретања и директно читање ПИН кода.
Међутим, наш напад има неке недостатке због проблема са синхронизацијом. Може се побољшати на следећи начин:
напишите услужни програм за исправно декодирање излазних података који се добијају као резултат "цолд боот траце" напада;
користите ФПГА гаџет за креирање прецизнијих временских кашњења (или користите Ардуино хардверске тајмере);
покушајте са другим нападом: унесите намерно нетачан ПИН код, рестартујте и избаците РАМ, надајући се да ће тачан ПИН код бити сачуван у РАМ-у ради поређења. Међутим, то није тако лако урадити на Ардуину, пошто је ниво сигнала Ардуино 5 волти, док плоча коју испитујемо ради са сигналима од 3,3 волта.
Једна занимљива ствар која би се могла испробати је поигравање са нивоом напона да би се заобишла заштита читања. Ако би овај приступ функционисао, могли бисмо да добијемо апсолутно тачне податке са флеш диска – уместо да се ослањамо на читање контролне суме са непрецизним временским кашњењима.
Пошто СРОМ вероватно чита заштитне битове преко РеадБлоцк системског позива, могли бисмо да урадимо исту ствар као opisano на блогу Дмитрија Недоспасова - реимплементација напада Криса Герлинског, најављена на конференцији "РЕцон Брисел 2017".
Још једна забавна ствар која би могла да се уради је да се одвоји кућиште од чипа: да се направи СРАМ думп, идентификује недокументоване системске позиве и рањивости.
9. Закључак
Дакле, заштита овог драјва оставља много да се пожели, јер користи обичан (не „очврснути“) микроконтролер за чување ПИН кода... Плус, нисам (још) погледао како се ствари одвијају са подацима шифровање на овом уређају!
Шта можете препоручити за Аиго? Након анализе неколико модела шифрованих ХДД дискова, направио сам 2015 презентација на СиСцан-у, у којем је испитао безбедносне проблеме неколико екстерних ХДД дискова и дао препоруке шта би се на њима могло побољшати. 🙂
Провео сам два викенда и неколико вечери радећи ово истраживање. Укупно око 40 сати. Рачунајући од самог почетка (када сам отворио диск) до краја (дамп ПИН кода). Истих 40 сати укључује време које сам потрошио на писање овог чланка. Било је то веома узбудљиво путовање.