Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 292 additions & 0 deletions content/exchange/artifacts/Windows.Vhdx.RemapConfigBuilder.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
name: Windows.Vhdx.RemapConfigBuilder
description: |
Create remapping configuration YAML file(s) to virtually mount
VHDX profiles into virtual Velociraptor clients.

This artifact is part of the Vhdx Suite. This suite requires to have
the Velociraptor server artifact `Windows.Sys.Users` override on the
server to work properly.

Read the dedicated blog post before using this artifact.

author: Yann Malherbe - @mirwitch

reference:
- https://labs.infoguard.ch/posts/automation_of_vhdx_investigations/

type: CLIENT

implied_permissions:
- FILESYSTEM_WRITE

parameters:
- name: vhdxFolderPath
type: string
default: "F:\\vhdx\\"
description: "Directory containing VHDX profile files."

- name: usernameExtractor
type: regex
default: "profile_(?<Username>.*).vhdx"
description: "Regex used to extract usernames from filenames."

- name: user
type: regex
default: Administrator
description: "Optional filter to restrict which profiles to process."

- name: virtualHostname
type: string
default: "VHDX"
description: "Hostname to assign to the virtual clients."

- name: vhdxOffset
type: hidden
default: 1048576
description: "NTFS start offset inside VHDX."

- name: batchSize
type: int
default: 30
description: "Number of profiles per virtual client."

sources:
- precondition:
SELECT OS From info() where OS = 'windows'

query: |
// Static remapping header
LET RemappingHeader ='''
remappings:
- type: permissions
permissions:
- COLLECT_CLIENT
- FILESYSTEM_READ
- FILESYSTEM_WRITE
- READ_RESULTS
- MACHINE_STATE
- SERVER_ADMIN
- type: impersonation
os: windows
hostname: "<VIRTUAL_HOSTNAME>"
env:
- key: SystemRoot
value: C:\Windows
- key: WinDir
value: C:\Windows
disabled_functions:
- amsi
- lookupSID
- token
disabled_plugins:
- users
- certificates
- handles
- pslist
- interfaces
- modules
- netstat
- partitions
- proc_dump
- proc_yara
- vad
- winobj
- wmi

- type: shadow
from:
accessor: data
"on":
accessor: data

- type: shadow
from:
accessor: raw_reg
"on":
accessor: raw_reg

- type: shadow
from:
accessor: zip
"on":
accessor: zip
'''

// Remapping required per user
LET RemappingUser = '''
- type: mount
description: 'NTFS - <USERNAME>'
from:
accessor: raw_ntfs
prefix: |
{
"DelegateAccessor": "offset",
"Delegate": {
"DelegateAccessor": "vhdx",
"DelegatePath": "<VHDX_PATH>",
"Path":"/<VHDX_OFFSET>"
},
"Path": "<PROFILE_PATH>"
}
"on":
accessor: ntfs
prefix: '\\.\C:\Users\<USERNAME>'
path_type: ntfs

- type: mount
description: 'File - <USERNAME>'
from:
accessor: raw_ntfs
prefix: |
{
"DelegateAccessor": "offset",
"Delegate": {
"DelegateAccessor": "vhdx",
"DelegatePath": "<VHDX_PATH>",
"Path":"/<VHDX_OFFSET>"
},
"Path": "<PROFILE_PATH>"
}
"on":
accessor: file
prefix: 'C:\Users\<USERNAME>'
path_type: windows

- type: mount
description: 'Auto - <USERNAME>'
from:
accessor: raw_ntfs
prefix: |
{
"DelegateAccessor": "offset",
"Delegate": {
"DelegateAccessor": "vhdx",
"DelegatePath": "<VHDX_PATH>",
"Path":"/<VHDX_OFFSET>"
},
"Path": "<PROFILE_PATH>"
}
"on":
accessor: auto
prefix: 'C:\Users\<USERNAME>'
path_type: windows

- type: mount
description: 'Registry - <USERNAME> NTUSER.DAT'
from:
accessor: raw_reg
prefix: |-
{
"Path": "/",
"DelegateAccessor": "raw_ntfs",
"Delegate": {
"DelegateAccessor":"offset",
"Delegate": {
"DelegateAccessor": "vhdx",
"DelegatePath": "<VHDX_PATH>",
"Path": "/<VHDX_OFFSET>"
},
"Path":"<PROFILE_PATH>/NTUSER.DAT"
}
}
path_type: registry
"on":
accessor: registry
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are actually alot of registry paths to mount - like user class, sam etc. You are better off to use the supported remapping builder artifact for completeness.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For user profiles, only NTUser.dat and UsrClass.dat are available. I added UsrClass with de0f4a3

prefix: HKEY_USERS\<USERNAME>
path_type: registry

- type: mount
description: 'Registry - <USERNAME> UsrClass'
from:
accessor: raw_reg
prefix: |-
{
"Path": "/",
"DelegateAccessor": "raw_ntfs",
"Delegate": {
"DelegateAccessor":"offset",
"Delegate": {
"DelegateAccessor": "vhdx",
"DelegatePath": "<VHDX_PATH>",
"Path": "/<VHDX_OFFSET>"
},
"Path":"<PROFILE_PATH>/AppData/Local/Microsoft/Windows/UsrClass.dat"
}
}
path_type: registry
"on":
accessor: registry
prefix: HKEY_USERS\<USERNAME>_Classes
path_type: registry
'''

