Forked from HarmJ0y/ConvertFrom-UserParameter.ps1
Last active
October 8, 2021 03:25
-
-
Save itc-lab/3749188ce58ad803a97ae3f1f924c435 to your computer and use it in GitHub Desktop.
ConvertFrom-UserParameter.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function ConvertFrom-UserParameter { | |
<# | |
.SYNOPSIS | |
Converts a userparameters encoded blob into an ordered dictionary of decoded values. | |
Author: Will Schroeder (@harmj0y) | |
License: BSD 3-Clause | |
Required Dependencies: None | |
.DESCRIPTION | |
This function will take a userparameters blob from an active directory user | |
and decodes the arbitrary format into human readable values. | |
Heavily based on Cybericom's PoC code at https://social.technet.microsoft.com/Forums/scriptcenter/en-US/953cd9b5-8d6f-4823-be6b-ebc009cc1ed9/powershell-script-to-modify-the-activedirectory-userparameters-attribute-to-set-terminal-services?forum=ITCG | |
.PARAMETER Value | |
Specifies the string blob to decode. | |
.PARAMETER ShowAll | |
Switch. Signals ConvertFrom-UserParameter to display all values, including null/blank values. | |
.EXAMPLE | |
Get-NetUser testuser | ConvertFrom-UserParameter | |
Name Value | |
---- ----- | |
CtxCfgPresent 1428032432 | |
CtxMinEncryptionLevel 1 | |
CtxWFProfilePath \\primary\blah.profile | |
CtxWFProfilePathW \\primary\blah.profile | |
CtxShadow EnableInputNoNotify | |
CtxMaxDisconnectionTime 0 | |
CtxMaxConnectionTime 0 | |
CtxMaxIdleTime 0 | |
CtxCfgFlags1 {INHERITMAXDISCONNECTIONTIME, DISABLECCM, INH... | |
CtxInitialProgram \\server\evil.exe | |
CtxInitialProgramW \\server\evil.exe | |
.EXAMPLE | |
Get-NetUser testuser | ConvertFrom-UserParameter -ShowAll | |
Name Value | |
---- ----- | |
CtxCfgPresent 1428032432 | |
CtxMinEncryptionLevel 1 | |
CtxWFProfilePath \\primary\blah.profile | |
CtxWFProfilePathW \\primary\blah.profile | |
CtxWFHomeDir | |
CtxWFHomeDirW | |
CtxWFHomeDirDrive | |
CtxWFHomeDirDriveW | |
CtxShadow EnableInputNoNotify | |
CtxMaxDisconnectionTime 0 | |
CtxMaxConnectionTime 0 | |
CtxMaxIdleTime 0 | |
CtxWorkDirectory | |
CtxWorkDirectoryW | |
CtxCfgFlags1 {INHERITMAXDISCONNECTIONTIME, DISABLECCM, INH... | |
CtxInitialProgram \\server\evil.exe | |
CtxInitialProgramW \\server\evil.exe | |
.INPUTS | |
String | |
Accepts an string representing a user userparameters binary blob. | |
.OUTPUTS | |
System.Collections.Specialized.OrderedDictionary | |
An ordered dictionary with the converted user userparameter fields. | |
.LINK | |
https://social.technet.microsoft.com/Forums/scriptcenter/en-US/953cd9b5-8d6f-4823-be6b-ebc009cc1ed9/powershell-script-to-modify-the-activedirectory-userparameters-attribute-to-set-terminal-services?forum=ITCG | |
https://msdn.microsoft.com/en-us/library/ff635169.aspx | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] | |
[Alias('userparameters')] | |
[String] | |
$Value, | |
[Switch] | |
$ShowAll | |
) | |
BEGIN { | |
# values from https://msdn.microsoft.com/en-us/library/ff635169.aspx | |
$CtxCfgFlagsBitValues = @{ | |
'INHERITINITIALPROGRAM' = 0x10000000 | |
'INHERITCALLBACK' = 0x08000000 | |
'INHERITCALLBACKNUMBER' = 0x04000000 | |
'INHERITSHADOW' = 0x02000000 | |
'INHERITMAXSESSIONTIME' = 0x01000000 | |
'INHERITMAXDISCONNECTIONTIME' = 0x00800000 | |
'INHERITMAXIDLETIME' = 0x00400000 | |
'INHERITAUTOCLIENT' = 0x00200000 | |
'INHERITSECURITY' = 0x00100000 | |
'PROMPTFORPASSWORD' = 0x00080000 | |
'RESETBROKEN' = 0x00040000 | |
'RECONNECTSAME' = 0x00020000 | |
'LOGONDISABLED' = 0x00010000 | |
'AUTOCLIENTDRIVES' = 0x00008000 | |
'AUTOCLIENTLPTS' = 0x00004000 | |
'FORCECLIENTLPTDEF' = 0x00002000 | |
'DISABLEENCRYPTION' = 0x00001000 | |
'HOMEDIRECTORYMAPROOT' = 0x00000800 | |
'USEDEFAULTGINA' = 0x00000400 | |
'DISABLECPM' = 0x00000200 | |
'DISABLECDM' = 0x00000100 | |
'DISABLECCM' = 0x00000080 | |
'DISABLELPT' = 0x00000040 | |
'DISABLECLIP' = 0x00000020 | |
'DISABLEEXE' = 0x00000010 | |
'WALLPAPERDISABLED' = 0x00000008 | |
'DISABLECAM' = 0x00000004 | |
} | |
} | |
PROCESS { | |
$UserParameterBytes = [System.Text.Encoding]::UNICODE.GetBytes($Value) | |
$MemoryStream = New-Object -TypeName System.IO.MemoryStream -ArgumentList @(,$UserParameterBytes) | |
$BinaryReader = New-Object -TypeName System.IO.BinaryReader -ArgumentList $MemoryStream, ([Text.Encoding]::Unicode) | |
$ResultValues = New-Object System.Collections.Specialized.OrderedDictionary | |
# [0-95] -> reserved | |
$Null = $BinaryReader.ReadBytes(96) | |
# [96-97] -> signature: @(80,0) (UNICODE 'P') | |
$Signature = $BinaryReader.ReadUInt16() | |
if ($Signature -eq 80) { | |
Write-Verbose 'Signature match' | |
# [97-98] -> number of attributes in blob | |
$NumAttributes = $BinaryReader.ReadUInt16() | |
Write-Verbose "Number of attributes found in blob: $NumAttributes" | |
1..$NumAttributes | % { | |
# 2 bytes for the name length | |
$NameLength = $BinaryReader.ReadUInt16() | |
Write-Verbose "NameLength: $NameLength" | |
# 2 bytes for the value length | |
$ValueLength = $BinaryReader.ReadUInt16() | |
Write-Verbose "ValueLength: $ValueLength" | |
# 2 bytes for the type | |
$Type = $BinaryReader.ReadUInt16() | |
$AttributeName = [System.Text.Encoding]::UNICODE.GetString($BinaryReader.ReadBytes($NameLength)) | |
Write-Verbose "AttributeName: $AttributeName" | |
if ($AttributeName -match 'CtxCfgPresent|CtxCfgFlags1|CtxCallBack|CtxKeyboardLayout|CtxMinEncryptionLevel|CtxNWLogonServer|CtxMaxConnectionTime|CtxMaxDisconnectionTime|CtxMaxIdleTime|CtxShadow|CtxMinEncryptionLevel') { | |
$AttributeData = "" | |
For ($i = 0; $i -lt $ValueLength; $i += 2) { | |
$AttributeData = [System.Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes(2)) + $AttributeData | |
} | |
$AttributeValue = [Convert]::ToInt32($AttributeData, 16) | |
if ($AttributeName -match 'CtxShadow') { | |
$AttributeValue = Switch ($AttributeValue) { | |
0 { 'Disable' } | |
1 { 'EnableInputNotify' } | |
2 { 'EnableInputNoNotify' } | |
3 { 'EnableNoInputNotify' } | |
4 { 'EnableNoInputNoNotify' } | |
default { $AttributeValue } | |
} | |
} | |
elseif ($AttributeName -match 'CtxCfgFlags1') { | |
# this field is represented as a bitmask | |
$CtxCfgFlags1 = New-Object System.Collections.ArrayList | |
$CtxCfgFlagsBitValues.GetEnumerator() | ForEach-Object { | |
if (($AttributeValue -band $_.Value) -eq $_.Value) { | |
$Null = $CtxCfgFlags1.Add($_.Name) | |
} | |
} | |
$AttributeValue = $CtxCfgFlags1.ToArray() | |
} | |
$ResultValues.Add($AttributeName, $AttributeValue) | |
} | |
elseif ($AttributeName -match 'CtxWFHomeDirDrive|CtxWFHomeDir|CtxWFHomeDrive|CtxInitialProgram|CtxWFProfilePath|CtxWorkDirectory|CtxCallbackNumber') { | |
$AttributeData = $BinaryReader.ReadBytes($ValueLength) | |
$AttributeValue = '' | |
For ($i = 0; $i -lt $ValueLength; $i += 2) { | |
$ValueChar = [Char][Byte]([Convert]::ToInt16([Char][byte]$AttributeData[$i] + [char][byte]$AttributeData[$i+1], 16)) | |
$AttributeValue += $ValueChar | |
} | |
if ($AttributeName -match ',*w$') { | |
# handle wide strings | |
$AttributeValue = [System.Text.Encoding]::UNICODE.GetString([System.Text.Encoding]::ASCII.GetBytes($AttributeValue)) | |
} | |
if($ShowAll -or ($AttributeValue.Length -ne 1)) { | |
$ResultValues.Add($AttributeName, $AttributeValue) | |
} | |
} | |
else { | |
Write-Verbose "Unrecognized AttributeName: $AttributeName" | |
} | |
} | |
} | |
$BinaryReader.Close() | |
$MemoryStream.Close() | |
$ResultValues | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment