Full detailed writeup for ReAL-File-System which is centered around ReFS Log Analysis.
tl;dr
- Disk Forensics
- Resilient File System
- Log Analysis
Challenge Points: 1000
No. of solves: 0
Challenge Author: 5h4rrK
Challenge Description
In a desperate bid to synchronize my PC clock, I unwittingly downloaded an application that promised a quick fix but instead wrought havoc by encrypting all my important files. Turning to my DFIR friend for help, his attempts to decipher the encrypted mess only worsened the situation, leaving the filesystem corrupted. My friend told me that only a DFIR expert can help recover my files. I’m filled with hope for their assistance in fixing my computer catastrophe.
Question 1
List all directories that have been renamed, including their original names and the timestamps of when they were renamed.
TimeZone - UTC(+05:30) [YYYY-MM-DD HH:MM:SS.XX]
Format - [ [‘OriginalDirName’, ‘RenamedDirName’, ‘TimeStamp’] , .. ]
Question 2
Name all the deleted directories with deletion timestamps.
TimeZone : UTC(+05:30) [YYYY-MM-DD HH:MM:SS.XX]
Format - [ [‘DirectoryName’ , ‘TimeStamp’] , .. ]
Question 3
List all directories with their creation times, including originals if any that have been renamed or deleted.
TimeZone : UTC(+05:30) [YYYY-MM-DD HH:MM:SS.XX]
Format - [ [‘DirectoryName’ , ‘CreatedTime’] , …. ]
Question 4
Recover the files that have been deleted, and provide the md5sum of each recovered file.
Format - [ [‘file1hash’] , [‘file2hash’], … ]
Question 5
Identify all files that have been deleted (Simple + Permanent), including their deletion timestamps.
TimeZone : UTC(+05:30) [YYYY-MM-DD HH:MM:SS.XX]
Format - [ [ ‘filename’ , ‘TimeStamp’ , ‘Simple/Permanent’ ] , .. ]
Question 6
Restore all encrypted files, decrypt them, and provide the md5sum of each decrypted file after removing any extra bytes before computing the hash.
Format - [ [‘hash1’] , [‘hash2’], ‘,..]
Solution
To address the corruption in the file system, we’ll initiate the repair process by examining the file system using a Hex Editor. Since the Resilient File System (ReFS) is not widely supported by forensic tools, we’ll rely on research papers for its structures.
The most recent version of Active Disk Editor is capable of loading ReFS, so we can load the image and confirm its integrity.
Overview of ReFS structure
The Volume Boot Record (VBR) serves as a reference to the Superblock
, a crucial component. The Superblock
references to child nodes known as CheckPoint
.
Our initial step would be calculating the cluster size. Later on we need this for carving out the files
Cluster Size = BytesPerSector * SectorsPerCluster
ClusterSize = 0x08 * 0x200 = 0x1000 (4KB)
SuperBlock
The file system includes three SuperBlocks: one at the start and two at the end.
Each SuperBlock contains Cluster Numbers that point to primary and secondary Checkpoints.
This redundancy enhances system reliability by enabling quick recovery and maintaining data integrity.
CheckPoint
The file system has two CheckPoints.
Each CheckPoint includes ReFS version information, Entry Size, and Entry Offset relative in position.
At offset
0xD0
, there are 4 bytes representing the number of entries. Reading the next 4 bytes for each entry, we can identify the offsets to entries of the Object Table, Container Table, Directory Tree, and so on.Following these offsets allows us to precisely determine the locations of
MSB+
standard nodes. It’s crucial to note that the signature for standard nodes is absence in ReFS 1.2 version.
1 | META_HDR_3FORMAT = '<4sIQ16s4Q2Q' |
Source : ARIN
The SuperBlock
class inherits properties from the FSMetaPage
class. The first Logical Cluster Number LCN(1), is a self-referencing offset. This means that it points back to the SuperBlock
itself.
Similarly, in the case of the CheckPoint
, the first Logical Cluster Number LCN(1) also serves as a self-referencing offset .
Upon examination of the filesystem, it becomes evident that the SuperBlock
and CheckPoint
LCN(1) appears as null. To rectify this issue, we must ensure that the offset of the Superblock and CheckPoint is correctly set.
Offset of the first Superblock =
0x1E0000
LCN1 =
0x1E000 / 0x1000 = 0x1E
(Little Endian)
Similar calculations and adjustments are necessary for other Superblock and CheckPoint
- Within the filesystem, there are three SuperBlocks and two Checkpoints. Fixing any one of these SuperBlocks and Checkpoints will resolve the issue at hand.
- LCN1 :
0x1e
- Offset : 0x1e * 0x1000 = 0x1e000
- LCN1 :
0x0f7ffd
- LCN1 :
0x0f7ffe
- LCN1 :
0x27d0
- LCN1 :
0x01dc48
Now, let’s proceed by opening the file system image in Active Disk Editor.
LogFile
A logfile is a metadata file that stores transactional data to facilitate the recovery of the file system to a consistent state in the event of system crashes and unexpected conditions
It contains changes in metadata of files and directories like
CREATE
,MOVE
,RENAME
andDELETE
.In ReFS, logs are stored in a
LogFile
. The structure of theLogFile
differs significantly from that of NTFS.The opcodes used in ReFS
LogFile
differ from those found in NTFS’s$LogFile
.ReFS performs only
redo
operations, whereas NTFS performs bothredo
andundo
operations.We can locate the LogFile by searching
MLog
and ignores the first twoMLog
pages (control log type).
ReFS LogFile vs NTFS $LogFile
Fig : Redo Structure of ReFS
Fig : Structure of Operation Record of NTFS $LogFile
Read Finding Forensic Information on Creating a Folder in $LogFile of NTFS
Now, let’s explore the structure of the ReFS LogFile in detail.
ReFS LogFile Structure
Explanation
Entry Header and Log Record Header
Red Bounded Area ->
ReFS Entry Header
Green Bounded Area ->ReFS Log Header
Log Record
Redo Record Header
Redo Log Header Size : 0x38 bytes
Redo Data Offset Array
Tail Offset
: indicates ending of data offset array
Tail Size
: size of each transaction
Redo Transactional Data
Example 1
Example 2
1 | REFS_REDO_REC_HDR_FIELDS = [ |
Source: ARIN
Example 3
Example 4 (Control Log Type)
Fig : Control Log Type
Answers
Extracting Log Files from the File System
1 | dd if=ReAL_File_System.001 skip=$((0x20001000)) count=$((0x200A7000 -0x20001000)) of=logs.txt bs=1 |
Question-1
- List all directories that have been renamed, including their original names and the timestamps of when they were renamed.
Format :[ [‘OriginalDirName’, ‘RenamedDirName’, TimeStamp] , .. ]
To identify renamed directories in ReFS, search for the specific opcodes related to directory renaming. By examining consecutive MLog entries associated with these opcodes, we can reveal the timestamps indicating when the renaming occurred.
To locate directories in the LogFile, search for the hexadecimal values 30 00 02 00
, which serve as prefixes for directory entries. Conversely, the value 30 00 01 00
typically indicates file entries. However, it’s essential to note that there may be some false positives in the search results.
- File Prefix (from ReFS Detector) :
30 01 00 00 80 01 00 00 00 00 00 00 30 00 01 00
Directory Renamed Opcode : 0x2 -> 0x2 -> 0x1 -> 0x1 -> 0x4
Timestamp will be found in next MLog
page (starting with opcode 0x4
).
The timestamp in the ReFS LogFile is in Little-Endian format. To convert it to UTC +5:30, we can use DCode tool.
[‘e88e52cac’, ‘88077a4a1370’, ‘2024-02-18 13:10:20.49’]
We can write a script to automate the process.
[‘fb828d071’, ‘cad090f9724d’, ‘2024-02-18 13:12:12.48’]
Final Ans
1 | [ ['e88e52cac', '88077a4a1370', '2024-02-18 13:10:20.49'], ['fb828d071','cad090f9724d', '2024-02-18 13:12:12.48'] ] |
Question-2
- Name all the deleted directories with deletion timestamps.
Format : [ [‘DirectoryName’ , ‘TimeStamp’] , .. ]
Deletions can occur in two ways: Simple and Permanent.
Opcode for Permanent Deletion
1 | 0x2 -> 0xf -> 0x2 -> 0xf -> 0x4 -> 0x12 |
To find the remaining opcodes, you can search in the next MLog
entries. The timestamp can be found in opcode 0x04
.
Permanently Deleted Directories
1 | [ |
Simple Deleted Directory Opcode
1 | P(Directory Creation) → 0x06 → 0x04 → 0x04 → 0x04 → 0x04 → 0x03 → 0x02 → 0x02 → 0x01 → 0x01 → 0x0e → 0x04 → 0x03 → 0x04 → 0x04 → 0x04 → 0x01 → 0x04 → 0x03 → 0x04 → 0x04 → 0x08 |
After a directory is created, the opcode follows. It’s important to note that directories created with names starting with $
can be easily distinguished from other directory creations.
1 | ['c062fb828', '2024-02-18 13:10:48.62'] |
Alternatively, another method to determine the deletion timestamp is by analyzing $IXXXXX
entries.
Final Ans
1 | [ |
Question -3
- List all directories with their creation times, including renamed and deleted.
Note : If a directory was renamed, include its original name and creation time.
Fomat : [ [‘DirectoryName’ , ‘CreatedTime’] , …. ]
Dir Creation
1 | 0x00 → 0x00 → 0x04 → 0x10 → 0x01 → 0x01 → 0x01 → 0x0e → 0x03 → 0x04 |
Final Ans
1 | [ |
Question - 4
- Recover the files that have been deleted, and provide the md5sum of each recovered file.
Format : [ [‘file1hash’]’ , [‘file2hash2’], … ]
- We can find files that are simply deleted.
$IXXXXX
file has components like filename, fullpath , deletion time etc.$RXXXXX
file contents can be recovered.
We can use the dd command to extract a file from an image file. We can also increase the extraction speed by adjusting the block size.
Divide the skip and count by block-size.
Copying and pasting content from Active Disk Editor works for smaller files. However, for larger files, it won’t work :)
Extracting deleted files
1 | dd if=ReAL_File_System.001 skip=$(((0x1000 * 71168)/1)) count=$(((0x15a)/1)) bs=1 of=simple-pass.txt |
1 | f91488b7e00c31793bd7aa98c51896d0 simple-pass.txt |
Final Ans
1 | [ |
Question - 5
- Identify all files that have been deleted (Simple + Permanent), including their deletion timestamps.
Format :[ [ ‘filename’ , ‘TimeStamp’ , ‘Simple/Permanent’ ] , .. ]
Simple Deletion
Permanent Deletion
simple-pass.txt
TimeStamp
2024-02-18 13:15:00.51
In the same manner, examine the log file and we will find the other simple deleted files
1 | [ |
Permanent Deleted File Opcode
1 | 0x0f -> 0x02 -> 0x0f -> 0x02 -> 0x04 |
1 | ('ead47cb','2024-02-18 13:19:26.69','Permanent') |
Final Ans
1 | [ |
Question-6
- Restore all encrypted files, decrypt them, and provide the md5sum of each decrypted file after removing any extra bytes before computing the hash.
Format :[[‘hash1’], [‘hash2’],..]
An Overview on time_update.exe
Enumerate each directory and rename all files.
Generate a random time and convert it to
SystemToFileTime
. Generatekey1
and append a 4-byte nonce.Compute the MD5 hash of
key1
and set it askey2
.Rename each file with a random time and encrypt it.
- Encryption :
enc[i] = file[i] ^ key1[i] ^ key2[i]
.
- Encryption :
Enumerate all files again and update their names with the
.tort
extension.Before exiting, restore the system time to its initial state.
File-Decryption
The
time_update.exe
uses two keyskey1
and hash ofkey1
as key2 to encrypt the files.To locate
key1
, we need to find the timestamp of the second rename. However,key1
includes an additional 4-byte nonce, the value of which is unknown. Therefore, brute force is required to determine the correctkey1
.By cross-referencing the encrypted files in the logfile, we can identify the original filenames and then brute force the files using their headers to determine the nonce.
Some files only have small byte file signatures, resulting in numerous possible nonces. We can validate these nonces by examining the end-of-file signatures and chunks present in the file.
Because it updates the time before encrypting the files, it leaves a trace in the log files that could potentially be used to recover key.
Opcode for Renamed File :
0x02 → 0x05 → 0x01 → 0x04 → 0x04
Let’s manually search for the required information. We’ll begin by assigning filenames with the extension
tort
and then conduct a search within the log file.Specifically, we’ll focus on identifying the first operation code (opcode) located at offset
0xb0
.After locating it, we’ll parse the data and examine the previous MLog to find the corresponding filenames.
First Rename : 15005-39026.pdf -> bf2f63b3
Search for the bf2f63b3
.
Second Rename : bf2f6b3 -> 0cf51fbc
The system time is expressed in Coordinated Universal Time (UTC).
Rename Time(Encryption Key) : 2010-02-26 12:38:43.0000000
1 | Enc Key Time : 2010 2 2 26 12 38 43 0 |
Third Rename : 0cf51fbc -> 0cf51fbc.tort
Similarly, locate the timestamps used for encryption on all remaining files, and then proceed to extract those files accordingly.
binary-01.gif -> c7982ef6 -> a917438f -> a917438f.tort
: 1995 2 2 27 2 11 42 0
Everest Vista.webp -> d406327c -> 3a7fab71 -> 3a7fab71.tort
: 1990 8 2 28 21 6 35 0
Paranormal Phenomenon.docx -> 830c92a3 -> bb292337 -> bb292337.tort
: 1993 7 2 16 17 10 46 0
so-cappy.jpg -> 141e0f79 -> 24819686 -> 24819686.tort
: 2001 4 2 19 8 27 45 0
stuffs.rar -> f15ebcd2 -> 7a6c7166 -> 7a6c7166.tort
: 2009 11 2 22 17 5 55 0
ySq12b0T.mp4 -> 86c66c9c -> 185c65f8 -> 185c65f8.tort
: 2007 7 2 28 3 27 33 0
vl36hkjkzbh91.png -> 313feb6e -> cc876a3b -> cc876a3b.tort
: 1985 3 2 4 16 23 53 0
Carving Out Files
0cf51fbc.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x10e94000)/4096)) count=$(((0x16c000)/4096)) bs=4096 of=0cf51fbc_1.tort |
a917438f.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x10025000)/64)) count=$(((0x89ce40)/64)) bs=64 of=a917438f.tort |
Remove the extra bytes from the extracted files present due to the block size used during extraction.
7a6c7166.tort
Go to offset 0x108c2000
, 0x10c05000
& read 830 * 0x1000
,130 * 0x1000
bytes respectively
1
2
3
dd if=ReAL_File_System.001 skip=$(((0x108c2000)/8192)) count=$(((830 * 0x1000)/8192)) bs=8192 of=7a6c7166_1.tort
dd if=ReAL_File_System.001 skip=$(((0x10c05000)/4096)) count=$(((130 * 0x1000)/4096)) bs=4096 of=7a6c7166_2.tort
cat 7a6c7166_1.tort 7a6c7166_2.tort > 7a6c7166.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x108c2000)/8192)) count=$(((830 * 0x1000)/8192)) bs=8192 of=7a6c7166_1.tort |
185c65f8.tort
1 | dd if=ReAL_File_System.001 skip=$(((68743 * 0x1000)/4096)) count=$(((525 * 0x1000)/4096)) bs=4096 of=185c65f8.tort |
24819686.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x12800000)/4096)) count=$(((0x10000)/4096)) bs=4096 of=24819686.tort |
3a7fab71.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x10000000)/4096)) count=$(((0x25000)/4096)) bs=4096 of=3a7fab71.tort |
bb292337.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x13a00000)/4096)) count=$(((0x8000)/4096)) bs=4096 of=bb292337.tort |
cc876a3b.tort
1 | dd if=ReAL_File_System.001 skip=$(((0x1000 * 72705))) count=$((0x175f)) bs=1 of=cc876a3b.tort |
We can write a script to remove the extra bytes from the extracted files.
1 | import struct |
1 | typedef struct _SYSTEMTIME { |
Read about SystemTime
1 | BOOL SystemTimeToFileTime( |
Read about SystemTimetoFileSystemTime
We need to change SystemTime to SystemTimeToFileTime. To do so, write a cpp code & compile it.
1 |
|
- Generate all possible 4 bytes nonces wordlist.
1 | crunch 4 4 "0123456789abcdef\!#&*%GHIJ-lm+_" > passwd.txt |
File Decryption Script
1 | import os |
Let’s calculate md5sum of each file
1 | da8ed3e98eb5a2ba769ea60b48b0f6eb 15005-39026.pdf |
Final Ans
1 | [ |
Flag : bi0sctf{ReAL_1_w0nd3r_wHa7_t1m3_is_17_14dbc653fdb414c1d}
References
Contact: Sabhya Raj Mehta(5h4rrK) | Twitter | GitHub | LinkedIn