// Get the list of user profiles of interest
LET userProfilesList = SELECT *,
parse_string_with_regex(
string=Name,
regex=usernameExtractor
) AS userProfile
FROM glob(globs=vhdxFolderPath+"/*.vhdx")
WHERE userProfile.Username AND userProfile.Username =~ user


// Split the user list into batches
LET userBatch = SELECT rows AS user_batch
FROM batch(query={
SELECT *
FROM userProfilesList
}, batch_size=batchSize)


// Create the header of the remapping file
LET HeaderRemapping = regex_replace(re="<VIRTUAL_HOSTNAME>",
replace=virtualHostname + "_" + firstBatchUser,
source=RemappingHeader)

// Create the user sections of the remapping file
LET UserRemapping = SELECT
regex_replace(re="<USERNAME>",
replace=userProfile.Username, source=
regex_replace(re="<PROFILE_PATH>",
replace="/Profile", source=
regex_replace(re="<VHDX_OFFSET>",
replace=vhdxOffset, source=
regex_replace(re="<VHDX_PATH>",
replace=regex_replace(
re="\\\\", replace="/", source=OSPath),
source=RemappingUser)
))) AS Content
FROM scope()

// Iterate on the users part of the bulk
LET BulkUserRemapping = SELECT * FROM foreach(
row=user_batch,
query=UserRemapping
)

// Write the remapping content in a temp file
LET tmpFile = tempfile(
data=HeaderRemapping + BulkUserRemapping.Content,
remove_last=TRUE
)

// Retrieve the directory from the running Velociraptor executable
LET veloInfo = SELECT Exe FROM info()

// Get the first user of the batch
LET firstBatchUser = user_batch[0].userProfile.Username

// Get the remapping filename
LET remappingFilename = firstBatchUser + ".yaml"

// Copy the file to the remapping YAML file
LET copyFile = SELECT copy(
filename=tmpFile,
dest=pathspec(parse=veloInfo[0].Exe).Dirname + "Vhdx" + "Remapping" + remappingFilename,
create_directories=TRUE) AS CreatedConfig
FROM scope()



// Create dedicated YAML file for each batch of users
SELECT * FROM foreach(row=userBatch, query=copyFile)
79 changes: 79 additions & 0 deletions content/exchange/artifacts/Windows.Vhdx.Sys.Users.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Windows.Vhdx.Sys.Users
description: |
List User accounts by inspecting registry keys. This method is a
reliable indicator for users who have physically logged into the
system and thereby created local profiles.

This will not include domain users or the output from `NetUserEnum`
- you should collect the `Windows.Sys.AllUsers` artifact to get all
possible users on the system.

This artifact should be rename to replace the official
`Windows.Sys.Users` allowing to list the users for potential user
profiles from virtual Velociraptor clients.

Read the dedicated blog post before using this artifact.

author: Yann Malherbe - @mirwitch

reference:
- https://labs.infoguard.ch/posts/automation_of_vhdx_investigations/

parameters:
- name: remoteRegKey
default: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*
description: "Location of the registry key holding the profile list."

- name: labelName
default: "remapped_profile"
type: string
description: "Client label that signals the use of VHDX-based enumeration."

imports:
- Windows.Sys.AllUsers

sources:
- precondition:
SELECT OS From info() where OS = 'windows'

query: |

LET GetTimestamp(High, Low) = if(condition=High,
then=timestamp(winfiletime=High * 4294967296 + Low))

// lookupSID() may not be available on deaddisk analysis
LET Standard = SELECT split(string=Key.OSPath.Basename, sep="-")[-1] as Uid,
"" AS Gid,
LookupSIDCache(SID=Key.OSPath.Basename || "") AS Name,
Key.OSPath as Description,
ProfileImagePath as Directory,
Key.OSPath.Basename as UUID,
Key.Mtime as Mtime,
{
SELECT Mtime
FROM stat(filename=expand(path=ProfileImagePath))
} AS HomedirMtime,
dict(ProfileLoadTime=GetTimestamp(
High=LocalProfileLoadTimeHigh, Low=LocalProfileLoadTimeLow),
ProfileUnloadTime=GetTimestamp(
High=LocalProfileUnloadTimeHigh, Low=LocalProfileUnloadTimeLow)
) AS Data
FROM read_reg_key(globs=remoteRegKey, accessor="registry")

// User list for VHDX profiles emulating the Standard one
LET UserProfile = SELECT
OSPath.Components[-2] AS Uid,
OSPath.Dirname AS Directory,
OSPath.Components[-2] AS UUID,
OSPath.Components[-2] AS Name,
"" AS Gid,
Mtime,
Mtime AS HomedirMtime,
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\" + OSPath.Components[-2] AS Description
FROM glob(globs="C:/Users/*/NTUSER.DAT")

// Get the labels of the agent
LET agent_config = SELECT labels FROM config

// Take the appropriate user list method
SELECT * FROM if(condition=agent_config.labels=~labelName, then=UserProfile, else=Standard)
Loading