RR

The task’s description points at data recovery from a broken RAID array:

“One of my drives failed”

Reusing All of Internal Disks

The ZIP contains 3 disk images, the 2nd of which is faulty:

512M  1.img
0B    2.img
512M  3.img

1.img & 3.img are different, ruling out a (completely trivial) RAID 1.

By far, the most common 3-drive configuration that allows for recovery (in case a single drive fails) is RAID 5. Let’s go with that.

Once you acquaint yourself with RAID 5 (you may wish to do so now), array reconstruction is fairly simple to implement. The hard part is detecting the array’s block size & drive order (as that affects the parity schedule) – but all it takes for this task is a few educated guesses and a modicum of luck.

import numpy as np

def xor_blocks(lhs, rhs):
    assert len(lhs) == len(rhs)
    lhs = np.frombuffer(lhs, dtype=np.uint8)
    rhs = np.frombuffer(rhs, dtype=np.uint8)
    return (lhs ^ rhs).tostring()

drives = (
    open("1.img"), # Drive 0 - OK
    open("2.img"), # Drive 1 - Faulty
    open("3.img")  # Drive 2 - OK
)
extracted = open("recovered.img", 'w')

parity_drive = 2  # The first stripe stores parity information on this drive. Rotates with every iteration.
block_size = 64 * 1024  # An educated guess. It's possible that other sizes would work well enough for flag recovery.

# Each iteration recovers a stripe
while True:
    drive_blocks = [d.read(block_size) for d in drives]
    if len(drive_blocks[0]) == 0:
        print "Done"
        exit()

    if parity_drive == 0:
        # 0 | Parity - Valid
        # 1 | Block1 - Missing
        # 2 | Block2 - Valid
        extracted.write(xor_blocks(drive_blocks[0], drive_blocks[2]))
        extracted.write(drive_blocks[2])
    elif parity_drive == 1:
        # 0 | Block1 - Valid
        # 1 | Parity - Missing
        # 2 | Block2 - Valid
        extracted.write(drive_blocks[0])
        extracted.write(drive_blocks[2])
    elif parity_drive == 2:
        # 0 | Block1 - Valid
        # 1 | Block2 - Missing
        # 2 | Parity - Valid
        extracted.write(drive_blocks[0])
        extracted.write(xor_blocks(drive_blocks[0], drive_blocks[2]))

    # Set the next stripe's parity drive (2, 0, 1, 2, 0, 1, 2, 0, 1, .....)
    parity_drive = (parity_drive + 1) % len(drives)

We tried booting from recovered.img (Using VirtualBox & VBoxManage convertfromraw to create a VDI) — as 1.img contains a proper MBR — but that failed spectacularly 🙁

Next, we ran PhotoRec on the recovered drive. It emitted a bunch of (useless) text files and a single JPG:

And that’s all there was to it!

(That’s literally the flag’s text in the picture – no conversion of any sort was required)