In this Sherlock, you will become acquainted with MFT (Master File Table) forensics. You will be introduced to well-known tools and methodologies for analyzing MFT artifacts to identify malicious activity. During our analysis, you will utilize the MFTECmd tool to parse the provided MFT file, TimeLine Explorer to open and analyze the results from the parsed MFT, and a Hex editor to recover file contents from the MFT.
A user downloaded a ZIP archive from a phishing link, and the file system recorded where it came from: a Google Drive bulk export URL held in the archive’s Zone.Identifier stream. The archive was extracted into the user’s Downloads folder and a batch file named like an invoice sat inside it. The batch file was small enough to live inside its own MFT record, so its contents survived in the $MFT itself. Carving that record showed a PowerShell download cradle pointing at a bare IP on a non-standard port. The whole case was solved from file system metadata alone, with no logs and no memory.
$MFT from the user’s machine → parse with MFTECmd, read the CSV in Timeline Explorer or Python. Carve raw records with dd or a hex editorTimestamp note: MFTECmd prints
$STANDARD_INFORMATIONand$FILE_NAMEtimes side by side.$Created0x10is the former and$Created0x30is the latter. Q4 asks for$Created0x30, so read the right column and truncate to whole seconds.
Parse the $MFT to CSV, then filter for ZIP files under the user’s Downloads folder. One archive stands out (MFT entry 75191), and it is the only one carrying a Zone.Identifier alternate data stream, which means it came from the internet. The timestamp embedded in the name matches the date in the scenario.
MFTECmd -f '$MFT' --csv work
python3 -I -c "import csv,sys; [print(r['EntryNumber'],r['ParentPath'],r['FileName'],r['ZoneIdContents'][:60]) for r in csv.DictReader(open('work/mft.csv',encoding='utf-8-sig')) if r['FileName'].lower().endswith('.zip') and 'Downloads' in r['ParentPath']]"
From Q1, entry 75191 is the pivot. MFTECmd exposes the stream in the ZoneIdContents column, and the HostUrl= line points at a storage.googleapis.com/drive-bulk-export-anonymous/ path, which matches the Drive bulk export naming of the ZIP. Copy the whole value.
python3 -I -c "import csv; [print(r['ZoneIdContents']) for r in csv.DictReader(open('work/mft.csv',encoding='utf-8-sig')) if r['EntryNumber']=='75191']"
Gotcha: The URL ends in a bare
?authuserwith no value. That is how it is stored in the MFT, not a copy error, and the full string is what is accepted.
The archive was extracted into Downloads\Stage-20240213T093324Z-001\Stage\. Listing the files under that folder, invoice\invoices\ holds a batch file named like an invoice (entry 23436). A .bat delivered inside a phished ZIP is the obvious stager, and Q6 confirms it from its contents. Rebuild the full path from ParentPath plus FileName.
python3 -I -c "import csv; [print(r['EntryNumber'],r['ParentPath'],r['FileName']) for r in csv.DictReader(open('work/mft.csv',encoding='utf-8-sig')) if 'Stage-20240213T093324Z-001' in r['ParentPath'] and r['FileName'].lower().endswith('.bat')]"
Read the Created0x30 column for entry 23436. That is the $FILE_NAME attribute time, which is harder to timestomp than the $STANDARD_INFORMATION pair in Created0x10. The value is 16:38:39.9341326 on 13 February, so drop the fractional part for the answer.
python3 -I -c "import csv; [print(r['Created0x10'],r['Created0x30']) for r in csv.DictReader(open('work/mft.csv',encoding='utf-8-sig')) if r['EntryNumber']=='23436']"
Every MFT record is 1024 bytes, so a record sits at its entry number times 1024. Entry 23436 from Q3 gives the offset, printed in hex without a prefix.
python3 -I -c "print(format(23436*1024,'X'))"
Tip: The format hint looks like a mixed-case pattern. Give the plain uppercase hex digits with no
0xprefix.
The script is small enough to be resident, but MFTECmd left its resident data columns empty for this file, so go to the raw $MFT. Carve the 1024-byte record at the Q5 offset with dd and run strings over it. The content is a PowerShell download cradle that calls out to a bare IP on port 6666. This was read statically from the record and nothing was executed.
dd if='$MFT' bs=1024 skip=23436 count=1 2>/dev/null | strings -e l
Tip: If
stringsshows nothing useful with the default settings, try both 8-bit and 16-bit little-endian modes (-e l), since the content may be stored either way.
| Phase | Action |
|---|---|
| Initial Access | Phishing email on February 13 with a link to a Google Drive bulk export |
| Initial Access | ZIP downloaded to the user’s Downloads folder, tagged with a Zone.Identifier stream |
| Execution | Archive extracted; invoice.bat lands in Stage\invoice\invoices\ |
| Command and Control | Batch file runs a PowerShell download cradle toward 43.204.110[.]203:6666 |
| Type | Value |
|---|---|
| File (archive) | Stage-20240213T093324Z-001.zip |
| URL | hxxps[://]storage[.]googleapis[.]com/drive-bulk-export-anonymous/20240213T093324.039Z/4133399871716478688/a40aecd0-1cf3-4f88-b55a-e188d5c1c04f/1/c277a8b4-afa9-4d34-b8ca-e1eb5e5f983c?authuser |
| File (stager) | C:\Users\simon.stark\Downloads\Stage-20240213T093324Z-001\Stage\invoice\invoices\invoice.bat |
| C2 | 43.204.110[.]203:6666 |
| Technique | ID | Description |
|---|---|---|
| Phishing: Spearphishing Link | T1566.002 | Emailed link to a Google Drive bulk export ZIP |
| User Execution: Malicious File | T1204.002 | invoice.bat extracted from the downloaded archive |
| Command and Scripting Interpreter: Windows Command Shell | T1059.003 | Batch file stager |
| Command and Scripting Interpreter: PowerShell | T1059.001 | PowerShell download cradle inside the batch file |
| Ingress Tool Transfer | T1105 | Cradle reaching out to 43.204.110[.]203:6666 |
Read Zone.Identifier streams. The HostUrl is a ready-made IOC for where a file came from, and it survives in the MFT even when the file is out of sight. Hunt it for cloud storage download URLs on phishing-related files.
Small files live inside the MFT. Scripts under roughly 1 KB can be recovered from the record itself, even when the parser leaves the resident columns empty. Carve the record by offset when that happens.
Flag batch files appearing under extracted archive folders in Downloads. A Drive-hosted ZIP that unpacks a .bat named like an invoice is a cheap, high-signal rule to write.
Alert on script interpreters connecting to bare IPs on odd ports. Port 6666 from a PowerShell cradle stands out in any egress baseline.
Prefer $FILE_NAME timestamps when timestomping is possible. The $Created0x30 value is the harder one to tamper with, so use it to anchor a timeline.
One MFT was enough to recover the whole delivery chain: the phishing download, the stager, and the C2 address inside it. The file system recorded where the file came from, what it contained, and when it landed, and nothing needed to be executed to learn it.