Ξεκλειδωμα κρυπτογραφημένου δίσκου με ένα απλο στικάκι USB

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

Το σύστημα της δοκιμής τρέχει Pop OS, το οποίο χρησιμοποιείς το initramfs-tools για τη δημιουργία του αρχικού ramdisk εκκίνησης. Αν το σύστημα σας χρησιμοποιεί το drakut δε θα λειτουργήσει και θα πρέπει να τροποποιηθούν οι οδηγίες. Αν κάποιος το κάνει θα εκτιμούσα ένα σχόλιο με το πως το έκανε.

Για την κρυπτογράφηση των δίσκων

Διαβάστε πρώτα το κεφάλαιο “Εξερεύνηση του κρυπτογραφημένου δίσκου” στο άρθρο

όπου έχει κάποιες λίγες, αλλά χρήσιμες πληροφορίες για τους κρυπτογραφημένους δίσκους.

Πως θα είναι αποθηκευμένος ο κωδικός

Μα κάπου μέσα στο στικάκι, αλλά πως; Υπάρχουν οι εξής λύσεις

  • Σαν κάποιο αρχείο μέσα στο στικάκι, η ποιο απλή λύση, αλλά μπορεί να σβηστεί το αρχείο
  • Κρυμμένο μέσα στο στικάκι, έκτος συστήματος αρχείων με την εντολή dd στους τελευταίους sectors
  • Πουθενά :face_with_monocle:

Δοκίμασα και τις 3 λύσεις και βρίσκω την τελευταία σαν την καλύτερη, γιατί μιας και δεν υπάρχει η πληροφορία μέσα στον δίσκο, μπορείς ακόμα και να το κάνεις format. Πώς όμως;

Σε ένα τερματικό γράφουμε (αλλάξτε το /dev/sdc με αυτό του USB):

udevadm info -n /dev/sdc | grep ID_ | cut -f2 -d" "

Μεταξύ άλλων θα δούμε κάποια πράγματαπου μας βοηθούν να αναγνωρίσουμε την συσκευή

ID_VENDOR=USB
ID_VENDOR_ENC=\x20USB\x20\x20\x20\x20
ID_VENDOR_ID=0781
ID_MODEL=SanDisk_3.2Gen1
ID_MODEL_ENC=\x20SanDisk\x203.2Gen1
ID_MODEL_ID=5583
ID_REVISION=1.00
ID_SERIAL=USB_SanDisk_3.2Gen1_04013df.........
ID_SERIAL_SHORT=04013dfec44a56d6b825.......
ID_USB_DRIVER=usb-storage
ID_PATH=pci-0000:02:00.0-usb-0:3:1.0-scsi-0:0:0:0
ID_PART_TABLE_TYPE=dos

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

Αν πάει κάτι στραβά

Στη διαδικασία που ακολουθεί θα τροποποιήσουμε το initramdisk. Και πιστέψτε με πολλά μπορούν να πάνε στραβά. Οπότε καλό είναι να κάνουμε πρώτα ένα αντίγραφο του αρχικού, ώστε πειράζοντας στον grub ή στο systemd-boot το command line, να μπορούμε να συνδεθούμε:

sudo cp /boot/initrd.img-$(uname -r) /boot/initrd.img-$(uname -r)-old

Θα πρέπει να ξέρεις πως να αλλάξεις τις παραμέτρους απο το grub ή το systemd-boot. Στο τελευταίο στο μενού πατάς e. Αλλάζεις το initrd με το αντίγραφο.

Βήμα 1: Αυτόματη αποκρυπτογράφηση

Θα φτιάξομε ένα αρχείο sh με όνομα crypt-usb-from-serial και θα το μεταφέρουμε στον κατάλογο /usr/local/bin ή όπου αλλού θέλουμε. Θα περιέχει τα παρακάτω:

#!/bin/sh

# Replace with your usb parameters. Find yours with:
# udevadm info -n /dev/... | grep ID_ | cut -f2 -d" "
ID_VENDOR_ENC="\x20USB\x20\x20\x20\x20"
ID_MODEL_ENC="\x20SanDisk\x203.2Gen1"
ID_VENDOR_ID="0781"
ID_MODEL_ID="5583"

## Number of tries, allow at least 2 for password fallback
NUM_TRIES=3

