// HackTheBox  ·  writeup

BFT

HackTheBox Very Easy MFTECmdPythonddstrings
DFIR

Scenario

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.

Executive Summary

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.

Artifacts

Timestamp note: MFTECmd prints $STANDARD_INFORMATION and $FILE_NAME times side by side. $Created0x10 is the former and $Created0x30 is the latter. Q4 asks for $Created0x30, so read the right column and truncate to whole seconds.

Initial Access

Q1 — Simon Stark was targeted by attackers on February 13. He downloaded a ZIP file from a link received in an email. What was the name of the ZIP file he downloaded from the link?

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']]"
Click flag to reveal Stage-20240213T093324Z-001.zip
Q2 — Examine the Zone Identifier contents for the initially downloaded ZIP file. This field reveals the HostUrl from where the file was downloaded, serving as a valuable Indicator of Compromise (IOC) in our investigation/analysis. What is the full Host URL from where this ZIP file was downloaded?

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 ?authuser with no value. That is how it is stored in the MFT, not a copy error, and the full string is what is accepted.

Click to reveal answer https://storage.googleapis.com/drive-bulk-export-anonymous/20240213T093324.039Z/4133399871716478688/a40aecd0-1cf3-4f88-b55a-e188d5c1c04f/1/c277a8b4-afa9-4d34-b8ca-e1eb5e5f983c?authuser

Execution

Q3 — What is the full path and name of the malicious file that executed malicious code and connected to a C2 server?

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')]"
Click flag to reveal C:\Users\simon.stark\Downloads\Stage-20240213T093324Z-001\Stage\invoice\invoices\invoice.bat
Q4 — Analyze the $Created0x30 timestamp for the previously identified file. When was this file created on disk?

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']"
Click to reveal answer 2024-02-13 16:38:39
Q5 — Finding the hex offset of an MFT record is beneficial in many investigative scenarios. Find the hex offset of the stager file from Question 3.

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 0x prefix.

Click flag to reveal 16E3000

Command and Control

Q6 — Each MFT record is 1024 bytes in size. If a file on disk has smaller size than 1024 bytes, they can be stored directly on MFT File itself. These are called MFT Resident files. During Windows File system Investigation, its crucial to look for any malicious/suspicious files that may be resident in MFT. This way we can find contents of malicious files/scripts. Find the contents of The malicious stager identified in Question3 and answer with the C2 IP and port.

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 strings shows 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.

Click to reveal answer 43.204.110.203:6666

Attack Summary

PhaseAction
Initial AccessPhishing email on February 13 with a link to a Google Drive bulk export
Initial AccessZIP downloaded to the user’s Downloads folder, tagged with a Zone.Identifier stream
ExecutionArchive extracted; invoice.bat lands in Stage\invoice\invoices\
Command and ControlBatch file runs a PowerShell download cradle toward 43.204.110[.]203:6666

IOCs

TypeValue
File (archive)Stage-20240213T093324Z-001.zip
URLhxxps[://]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
C243.204.110[.]203:6666

MITRE ATT&CK

TechniqueIDDescription
Phishing: Spearphishing LinkT1566.002Emailed link to a Google Drive bulk export ZIP
User Execution: Malicious FileT1204.002invoice.bat extracted from the downloaded archive
Command and Scripting Interpreter: Windows Command ShellT1059.003Batch file stager
Command and Scripting Interpreter: PowerShellT1059.001PowerShell download cradle inside the batch file
Ingress Tool TransferT1105Cradle reaching out to 43.204.110[.]203:6666

Defender Takeaways

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.

Conclusion

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.