fkr.dev

About


I am Flo, born 1990, developer, hacker, artist and this is my blog. Into lisps, oil painting and esoteric text editors. On the interwebs I like to appear as a badger interested in cryptography. Every other decade I like spinning old jazz/funk records or programming experimental electronic music as Winfried Scratchmann. Currently located in Aix la Chapelle.

Recovering an encrypted persistent TAILS partition from a reformatted medium

Table of Contents

The Problem
The path to recovery
Last weekend a friend and me had a little emergency to fix, and here is how it played out:
The ProblemIt went as usual, the problems arise on sundays, sometime at noon, just when you are about to wake up. Some person contacted us, stating that there was some encrypted persistent partition of a tails-live usb-stick that somehow got “overwritten” with a new partition type.
Since a friend and I are no data forensicists(?) the main relevant question was whether the new partition was already in use so that the data could be corrupted already. Since we were also dealing with an encrypted partition I think even a little bit of lost data could’ve spelled disaster here.
The path to recovery
  • Welp, since everyday is a great day to try some new things we accepted the challenge, obtained the USB-stick and had a look.Always make a backup As stated above, we ain’t no forensic guys, but the common sense dictates: always try to recover every bit and do this by first and foremost: backup bitwise and only ever work on the copy:
    dd if=/dev/sdX of=/path/to/backup.img
    
    As every hobby-it-support-guy knows, fixing other peoples computing problems usually consists of 10% googling, 80% waiting and 10% hard earned knowledge. So as I happened to have no space left on my ever-to-small harddisk I had to clone the stick on another stick (so no of=/path/to/backup.img more like of=/dev/sdY) which took a whooping 2 hours for 64GB. (64000 / (60*60*2) = 8MB/s hmpf) So our clients had enough time to learn the it-support-guys first virtue: patience.
    img

  • To little surprise fdisk -l really showed a definitly-not-tails partition and -type. However on the first look the partition indeed seemed to be empty. In the meantime we had done the duty of googling that yielded (at least) three potential remedys:
    • TestDisk: a tool to recover partitions and files from a disk
    • Part
    • ..
  • We went on, hit the analyze button in the testdisk TUI and were greeted with another, way to slowly growing, percentage bar. Meanwhile ADHD was seriously kicking in so we decided to start fiddling ourselves: Why not have a look at the file structure using a hex editor and some offset addresses we obtained from looking at benign tails partitions.
    whether magic header could tell us about the beginning of a yet to recover encrypted partition. Try this, try that, end with
    cat /path/to/backup.img | hexdump -C | grep -i "luks"
    
    Yes, dear reader, in the meantime the friend with sufficient disk space had arrived and dumped the stick aswell!
    img

    And happy little it-support-guys were we when the incantation above yielded:
    100000000  4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00  |LUKS..........@.|
    
    (babe!)
  • We took the leap of faith and asserted that all the data after the header would-could-should be the encrypted partition. After making one, maybe two mistakes we settled on a costly but hopefully correct solution path:
    1. Take another 64GB usb stick,
    2. set up tails in a way that mimics the setup on the corrupted stick
    3. dd the corrupted stick, starting from the header located above, on to the fresh stick
    4. ..profit!
The solution + aftermath
dd if=/path/to/backup.img of=/dev/sdZ bs=512 skip=<addr-from-above>/512 seek=<addr-from-above>/512
And just like that - once again some hobby-it-support-guys saved the day. It was close, though: the final dd command finished some time short before midnight. For me this leaves one relevant question: why do those, in hindsight rather simple operations/problems always take so much time?
On a side note: The tools above did not yield a satisfying result. In the light of the simplistic solution above I am either pretty disappointed or we simply did not know / took the time to use the tools correctly. If you think you know - please leave a comment!
I implemented the solution above in a simple script in case it is you who gets the next call on an otherwise (as i heard the next day) sunny sunday. Please read the script before applying, I did not find the time to test it thoroughly and we operate in sensitive spots here.
Seriously, though, do not run this code until you find a link here that shows this script under version control. I did not run it once and actually the constants should be wrong because I do not find time to look up the correct ones. Find me chipping away at my [TODO](file:///todo.html) list.
import subprocess
import os
TAILS_LUKS_MAGIC_BYTES="4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00"
input_file = input("Please enter the path to the backup file:\n")
assert os.path.isfile(input_file), "File does not exist"

# find luks header start
cmd = f"cat {input_file} | hexdump -C | grep -i 'luks' | head -n 1 | cut -d ' ' -f 1"
print(cmd)
addr = subprocess.check_output(cmd, shell=True).decode()
addr = int(addr, 16)
print("Found LUKS header at address: {}".format(addr))

# instructions
print("="*80)
print("Set up a Tails-USB stick in the way the corrupted USB-Stick was set up.")
print("E.g. choose the same size and set up a persistent partition.")
print("="*80)
new_stick = input("Please enter the path of the new USB-Stick:\n")
assert os.path.exists(new_stick), "Device does not exist"

# hexdump the same addr on new stick to assert compatibility
cmd = f"sudo dd if={new_stick} skip={addr} count=1 | hexdump -C  | head -1"
print(cmd)
assert "babe" in subprocess.check_output(cmd, shell=True).decode().replace(" ", ""), "The new stick is not compatible"

# dd the backup file from offset addr indicating partition start to the new stick
cmd = f"sudo dd if={input_file} of={new_stick} skip={addr/512} seek={addr/512}"
print(cmd)
subprocess.check_output(cmd, shell=True)
print("done.")
And thats that!
Honorable mentions
  • GNU Coreutils: they are awesome.
  • iotop: if you forgot the status=progress flag for dd, iotop allows you to show at least some changing things to the “clients” :D
  • baclava: because sugar is energy!

fkr.dev

About


I am Flo, born 1990, developer, hacker, artist and this is my blog. Into lisps, oil painting and esoteric text editors. On the interwebs I like to appear as a badger interested in cryptography. Every other decade I like spinning old jazz/funk records or programming experimental electronic music as Winfried Scratchmann. Currently located in Aix la Chapelle.

Recovering an encrypted persistent TAILS partition from a reformatted medium

Table of Contents

The Problem
The path to recovery
Last weekend a friend and me had a little emergency to fix, and here is how it played out:
The ProblemIt went as usual, the problems arise on sundays, sometime at noon, just when you are about to wake up. Some person contacted us, stating that there was some encrypted persistent partition of a tails-live usb-stick that somehow got “overwritten” with a new partition type.
Since a friend and I are no data forensicists(?) the main relevant question was whether the new partition was already in use so that the data could be corrupted already. Since we were also dealing with an encrypted partition I think even a little bit of lost data could’ve spelled disaster here.
The path to recovery
  • Welp, since everyday is a great day to try some new things we accepted the challenge, obtained the USB-stick and had a look.Always make a backup As stated above, we ain’t no forensic guys, but the common sense dictates: always try to recover every bit and do this by first and foremost: backup bitwise and only ever work on the copy:
    dd if=/dev/sdX of=/path/to/backup.img
    
    As every hobby-it-support-guy knows, fixing other peoples computing problems usually consists of 10% googling, 80% waiting and 10% hard earned knowledge. So as I happened to have no space left on my ever-to-small harddisk I had to clone the stick on another stick (so no of=/path/to/backup.img more like of=/dev/sdY) which took a whooping 2 hours for 64GB. (64000 / (60*60*2) = 8MB/s hmpf) So our clients had enough time to learn the it-support-guys first virtue: patience.
    img

  • To little surprise fdisk -l really showed a definitly-not-tails partition and -type. However on the first look the partition indeed seemed to be empty. In the meantime we had done the duty of googling that yielded (at least) three potential remedys:
    • TestDisk: a tool to recover partitions and files from a disk
    • Part
    • ..
  • We went on, hit the analyze button in the testdisk TUI and were greeted with another, way to slowly growing, percentage bar. Meanwhile ADHD was seriously kicking in so we decided to start fiddling ourselves: Why not have a look at the file structure using a hex editor and some offset addresses we obtained from looking at benign tails partitions.
    whether magic header could tell us about the beginning of a yet to recover encrypted partition. Try this, try that, end with
    cat /path/to/backup.img | hexdump -C | grep -i "luks"
    
    Yes, dear reader, in the meantime the friend with sufficient disk space had arrived and dumped the stick aswell!
    img

    And happy little it-support-guys were we when the incantation above yielded:
    100000000  4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00  |LUKS..........@.|
    
    (babe!)
  • We took the leap of faith and asserted that all the data after the header would-could-should be the encrypted partition. After making one, maybe two mistakes we settled on a costly but hopefully correct solution path:
    1. Take another 64GB usb stick,
    2. set up tails in a way that mimics the setup on the corrupted stick
    3. dd the corrupted stick, starting from the header located above, on to the fresh stick
    4. ..profit!
The solution + aftermath
dd if=/path/to/backup.img of=/dev/sdZ bs=512 skip=<addr-from-above>/512 seek=<addr-from-above>/512
And just like that - once again some hobby-it-support-guys saved the day. It was close, though: the final dd command finished some time short before midnight. For me this leaves one relevant question: why do those, in hindsight rather simple operations/problems always take so much time?
On a side note: The tools above did not yield a satisfying result. In the light of the simplistic solution above I am either pretty disappointed or we simply did not know / took the time to use the tools correctly. If you think you know - please leave a comment!
I implemented the solution above in a simple script in case it is you who gets the next call on an otherwise (as i heard the next day) sunny sunday. Please read the script before applying, I did not find the time to test it thoroughly and we operate in sensitive spots here.
Seriously, though, do not run this code until you find a link here that shows this script under version control. I did not run it once and actually the constants should be wrong because I do not find time to look up the correct ones. Find me chipping away at my [TODO](file:///todo.html) list.
import subprocess
import os
TAILS_LUKS_MAGIC_BYTES="4c 55 4b 53 ba be 00 02  00 00 00 00 00 00 40 00"
input_file = input("Please enter the path to the backup file:\n")
assert os.path.isfile(input_file), "File does not exist"

# find luks header start
cmd = f"cat {input_file} | hexdump -C | grep -i 'luks' | head -n 1 | cut -d ' ' -f 1"
print(cmd)
addr = subprocess.check_output(cmd, shell=True).decode()
addr = int(addr, 16)
print("Found LUKS header at address: {}".format(addr))

# instructions
print("="*80)
print("Set up a Tails-USB stick in the way the corrupted USB-Stick was set up.")
print("E.g. choose the same size and set up a persistent partition.")
print("="*80)
new_stick = input("Please enter the path of the new USB-Stick:\n")
assert os.path.exists(new_stick), "Device does not exist"

# hexdump the same addr on new stick to assert compatibility
cmd = f"sudo dd if={new_stick} skip={addr} count=1 | hexdump -C  | head -1"
print(cmd)
assert "babe" in subprocess.check_output(cmd, shell=True).decode().replace(" ", ""), "The new stick is not compatible"

# dd the backup file from offset addr indicating partition start to the new stick
cmd = f"sudo dd if={input_file} of={new_stick} skip={addr/512} seek={addr/512}"
print(cmd)
subprocess.check_output(cmd, shell=True)
print("done.")
And thats that!
Honorable mentions
  • GNU Coreutils: they are awesome.
  • iotop: if you forgot the status=progress flag for dd, iotop allows you to show at least some changing things to the “clients” :D
  • baclava: because sugar is energy!