if test ! "$CRYPTTAB_TRIED" = $NUM_TRIES
then
  
  temp_file=$(mktemp)
  all_disks=$(lsblk -l -o PATH,TYPE | grep disk | cut -f1 -d" ")
  
  for disk in $all_disks; do
    udevadm info -n "$disk" | grep ID_ | cut -f2 -d" " > "$temp_file"

    vendor="$(grep 'ID_VENDOR_ENC=' < "$temp_file" | cut -f2 -d'=')"
    model="$(grep 'ID_MODEL_ENC=' < "$temp_file" | cut -f2 -d'=')"
    vendor_id="$(grep 'ID_VENDOR_ID=' < "$temp_file" | cut -f2 -d'=')"
    model_id="$(grep 'ID_MODEL_ID=' < "$temp_file" | cut -f2 -d'=')"
    vendor_id="$(grep ID_VENDOR_ID < "$temp_file" | cut -f2 -d'=')"

    if test "$vendor" = $ID_VENDOR_ENC -a "$model" = $ID_MODEL_ENC  ; then
      if test "$vendor_id" = $ID_VENDOR_ID -a "$model_id" = $ID_MODEL_ID ; then
      unlock_key="$(grep 'ID_SERIAL=' < "$temp_file" | cut -f2 -d'=')"
      # Pass password to unlock
      echo "$unlock_key" 
      fi
    fi
  done

else
  /lib/cryptsetup/askpass "Please enter passphrase: "
fi

Αν θέλεις να το προσαρμόσεις μερικές παρατηρήσεις: Σκοπός του είναι να γράψεις στην έξοδο τον κωδικό. Στην ποιο απλή του μορφή θα μπορούσε να είναι ένα απλό echo sectret. Οπότε δεν μπορείς να το κάνεις debug με εντολές echo.

Δεν είναι ένα αρχείο BASH! Θα τρέξει στο busybox που έχει μια πολύ πιο απλή γλώσσα. Εναλλακτικά θα μπορούσαμε να το κάνουμε bash και να προσθέσουμε το ίδιο το bash στο initrd. Διάλεξα τον δύσκολο δρόμο, και τις ελάχιστες δυνατές εξαρτήσεις, για να το κρατήσω όσο μικρότερο είναι δυνατόν.

Κατά την εκκίνηση θα γίνει προσπάθεια για ξεκλείδωμα κάμποσες φορές. Θα δούμε πως αλλάζουμε αυτό το νούμερο παρακάτω. Θα κάνει μια προσπάθεια να το ξεκλειδώσει NUM_TRIES=3 φορές. Αλλιώς θα δοκιμάσει να ζητήσει τον κωδικό από τον χρήστη. Οπότε είναι σημαντικό να του αφήσεις επιπλέον προσπάθειες. Εναλλακτικά βέβαια μπορείς πάντα να απαιτείς πάντα το στικάκι.

Γιατί θέλει να δοκιμάσει πολλές φορές; Γιατί ο υπολογιστής ξεκινά και μπορεί το στικάκι να μην είναι έτοιμο.

Τέλος, είναι απαραίτητο να του κάνεις chmod +x.

Η ώρα της αλήθειας. Το τρέχουμε και πρέπει να δούμε τον κωδικό. Αν τον δούμε προχωράμε. Μπορεί όμως να μη δουλεύει μέσα στο initrd. Σε αυτή την περίπτωση μια χρήσιμη παράμετρος στην εκκίνηση είναι η break=premount.

Βήμα 2: Ενημέρωση του /etc/crypttab

Τώρα θα πρέπει να πούμε στο σύστημα να χρησιμοποιεί αυτό το αρχείο. Θα το κάνουμε στο αρχείο /etc/crypttab. Θα τροποποιήσουμε τη σχετική γραμμή να γράφει:

cryptdata UUID=3aa...  none        luks,...,keyscript=/usr/local/bin/crypt-usb-from-serial,tries=6

Προσέξτε έδωσα 6 ευκαιρίες να ξεκλειδώσει ο δίσκος. Οι 3 θα δοκιμαστούν με το κλειδί. Μετά έχω 3 ευκαιρίες να μαντέψω σωστά τον κωδικό.

Βήμα 3: Εξαγωγή του κλειδιού και ορισμός του ως κλειδί αποκρυπτογράφησης

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

Σε ένα τερματικό:

./crypt-usb-from-serial   | tee sectret.txt

και προσθέτουμε τον σειριακό αριθμό σαν κλειδί αποκρυπτογράφησης

cryptsetup luksAddKey /dev/... sectret.txt

θα πρέπει να δώσουμε ένα υπάρχων κλειδί αποκρυπτογράφησης.

Αντικαταστήστε το /dev/.. με την κρυπτογραφημένη κατάτμηση στο σύστημα σας. Δεν είναι ανάγκη να μπούμε σε κάποιο live περιβάλλον.

Μπορούμε να σβήσουμε τώρα το αρχείο sectret.txt.

Βήμα 4: Προσθήκη στο inirtrd

Δεν τελειώσαμε ακόμα. Το ξεκλείδωμα του δίσκου θα γίνει στο initrd και μετά θα ξεκινήσει το λειτουργικό.

To initrd περιέχει ένα ελάχιστο σύστημα αρχείων με τα απαραίτητα για να φορτωθούν ότι οδηγοί χρειάζονται για να δει το σύστημα τους δίσκους και τα συστήματα αρχείων καθώς και να κάνει το όποιο ξεκλείδωμα στους δίσκους και γενικά οτι προετιμασία χρειάζετε για να ξεκινήσει το λειτουργικό απο τον δίσκο (ή απο κάπου αλλού σε υπολογιστές χωρίς δίσκους).

Θα πρέπει λοιπόν να μεταφέρουμε το σκριπτάκι κάναμε στο initrd. Επίσης να βεβαιωθούμε πως υπάρχει και ότι άλλο χρησιμοποιεί.

Για να καταλάβουεμ καλύτερα το initrd, ας δούμε πρώτα τι περιέχει:

lsinitramfs /boot/initrd.img-$(uname -r) | less

Θα πρέπει να σημειώσουμε τι χρησιμοποιεί το crypt-usb-from-serial όπως τις εντολές cut, grep, lsblk, udevadm κλπ… και να βεβαιωθύμε πως υπάρχουν μέσα.

Στο δικό μου σύστημα δεν υπάρχει ή lsblk και βέβαια σε όλα τα συστήματα δεν περιμένουμε να βρούμε το /usr/local/bin/crypt-usb-from-serial.

Θα φτιάξουμε ένα αρχείο /etc/initramfs-tools/hooks/crypt-hook-usb που θα περιέχει τα παρακάτω:

#!/bin/sh
# /etc/initramfs-tools/hooks/crypt-hook-usb

PREREQ=""

prereqs() {
        echo "$PREREQ"
}

case "$1" in
         prereqs)
                 prereqs
                 exit 0
         ;;
esac

. "${CONFDIR}/initramfs.conf"
. /usr/share/initramfs-tools/hook-functions

## Change from here

manual_add_modules usb_storage
copy_exec /usr/bin/lsblk                           /usr/bin/lsblk
copy_exec /usr/local/bin/crypt-usb-from-serial     /usr/local/bin/crypt-usb-from-serial

Στο αρχείο αυτό προσθέτουμε ότι επιπλέον θέλουμε να προστεθεί στο initrd. Έβαλα ένα επιπλέον module στον πυρήνα, έχει μείνει από παλιά προσπάθεια, αλλά το κράτησα να δείξω πως γίνετε.

Το βασικό το κάνουν οι γραμμές copy_exec που αντιγράφουν ένα εκτελέσιμο (μαζί με τις εξαρτήσεις του) από τον δίσκο στη ramdisk. Το αρχείο θα πρέπει να είναι εκτελέσιμο, οπότε φροντίστε να του έχετε κάνει πριν chmod +x.

Βήμα 5: Δημιουργία του Ramdisk

Επιτέλους ήρθε η ώρα να φτιάξουμε αυτήν τη ramdisk και να κάνουμε επανεκκίνησή και έλεγχο

sudo update-initramfs -k all -u

Εύχομαι όλα να πήγαν καλά :innocent:

Επόμενα βήματα

Γιατί να μείνουμε μόνο εκεί; Μπορούμε να επαναχρησιμοποιήσουμε το ίδιο κλειδί για να μη δίνουμε ποτέ τον κωδικό μας.

Διαβάστε πως εδώ:

Σημειώσεις για την ασφάλεια

Η μέθοδος είναι ασφαλής. Πουθενά δεν υπάρχει γραμμένος ο κωδικός αποκρυπτογράφησης στον δίσκο. Αλλά κρατήστε μυστική την ύπαρξη του κλειδιού. Στην τελική είναι ένα αθώο στικάκι με αρχεία είναι, όπως τόσα και τόσα άλλα :innocent:.

image

Αλλά οποιοσδήποτε που γνωρίζει το μυστικό μπορεί να αφαιρέσει το στικάκι και να κλέψει τον κωδικό. Και μπορεί εύκολα και φτηνά να φτιάξει μια USB συσκευή που να έχει τα ίδια στοιχεία. Δεν έχει μεγάλη διαφορά απο το να «δανειστεί» κάποιος τα κλειδίας σας και να πάει σε ένα κλειδαρά να βγάλει αντίγραφα.

Θα μπορούσε βέβαια κανείς να «αλατίσει» τον κωδικό – το αφήνω σαν άσκηση για όποιον θέλει να το κάνει στα σχόλια – για μεγαλύτερη ασφάλεια. Αλλά έχει νόημα; το αλάτι θα πρεπει να υπάρχει μέσα στο initrd το οποίο είναι ένα απλό αρχείο cpio χωρίς κρυπτογράφηση.

ΥΓ: Το άρθρο αυτό έχει γραφτεί με πολλά δάκρυα, πόνο :joy:

6 «Μου αρέσει»