Created
December 5, 2017 17:23
-
-
Save kappa7194/30473a6261e34a518bf5433702bde041 to your computer and use it in GitHub Desktop.
An example on how to decode Active Directory's User-Parameters attribute
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
namespace UserParametersParser | |
{ | |
using System; | |
using System.DirectoryServices.Protocols; | |
using System.IO; | |
using System.Net; | |
using System.Text; | |
public static class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
LdapDirectoryIdentifier ldapDirectoryIdentifier = new LdapDirectoryIdentifier(SERVER_NAME, 389, true, false); | |
NetworkCredential networkCredential = new NetworkCredential(USER_NAME, USER_PASSWORD, USER_DOMAIN); | |
LdapConnection ldapConnection = new LdapConnection(ldapDirectoryIdentifier, networkCredential, AuthType.Kerberos); | |
SearchRequest searchRequest = new SearchRequest(SEARCH_ROOT, SEARCH_FILTER, SearchScope.Subtree, "userParameters"); | |
SearchResponse searchResponse = (SearchResponse) ldapConnection.SendRequest(searchRequest); | |
foreach (SearchResultEntry searchResultEntry in searchResponse.Entries) | |
{ | |
// REMEMBER | |
// This WILL throw an exception if you read an object without the attribute. | |
// Do check if the attribute exists before accessing it. | |
var directoryAttribute = searchResultEntry.Attributes["userParameters"]; | |
var userParametersAttribute = (string) directoryAttribute[0]; | |
UserParameters userParameters = GetUserParameters(userParametersAttribute); | |
} | |
} | |
private static UserParameters GetUserParameters(string attributeValue) | |
{ | |
UserParameters userParameters = new UserParameters(); | |
byte[] bytes = Encoding.Unicode.GetBytes(attributeValue); | |
using (MemoryStream memoryStream = new MemoryStream(bytes)) | |
{ | |
using (BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.Unicode, true)) | |
{ | |
byte[] reservedData = binaryReader.ReadBytes(96); | |
byte[] signature = binaryReader.ReadBytes(2); | |
byte[] tsPropertyCount = binaryReader.ReadBytes(2); | |
string signatureValue = Encoding.Unicode.GetString(signature); | |
ushort tsPropertyCountValue = BitConverter.ToUInt16(tsPropertyCount, 0); | |
for (int i = 0; i < tsPropertyCountValue; i++) | |
{ | |
byte[] nameLength = binaryReader.ReadBytes(2); | |
byte[] valueLength = binaryReader.ReadBytes(2); | |
byte[] type = binaryReader.ReadBytes(2); | |
ushort nameLengthValue = BitConverter.ToUInt16(nameLength, 0); | |
ushort valueLengthValue = BitConverter.ToUInt16(valueLength, 0); | |
ushort typeValue = BitConverter.ToUInt16(type, 0); | |
byte[] propName = binaryReader.ReadBytes(nameLengthValue); | |
byte[] propValue = binaryReader.ReadBytes(valueLengthValue); | |
string propNameValue = Encoding.Unicode.GetString(propName); | |
byte[] propValueValue = GetPropValueValue(propValue); | |
if (string.Equals(propNameValue, nameof(UserParameters.CtxCfgPresent), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxCfgPresent = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxCfgFlags1), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxCfgFlags1 = (CtxCfgFlags1) BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxCallBack), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxCallBack = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxKeyboardLayout), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxKeyboardLayout = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxNwLogonServer), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxNwLogonServer = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxMaxConnectionTime), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxMaxConnectionTime = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxMaxDisconnectionTime), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxMaxDisconnectionTime = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxMaxIdleTime), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxMaxIdleTime = BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxShadow), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxShadow = (CtxShadow) BitConverter.ToUInt32(propValueValue, 0); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxMinEncryptionLevel), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxMinEncryptionLevel = propValueValue[0]; | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxWfHomeDir), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxWfHomeDir = Encoding.ASCII.GetString(propValueValue, 0, propValueValue.Length - 1); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxWfHomeDirDrive), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxWfHomeDirDrive = Encoding.ASCII.GetString(propValueValue, 0, propValueValue.Length - 1); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxInitialProgram), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxInitialProgram = Encoding.ASCII.GetString(propValueValue, 0, propValueValue.Length - 1); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxWfProfilePath), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxWfProfilePath = Encoding.ASCII.GetString(propValueValue, 0, propValueValue.Length - 1); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxWorkDirectory), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxWorkDirectory = Encoding.ASCII.GetString(propValueValue, 0, propValueValue.Length - 1); | |
} | |
else if (string.Equals(propNameValue, nameof(UserParameters.CtxCallbackNumber), StringComparison.OrdinalIgnoreCase)) | |
{ | |
userParameters.CtxCallbackNumber = Encoding.ASCII.GetString(propValueValue, 0, propValueValue.Length - 1); | |
} | |
else | |
{ | |
throw new Exception("Unsupported property."); | |
} | |
} | |
} | |
} | |
return userParameters; | |
} | |
private static byte[] GetPropValueValue(byte[] propValue) | |
{ | |
byte[] propValueValue = new byte[propValue.Length / 2]; | |
for (int j = 0; j < propValue.Length; j = j + 2) | |
{ | |
int highNibble = HexToInt(propValue[j]); | |
int lowNibble = HexToInt(propValue[j + 1]); | |
propValueValue[j / 2] = (byte) (highNibble << 4 | lowNibble); | |
} | |
return propValueValue; | |
} | |
private static int HexToInt(byte value) | |
{ | |
if ('0' <= value && value <= '9') | |
{ | |
return value - '0'; | |
} | |
if ('a' <= value && value <= 'f') | |
{ | |
return value - 'a' + 10; | |
} | |
if ('A' <= value && value <= 'F') | |
{ | |
return value - 'A' + 10; | |
} | |
throw new Exception("Invalid character."); | |
} | |
} | |
public class UserParameters | |
{ | |
public uint? CtxCfgPresent { get; set; } | |
public CtxCfgFlags1? CtxCfgFlags1 { get; set; } | |
public uint? CtxCallBack { get; set; } | |
public uint? CtxKeyboardLayout { get; set; } | |
public byte? CtxMinEncryptionLevel { get; set; } | |
public uint? CtxNwLogonServer { get; set; } | |
public string CtxWfHomeDir { get; set; } | |
public string CtxWfHomeDirDrive { get; set; } | |
public string CtxInitialProgram { get; set; } | |
public uint? CtxMaxConnectionTime { get; set; } | |
public uint? CtxMaxDisconnectionTime { get; set; } | |
public uint? CtxMaxIdleTime { get; set; } | |
public string CtxWfProfilePath { get; set; } | |
public CtxShadow? CtxShadow { get; set; } | |
public string CtxWorkDirectory { get; set; } | |
public string CtxCallbackNumber { get; set; } | |
} | |
[Flags] | |
public enum CtxCfgFlags1 : uint | |
{ | |
Undefined1 = 0x00000000, | |
Undefined2 = 0x00000001, | |
Undefined3 = 0x00000002, | |
DisableCam = 0x00000004, | |
WallpaperDisabled = 0x00000008, | |
DisableExe = 0x00000010, | |
DisableClip = 0x00000020, | |
DisableLpt = 0x00000040, | |
DisableCcm = 0x00000080, | |
DisableCdm = 0x00000100, | |
DisableCpm = 0x00000200, | |
UseDefaultGina = 0x00000400, | |
HomeDirectoryMapRoot = 0x00000800, | |
DisableEncryption = 0x00001000, | |
ForceClientLptDef = 0x00002000, | |
AutoClientLpts = 0x00004000, | |
AutoClientDrives = 0x00008000, | |
LogonDisabled = 0x00010000, | |
ReconnectSame = 0x00020000, | |
ResetBroken = 0x00040000, | |
PromptForPassword = 0x00080000, | |
InheritSecurity = 0x00100000, | |
InheritAutoClient = 0x00200000, | |
InheritMaxIdleTime = 0x00400000, | |
InheritMaxdisconnectionTime = 0x00800000, | |
InheritMaxsessionTime = 0x01000000, | |
InheritShadow = 0x02000000, | |
InheritCallbackNumber = 0x04000000, | |
InheritCallback = 0x08000000, | |
Undefined4 = 0x10000000, | |
Undefined5 = 0x20000000, | |
Undefined6 = 0x40000000, | |
Undefined7 = 0x80000000 | |
} | |
public enum CtxShadow : uint | |
{ | |
Disable = 0x00000000, | |
EnableInputNotify = 0x00000001, | |
EnableInputNoNotify = 0x00000002, | |
EnableNoInputNotify = 0x00000003, | |
EnableNoInputNoNotify = 0x00000004 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment