├── .gitignore ├── ADCollector3.sln ├── ADCollector3 ├── ADCollector.cs ├── ADCollector3.csproj ├── ADIDNS.cs ├── App.config ├── AsyncCollection.cs ├── BuildSearchString.cs ├── Collector │ ├── CollectAppliedGPO.cs │ ├── CollectNestedGroupMembership.cs │ ├── CollectSYSVOL.cs │ ├── CollectWithFilter.cs │ └── ICollector.cs ├── Display │ ├── DisplayADCS.cs │ ├── DisplayADIDNS.cs │ ├── DisplayDACL.cs │ ├── DisplayDD.cs │ ├── DisplayDL.cs │ ├── DisplayFileObject.cs │ ├── DisplayLDAPObjects.cs │ ├── DisplayList.cs │ ├── DisplayNativeMethod.cs │ ├── DisplayType.cs │ ├── DisplayUtil.cs │ └── IDisplay.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── Impersonation.cs ├── Logging.cs ├── NativeMethod.cs ├── Objects │ ├── ADCS.cs │ ├── CertificateTemplate.cs │ ├── DACL.cs │ ├── FileObject.cs │ ├── GPO.cs │ ├── ILDAPObject.cs │ ├── INFObject.cs │ ├── LDAPBaseObject.cs │ ├── OtherFileObject.cs │ ├── Trust.cs │ ├── User.cs │ └── XMLObject.cs ├── Options.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Results │ ├── DACLResult.cs │ ├── DDResult.cs │ ├── DLResult.cs │ ├── FileResult.cs │ ├── IResult.cs │ ├── LDAPResult.cs │ └── ListResult.cs ├── Rights.cs ├── SearchString │ ├── AppliedGPOSearchString.cs │ ├── LDAPSearchString.cs │ ├── NestedGMSearchString.cs │ ├── SAMAccountNameSearchString.cs │ ├── SMBSearchString.cs │ └── SearchString.cs ├── Searcher.cs ├── Utilities │ ├── Enums.cs │ ├── Helper.cs │ ├── Natives.cs │ ├── SchemaUtil.cs │ └── Struct.cs └── packages.config ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | .DS_Store 332 | -------------------------------------------------------------------------------- /ADCollector3.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32210.238 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ADCollector3", "ADCollector3\ADCollector3.csproj", "{D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Release|Any CPU = Release|Any CPU 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Debug|x64.ActiveCfg = Debug|x64 19 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Debug|x64.Build.0 = Debug|x64 20 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Release|x64.ActiveCfg = Release|x64 23 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {485030EE-8400-4BB2-B358-9D3F6F299211} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ADCollector3/ADCollector.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices.Protocols; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text.RegularExpressions; 8 | 9 | namespace ADCollector3 10 | { 11 | public class ADCollector 12 | { 13 | BuildSearchString buildSearchString; 14 | static bool CanConnectSYSVOL; 15 | public ADCollector() 16 | { 17 | Searcher searcher = new Searcher(); 18 | searcher.Init(); 19 | 20 | GPO.GetAllGPOs(); 21 | buildSearchString = new BuildSearchString(); 22 | 23 | Rights.BuildExtendedRightsDict(); 24 | Rights.BuildSchemaDict(); 25 | 26 | } 27 | 28 | 29 | public void Run() 30 | { 31 | GetLDAPBasicInfo(new List>> { Searcher.BasicLDAPInfo }); 32 | 33 | GetTypeObject(typeof(Trust)); 34 | 35 | GetLDAP(); 36 | 37 | GetNGAGP(); 38 | 39 | GetACL(); 40 | 41 | GetADCS(); 42 | 43 | CanConnectSYSVOL = CollectSYSVOL.CanConnectSYSVOL(); 44 | 45 | GetSMB(); 46 | 47 | GetGPP(); 48 | 49 | GetADIDNS(); 50 | } 51 | 52 | 53 | 54 | public void GetLDAPBasicInfo(List>> dl) 55 | { 56 | IDisplay displayer = new DisplayDL(); 57 | IDisplay.DisplayTitle("Basic LDAP Information"); 58 | IResult result = new DLResult { Result = dl }; 59 | displayer.DisplayResult(result); 60 | 61 | } 62 | 63 | 64 | public void GetNGAGP(List userList = null) 65 | { 66 | if (userList == null || userList.Count == 0 || (userList.Count==1 && userList[0] == null)) 67 | { 68 | userList = new List 69 | { 70 | Environment.GetEnvironmentVariable("USERNAME"), 71 | Environment.GetEnvironmentVariable("COMPUTERNAME")+"$" 72 | }; 73 | } 74 | List nestedGMSearchStringList = buildSearchString.GetNestedGMSearchString(userList); 75 | List appliedGPOSearchStringList = buildSearchString.GetAppliedGPOSearchString(userList); 76 | Collect(nestedGMSearchStringList); 77 | Collect(appliedGPOSearchStringList); 78 | } 79 | 80 | 81 | public void GetLDAP() 82 | { 83 | List ldapSearchStringList = buildSearchString.GetLDAPSearchString(); 84 | Collect(ldapSearchStringList); 85 | } 86 | 87 | 88 | public void GetLDAPOnly() 89 | { 90 | GetLDAPBasicInfo(new List>> { Searcher.BasicLDAPInfo }); 91 | 92 | GetLDAP(); 93 | } 94 | 95 | 96 | public void GetSMB() 97 | { 98 | List smbSearchStringList = buildSearchString.GetSMBSearchString(); 99 | 100 | foreach (var smbSearchString in smbSearchStringList) 101 | { 102 | IDisplay displayer = new DisplayFileObjects(); 103 | IDisplay.DisplayTitle(smbSearchString.Title); 104 | if (!CanConnectSYSVOL) { continue; } 105 | 106 | var sysvol = AsyncCollection.GetSYSVOLAsync(smbSearchString).Result; 107 | 108 | if (sysvol.Count == 0) { continue; } 109 | foreach (var file in sysvol) 110 | { 111 | displayer.DisplayResult(file); 112 | } 113 | } 114 | } 115 | 116 | 117 | public void GetACL(string targetDn = null) 118 | { 119 | IDisplay displayer = new DisplayDACL(); 120 | DACLResult result = new DACLResult(); 121 | 122 | if (targetDn == null) 123 | { 124 | IDisplay.DisplayTitle("Interesting ACL on the Domain Object"); 125 | var domainAcl = DACL.GetInterestingACLOnObject(Searcher.LdapInfo.RootDN); 126 | result.Result = new List { domainAcl }; 127 | displayer.DisplayResult(result); 128 | 129 | IDisplay.DisplayTitle("Interesting ACL on Group Policy Objects"); 130 | var gposDN = GPO.GetAllGPODNList(); 131 | result.Result = AsyncCollection.GetInterestingACLAsync(gposDN).Result; 132 | displayer.DisplayResult(result); 133 | 134 | IDisplay.DisplayTitle("LAPS Password View Access"); 135 | result.Result = DACL.GetLAPSACL(); 136 | displayer.DisplayResult(result); 137 | } 138 | else 139 | { 140 | IDisplay.DisplayTitle($"DACL on {targetDn.ToUpper()}"); 141 | result.Result = new List { DACL.GetACLOnObject(targetDn) }; 142 | displayer.DisplayResult(result); 143 | } 144 | } 145 | 146 | 147 | 148 | public void GetGPP() 149 | { 150 | IDisplay displayer = new DisplayFileObjects(); 151 | var gppSearchString = new SMBSearchString 152 | { 153 | Title = "Group Policy Preference Passwords", 154 | FileAttributes = new List { "cpassword" } 155 | }; 156 | IDisplay.DisplayTitle(gppSearchString.Title); 157 | if (!CanConnectSYSVOL) { return; } 158 | 159 | var xmlFileList = AsyncCollection.GetGPPXML().Result; 160 | gppSearchString.FilePathList = xmlFileList; 161 | var sysvol = AsyncCollection.GetSYSVOLAsync(gppSearchString).Result; 162 | 163 | if (sysvol.Count == 0) { return; } 164 | foreach (var file in sysvol) 165 | { 166 | displayer.DisplayResult(file); 167 | } 168 | 169 | } 170 | 171 | 172 | public static void Collect(string title, List dacl) 173 | { 174 | IDisplay displayer = new DisplayDACL(); 175 | IDisplay.DisplayTitle(title); 176 | IResult result = new DACLResult { Result = dacl }; 177 | displayer.DisplayResult(result); 178 | 179 | } 180 | 181 | 182 | 183 | public void Collect(List searchStringList) 184 | { 185 | ICollector collector = null; 186 | IDisplay displayer = null; 187 | 188 | if (searchStringList.FirstOrDefault() is LDAPSearchString) 189 | { 190 | collector = new CollectWithFilter(); 191 | displayer = new DisplayLDAPObjects(); 192 | } 193 | else if (searchStringList.FirstOrDefault() is NestedGMSearchString) 194 | { 195 | collector = new CollectNestedGroupMembership(); 196 | displayer = new DisplayList(); 197 | } 198 | else if (searchStringList.FirstOrDefault() is AppliedGPOSearchString) 199 | { 200 | collector = new CollectAppliedGPO(); 201 | displayer = new DisplayDD(); 202 | } 203 | 204 | foreach (SearchString searchString in searchStringList) 205 | { 206 | IDisplay.DisplayTitle(searchString.Title); 207 | IResult result = collector.Collect(searchString); 208 | displayer.DisplayResult(result); 209 | } 210 | } 211 | 212 | 213 | 214 | public void GetTypeObject(Type t) 215 | { 216 | IDisplay displayer = new Display(); 217 | 218 | if (t == typeof(Trust)) 219 | { 220 | IDisplay.DisplayTitle("Domain Trusts"); 221 | var domainTrust = new Trust(); 222 | var trustResult = NativeMethod.GetDsEnumerateDomainTrusts(); 223 | var domainTrusts = domainTrust.AnalyzeTrust(trustResult); 224 | DisplayType.DisplayTrust(domainTrusts); 225 | Console.WriteLine(); 226 | } 227 | } 228 | 229 | 230 | public void GetADCS() 231 | { 232 | var displayer = new DisplayADCS(); 233 | 234 | IDisplay.DisplayTitle("Certificate Services"); 235 | ADCS.CertificateServices = AsyncCollection.GetADCSAsync().Result; 236 | displayer.DisplayResult(ADCS.CertificateServices); 237 | 238 | 239 | IDisplay.DisplayTitle("Interesting Certificate Templates"); 240 | var certicateTemplates = AsyncCollection.GetInterestingCertTemplatesAsync().Result; 241 | displayer.DisplayResult(certicateTemplates); 242 | } 243 | 244 | 245 | 246 | public void GetTemplates() 247 | { 248 | var displayer = new DisplayADCS(); 249 | IDisplay.DisplayTitle("Certificate Templates"); 250 | var templates = AsyncCollection.GetAllCertTemplatesAsync(); 251 | displayer.DisplayResult(templates); 252 | } 253 | 254 | 255 | public void GetADIDNS() 256 | { 257 | var displayer = new DisplayADIDNS(); 258 | IDisplay.DisplayTitle("DNS Records in the Domain"); 259 | var domainDNS = ADIDNS.GetDNS(false); 260 | displayer.DisplayResult(domainDNS); 261 | 262 | IDisplay.DisplayTitle("DNS Records in the Forest"); 263 | var forestDNS = ADIDNS.GetDNS(true); 264 | displayer.DisplayResult(forestDNS); 265 | } 266 | 267 | 268 | public static void GetHostSession(string host) 269 | { 270 | var displayer = new DisplayNativeMethod(); 271 | IDisplay.DisplayTitle($"Session Enum on {host}"); 272 | var Results = NativeMethod.GetNetSessionEnum(host); 273 | displayer.DisplayNetSession(Results); 274 | } 275 | 276 | 277 | public static void GetHostUser(string host) 278 | { 279 | var displayer = new DisplayNativeMethod(); 280 | IDisplay.DisplayTitle($"User Enum on {host}"); 281 | var Results = NativeMethod.GetNetWkstaUserEnum(host); 282 | displayer.DisplayNetWkstaUserEnum(Results); 283 | } 284 | 285 | 286 | public static void GetHostGroupMember(string host, string group = "Administrators") 287 | { 288 | var displayer = new DisplayNativeMethod(); 289 | IDisplay.DisplayTitle($"Local Group Members Enum on {host} for {group}"); 290 | var Results = NativeMethod.GetNetLocalGroupGetMembers(host, group); 291 | displayer.DisplayNetLocalGroupGetMembers(Results); 292 | } 293 | 294 | 295 | public void InvokeACLScan(string user) 296 | { 297 | if (user == null) { return; } 298 | var displayer = new DisplayDACL(); 299 | IDisplay.DisplayTitle($"Interesting ACL for {user.ToUpper()}"); 300 | DACLResult result = new DACLResult(); 301 | 302 | var groups = new CollectNestedGroupMembership(); 303 | groups.Collect(new NestedGMSearchString { SAMAccountName = user }); 304 | var groupSIDs = CollectNestedGroupMembership.UserSIDNameDictionary[user.ToUpper()].Keys.ToList(); 305 | result.Result = DACL.ACLScan(user, groupSIDs); 306 | 307 | displayer.DisplayResult(result); 308 | } 309 | 310 | 311 | 312 | public void GetSchemaCount() 313 | { 314 | var displayer = new DisplayList(); 315 | IDisplay.DisplayTitle($"Scheme Attributes Count"); 316 | ListResult result = new ListResult(); 317 | var attrList = new List(); 318 | var allAttrs = SchemaUtil.GetSchemaAttributes(); 319 | var attributes = SchemaUtil.GetUncommonSchemaAttributes(allAttrs); 320 | 321 | //var attrResult = AsyncCollection.GetAttributeCount(attributes); 322 | var attrResult = AsyncCollection.GetAttributeCountAsync(attributes);//.Result; 323 | 324 | foreach (var attr in attrResult) 325 | { 326 | if (attr != null){ attrList.Add(attr); } 327 | } 328 | result.Result = attrList; 329 | 330 | displayer.DisplayResult(result); 331 | } 332 | } 333 | 334 | } 335 | -------------------------------------------------------------------------------- /ADCollector3/ADCollector3.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {D1AE1ACF-8AA2-4935-ACDF-EC22BAE2DF76} 9 | Exe 10 | ADCollector3 11 | ADCollector 12 | v4.7.2 13 | 512 14 | true 15 | true 16 | 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | true 40 | bin\x64\Debug\ 41 | DEBUG;TRACE 42 | full 43 | x64 44 | 7.3 45 | prompt 46 | true 47 | 48 | 49 | bin\x64\Release\ 50 | TRACE 51 | true 52 | pdbonly 53 | x64 54 | 7.3 55 | prompt 56 | true 57 | 58 | 59 | 60 | ..\packages\CommandLineParser.2.7.82\lib\net461\CommandLine.dll 61 | 62 | 63 | ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll 64 | 65 | 66 | ..\packages\NLog.4.7.13\lib\net45\NLog.dll 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /ADCollector3/ADIDNS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ADCollector3 9 | { 10 | class ADIDNS 11 | { 12 | //Retrieve IP from LDAP dnsRecord only 13 | public static Dictionary> GetDNS(bool searchForest = false) 14 | { 15 | Dictionary> dnsDict = new Dictionary>(); 16 | 17 | string dDnsDn = "DC=DomainDnsZones," + Searcher.LdapInfo.RootDN;//not searching from "CN=MicrosoftDNS,DC=DomainDnsZones,"; 18 | string fDnsDn = "DC=ForestDnsZones," + Searcher.LdapInfo.ForestDN; 19 | string queryZones = @"(&(objectClass=dnsZone)(!(DC=*arpa))(!(DC=RootDNSServers)))"; 20 | string[] dnsZoneAttrs = { "name" }; 21 | 22 | var dnsZoneSearchResult = searchForest ? 23 | Searcher.GetResultEntries(new LDAPSearchString 24 | { 25 | DN = fDnsDn, 26 | Filter = queryZones, 27 | ReturnAttributes = dnsZoneAttrs, 28 | Scope = SearchScope.Subtree 29 | }).ToList() 30 | : 31 | Searcher.GetResultEntries(new LDAPSearchString 32 | { 33 | DN = dDnsDn, 34 | Filter = queryZones, 35 | ReturnAttributes = dnsZoneAttrs, 36 | Scope = SearchScope.Subtree 37 | }).ToList(); 38 | 39 | //excluding objects that have been removed 40 | string queryRecord = @"(&(objectClass=*)(!(DC=@))(!(DC=*DnsZones))(!(DC=*arpa))(!(DC=_*))(!dNSTombstoned=TRUE))"; 41 | string[] dnsAttrs = { "dnsRecord" }; 42 | 43 | byte[] dnsByte = null; 44 | string ip = null; 45 | string hostname = null; 46 | 47 | foreach (var dnsZone in dnsZoneSearchResult) 48 | { 49 | Dictionary dnsRecordDict = new Dictionary(); 50 | 51 | var dnsResponse = Searcher.GetResultEntries(new LDAPSearchString 52 | { 53 | DN = dnsZone.DistinguishedName, 54 | Filter = queryRecord, 55 | Scope = SearchScope.OneLevel, 56 | ReturnAttributes = dnsAttrs 57 | }).ToList(); 58 | 59 | foreach (var dnsResult in dnsResponse) 60 | { 61 | //If have permission to view the record 62 | if (dnsResult.Attributes.Contains("dnsRecord")) 63 | { 64 | dnsByte = ((byte[])dnsResult.Attributes["dnsRecord"][0]); 65 | 66 | ip = ResolveDNSRecord(dnsByte); 67 | if (ip == string.Empty || ip == null) 68 | { 69 | continue; 70 | } 71 | hostname = dnsResult.DistinguishedName; 72 | 73 | if (!dnsRecordDict.ContainsKey(hostname.ToUpper())) 74 | { 75 | dnsRecordDict.Add(hostname.ToUpper(), ip); 76 | } 77 | } 78 | } 79 | if (!dnsDict.ContainsKey(dnsZone.DistinguishedName.ToUpper())) 80 | { 81 | dnsDict.Add(dnsZone.DistinguishedName.ToUpper(), dnsRecordDict); 82 | } 83 | } 84 | return dnsDict; 85 | } 86 | 87 | 88 | 89 | 90 | 91 | 92 | ////Retrieve Single dnsRecord with a hostname 93 | //public static string GetSingleDNSRecord(string hostname, bool searchForest = true) 94 | //{ 95 | // string dDnsDn = "DC=DomainDnsZones," + Searcher.LdapInfo.RootDN;//not searching from "CN=MicrosoftDNS,DC=DomainDnsZones,"; 96 | // string fDnsDn = "DC=ForestDnsZones," + Searcher.LdapInfo.ForestDN; 97 | // string queryZones = @"(&(objectClass=dnsZone)(!(DC=*arpa))(!(DC=RootDNSServers)))"; 98 | // string[] dnsZoneAttrs = { "name" }; 99 | 100 | // var dnsZoneSearchResult = searchForest ? 101 | // Searcher.GetResultEntries(new LDAPSearchString 102 | // { 103 | // DN = fDnsDn, 104 | // Filter = queryZones, 105 | // ReturnAttributes = dnsZoneAttrs, 106 | // Scope = SearchScope.Subtree 107 | // }).ToList() 108 | // : 109 | // Searcher.GetResultEntries(new LDAPSearchString 110 | // { 111 | // DN = dDnsDn, 112 | // Filter = queryZones, 113 | // ReturnAttributes = dnsZoneAttrs, 114 | // Scope = SearchScope.Subtree 115 | // }).ToList(); 116 | 117 | 118 | // string queryRecord = @"(&(objectClass=*)(!(DC=@))(!(DC=*DnsZones))(!(DC=*arpa))(!(DC=_*))(name=" + hostname + "))"; 119 | 120 | // string[] dnsAttrs = { "dnsRecord", "name" }; 121 | 122 | // foreach (var dnsZone in dnsZoneSearchResult) 123 | // { 124 | // var hostResult = Searcher.GetResultEntry(new LDAPSearchString 125 | // { 126 | // DN = dnsZone.DistinguishedName, 127 | // Filter = queryRecord, 128 | // Scope = SearchScope.OneLevel, 129 | // ReturnAttributes = dnsAttrs 130 | // }); 131 | 132 | // if (hostResult == null) { continue; } 133 | 134 | // if (hostResult.Attributes.Contains("dnsRecord")) 135 | // { 136 | // string ip = ResolveDNSRecord(((byte[])hostResult.Attributes["dnsRecord"][0])); 137 | 138 | // return ip; 139 | // } 140 | 141 | // } 142 | // return null; 143 | //} 144 | 145 | 146 | public static string ResolveDNSRecord(byte[] dnsByte) 147 | { 148 | var rdatatype = dnsByte[2]; 149 | 150 | string ip = null; 151 | 152 | if (rdatatype == 1) 153 | { 154 | ip = dnsByte[24] + "." + dnsByte[25] + "." + dnsByte[26] + "." + dnsByte[27]; 155 | } 156 | return ip; 157 | } 158 | 159 | 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /ADCollector3/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ADCollector3/AsyncCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace ADCollector3 11 | { 12 | public static class AsyncCollection 13 | { 14 | public static async Task> GetInterestingACLAsync(List targetDnList) 15 | { 16 | var tasks = new List>(); 17 | 18 | foreach (string targetDn in targetDnList) 19 | { 20 | tasks.Add(Task.Run(() => DACL.GetInterestingACLOnObject(targetDn))); 21 | } 22 | 23 | var aclList = (await Task.WhenAll(tasks)).ToList(); 24 | 25 | return aclList; 26 | } 27 | 28 | 29 | public static async Task> GetACLAsync(List targetDnList) 30 | { 31 | var tasks = new List>(); 32 | 33 | foreach (string targetDn in targetDnList) 34 | { 35 | tasks.Add(Task.Run(() => DACL.GetACLOnObject(targetDn))); 36 | } 37 | 38 | var aclList = (await Task.WhenAll(tasks)).ToList(); 39 | 40 | return aclList; 41 | } 42 | 43 | 44 | public static async Task> GetSYSVOLAsync(SearchString searchstring) 45 | { 46 | SMBSearchString searchString = (SMBSearchString)searchstring; 47 | 48 | var tasks = new List>(); 49 | 50 | foreach(var filePath in searchString.FilePathList) 51 | { 52 | var ss = new SMBSearchString { Title = searchString.Title, FilePath = filePath, FileAttributes = searchString.FileAttributes }; 53 | tasks.Add(Task.Run(() => CollectSYSVOL.Collect(ss))); 54 | } 55 | 56 | var aclList = (await Task.WhenAll(tasks)).ToList(); 57 | return aclList; 58 | } 59 | 60 | 61 | public static async Task> TestEnrollmentEndpointsAsync(string caName, string caHostname) 62 | { 63 | List> tasks = new List>(); 64 | 65 | //Adjusted from PKIAudit 66 | foreach (var protocol in new string[] { "http://", "https://" }) 67 | { 68 | foreach (var suffix in new string[] { "/certsrv/", 69 | $"/{caName}_CES_Kerberos/service.svc", 70 | $"/{caName}_CES_Kerberos/service.svc/CES", 71 | "/ADPolicyProvider_CEP_Kerberos/service.svc", 72 | "/certsrv/mscep/" }) 73 | { 74 | var url = protocol + caHostname + suffix; 75 | 76 | tasks.Add(Task.Run(() => Helper.TestWebConnection(url))); 77 | } 78 | } 79 | var enrollmentEndpoints = (await Task.WhenAll(tasks)).ToList(); 80 | 81 | return enrollmentEndpoints; 82 | } 83 | 84 | 85 | 86 | public static async Task> GetADCSAsync() 87 | { 88 | string csFilter = @"(objectCategory=pKIEnrollmentService)"; 89 | 90 | string csDn = "CN=Enrollment Services,CN=Public Key Services,CN=Services," + Searcher.LdapInfo.ConfigDN; 91 | 92 | List> tasks = new List>(); 93 | 94 | var csEntries = Searcher.GetResultEntries(new LDAPSearchString {DN = csDn, 95 | Filter = csFilter, 96 | Scope = SearchScope.Subtree 97 | }).ToList(); 98 | 99 | foreach (SearchResultEntry csEntry in csEntries) 100 | { 101 | tasks.Add(Task.Run(() => ADCS.GetADCS(csEntry))); 102 | } 103 | 104 | var caList = (await Task.WhenAll(tasks)).ToList(); 105 | 106 | return caList; 107 | } 108 | 109 | 110 | 111 | public static async Task> GetInterestingCertTemplatesAsync() 112 | { 113 | var certTemplateList = Searcher.GetResultEntries(new LDAPSearchString 114 | { 115 | DN = "CN=Certificate Templates,CN=Public Key Services,CN=Services,"+Searcher.LdapInfo.ConfigDN, 116 | Filter = @"(objectCategory=pKICertificateTemplate)", 117 | Scope = SearchScope.Subtree }).ToList(); 118 | 119 | List> tasks = new List>(); 120 | 121 | foreach (var certTemplate in certTemplateList) 122 | { 123 | tasks.Add(Task.Run(() => CertificateTemplate.GetInterestingCertTemplates(certTemplate))); 124 | } 125 | var CTTasks = (await Task.WhenAll(tasks)).ToList(); 126 | return CTTasks; 127 | } 128 | 129 | 130 | 131 | 132 | 133 | public static async Task> GetGPPXML() 134 | { 135 | List gpoPathList = GPO.GroupPolicies.Keys.Select(k => $"\\\\{Searcher.LdapInfo.DomainController}\\SYSVOL\\{Searcher.LdapInfo.DomainName}\\Policies\\{k}").ToList(); 136 | 137 | var xmlList = new List { "Groups.xml", "Services.xml", "Scheduledtasks.xml", "Datasources.xml", "Printers.xml", "Drives.xml" }; 138 | 139 | List>> tasks = new List>>(); 140 | 141 | try 142 | { 143 | foreach(var path in gpoPathList) 144 | { 145 | tasks.Add(Task.Run(() => GetXMLFileFromPath(path))); 146 | } 147 | } 148 | catch { } 149 | 150 | var fileList = (await Task.WhenAll(tasks)).ToList(); 151 | var files = new List(); 152 | foreach(var file in fileList) 153 | { 154 | foreach(string name in file) 155 | { 156 | if (name != null && name == string.Empty) { files.Add(name); } 157 | } 158 | } 159 | return files; 160 | } 161 | 162 | 163 | public static async Task> GetXMLFileFromPath(string path) 164 | { 165 | List> tasks = new List>(); 166 | 167 | var xmlList = new List { "Groups.xml", "Services.xml", "ScheduledTasks.xml", "Datasources.xml", "Printers.xml", "Drives.xml" }; 168 | 169 | foreach (string file in Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories)) 170 | { 171 | tasks.Add(Task.Run(() => Helper.CheckFile(xmlList, file))); 172 | } 173 | 174 | var files = (await Task.WhenAll(tasks)).ToList(); 175 | return files; 176 | } 177 | 178 | 179 | 180 | public static List GetAllCertTemplatesAsync() 181 | { 182 | List CTS = new List(); 183 | var certTemplateList = Searcher.GetResultEntries(new LDAPSearchString 184 | { 185 | DN = "CN=Certificate Templates,CN=Public Key Services,CN=Services," + Searcher.LdapInfo.ConfigDN, 186 | Filter = @"(objectCategory=pKICertificateTemplate)", 187 | Scope = SearchScope.Subtree 188 | }).ToList(); 189 | 190 | foreach (var certTemplate in certTemplateList) 191 | { 192 | CTS.Add(CertificateTemplate.GetAllCertTemplates(certTemplate)); 193 | } 194 | 195 | return CTS; 196 | } 197 | 198 | 199 | public static List GetAttributeCountAsync(List attributes) 200 | { 201 | var attrCount = new List(); 202 | int maxConcurrency = 20; 203 | 204 | using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency)) 205 | { 206 | List tasks = new List(); 207 | foreach (var attr in attributes) 208 | { 209 | concurrencySemaphore.Wait(); 210 | var t = Task.Factory.StartNew(() => 211 | { 212 | try 213 | { 214 | var temp = SchemaUtil.GetAttributeCount(attr); 215 | 216 | if (temp != null){ attrCount.Add(temp); } 217 | } 218 | finally 219 | { 220 | concurrencySemaphore.Release(); 221 | } 222 | }); 223 | tasks.Add(t); 224 | } 225 | 226 | Task.WaitAll(tasks.ToArray()); 227 | } 228 | 229 | //foreach (var attr in attributes) 230 | //{ 231 | // tasks.Add(Task.Run(() => SchemaUtil.GetAttributeCount(attr))); 232 | //} 233 | //var c = (await Task.WhenAll(tasks)).ToList(); 234 | 235 | return attrCount; 236 | } 237 | 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /ADCollector3/BuildSearchString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ADCollector3 9 | { 10 | public class BuildSearchString 11 | { 12 | public List LDAPSearchStringObjectList { get; set; } = new List(); 13 | public List LDAPSearchStringList { get; set; } = new List(); 14 | public List SMBSearchStringObjectList { get; set; } = new List(); 15 | public List SMBSearchStringList { get; set; } = new List(); 16 | public List DictionaryListSearchStringList { get; set; } = new List(); 17 | public List NestedGMSearchStringList { get; set; } = new List(); 18 | public List AppliedGPOSearchStringList { get; set; } = new List(); 19 | 20 | 21 | LDAPInfo ldapInfo { get; set; } 22 | 23 | public BuildSearchString() 24 | { 25 | ldapInfo = Searcher.LdapInfo; 26 | CreateLDAPSearchString(); 27 | CreateSMBSearchString(); 28 | } 29 | 30 | public List GetLDAPSearchString() 31 | { 32 | foreach (var searchArray in LDAPSearchStringObjectList) 33 | { 34 | LDAPSearchStringList.Add(new LDAPSearchString 35 | { 36 | Title = (string)searchArray[0], 37 | DN = (string)searchArray[1], 38 | Filter = (string)searchArray[2], 39 | ReturnAttributes = (string[])searchArray[3], 40 | Scope = (SearchScope)(searchArray.Length > 4 ? searchArray[4] : SearchScope.Subtree), 41 | UseGlobalCatalog = (bool)(searchArray.Length > 5? searchArray[5] : false) 42 | });; 43 | } 44 | return LDAPSearchStringList; 45 | } 46 | 47 | public List GetSMBSearchString() 48 | { 49 | foreach(var searchArray in SMBSearchStringObjectList) 50 | { 51 | SMBSearchStringList.Add(new SMBSearchString 52 | { 53 | Title = (string)searchArray[0], 54 | FilePathList = (List)searchArray[1], 55 | FileAttributes = (List)searchArray[2] 56 | }); 57 | } 58 | 59 | return SMBSearchStringList; 60 | } 61 | 62 | public List GetNestedGMSearchString(List sAMAccountNameList) 63 | { 64 | foreach(string sAMAccountName in sAMAccountNameList) 65 | { 66 | NestedGMSearchStringList.Add( 67 | new NestedGMSearchString { Title = $"Nested Group Membership for {sAMAccountName}", SAMAccountName = sAMAccountName } 68 | ); 69 | } 70 | return NestedGMSearchStringList; 71 | } 72 | 73 | 74 | public List GetAppliedGPOSearchString(List sAMAccountNameList) 75 | { 76 | foreach (string sAMAccountName in sAMAccountNameList) 77 | { 78 | AppliedGPOSearchStringList.Add( 79 | new AppliedGPOSearchString { Title = $"Effective GPOs Applied on {sAMAccountName}", SAMAccountName = sAMAccountName } 80 | ); 81 | } 82 | return AppliedGPOSearchStringList; 83 | } 84 | 85 | 86 | 87 | public void CreateLDAPSearchString() 88 | { 89 | string targetDN = ldapInfo.TargetSearchBase; 90 | string RootDN = ldapInfo.RootDN; 91 | string SchemaDN = ldapInfo.SchemaDN; 92 | 93 | //Domain Attributes 94 | LDAPSearchStringObjectList.Add(new object[] 95 | { 96 | "Domain Attributes", RootDN, @"(name=*)", new string[] { "maxPwdAge","LockoutDuration", "whenCreated","whenChanged","ObjectSID", "ms-DS-MachineAccountQuota", "MinPwdLength","MinPwdLength","MaxPwdAge","LockoutThreshold","LockoutDuration",},SearchScope.Base 97 | }); 98 | 99 | 100 | //TDO 101 | LDAPSearchStringObjectList.Add(new object[] 102 | { 103 | "Trusted Domain Objects in the Current Domain", "CN=System,"+RootDN, @"(objectCategory=TrustedDomain)", new string[] { "cn", "securityidentifier" } 104 | }); 105 | 106 | 107 | //Domain Controllers 108 | LDAPSearchStringObjectList.Add(new object[] 109 | { 110 | "Domain Controllers", targetDN, @"(primaryGroupID=516)", new string[] { "cn", "samaccountname", "dNSHostName", "logonCount", "operatingsystem", "operatingsystemversion", "whenCreated", "whenChanged", "managedBy", "dnsRecord" } 111 | }); 112 | 113 | 114 | //Read Only Domain Controllers 115 | LDAPSearchStringObjectList.Add(new object[] 116 | { 117 | "Read-Only Domain Controllers", targetDN, @"(primaryGroupID=521)", new string[] { "cn", "samaccountname", "dNSHostName", "logonCount", "operatingsystem", "operatingsystemversion", "whenCreated", "whenChanged", "managedBy", "dnsRecord" } 118 | }); 119 | 120 | 121 | 122 | //Unconstrainde Delegation 123 | //TRUSTED_FOR_DELEGATION 124 | //By default, DCs are configured to allow Kerberos Unconstrained Delegation. 125 | //So excluding DCs here 126 | LDAPSearchStringObjectList.Add(new object[] 127 | { 128 | "Unconstrained Delegation Accounts", targetDN, @"(&(userAccountControl:1.2.840.113556.1.4.803:=524288)(!primaryGroupID=516))", new string[] { "servicePrincipalName", "sAMAccountName" } 129 | }); 130 | 131 | 132 | 133 | //Constrainde Delegation 134 | //TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION 135 | //By default, RODCs are configured to allow Kerberos Constrained Delegation with Protocol Transition. 136 | //So excluding RODCs here 137 | LDAPSearchStringObjectList.Add(new object[] 138 | { 139 | "Constrained Delegation [with S4U2Self enabled] Accounts (Any Authentication Protocol)", targetDN, @"(&(userAccountControl:1.2.840.113556.1.4.803:=16777216)(!primaryGroupID=521))", new string[] { "servicePrincipalName", "sAMAccountName" } 140 | }); 141 | 142 | 143 | 144 | ////Constrainde Delegation with Services 145 | LDAPSearchStringObjectList.Add(new object[] 146 | { 147 | "Constrained Delegation Accounts with associated services", targetDN, @"(msDS-AllowedToDelegateTo=*)", new string[] { "msDS-AllowedToDelegateTo", "servicePrincipalName", "sAMAccountName" } 148 | }); 149 | 150 | 151 | //Resources-based Constrained Delegation 152 | LDAPSearchStringObjectList.Add(new object[] 153 | { 154 | "Resources-based Constrained Delegation Accounts", targetDN, @"(msDS-AllowedToActOnBehalfOfOtherIdentity=*)", new string[] { "msDS-AllowedToActOnBehalfOfOtherIdentity", "servicePrincipalName", "sAMAccountName" } 155 | }); 156 | 157 | 158 | 159 | //Privileged Accounts 160 | LDAPSearchStringObjectList.Add(new object[] 161 | { 162 | "Privileged Accounts", targetDN, "(&(objectClass=user)(memberof:1.2.840.113556.1.4.1941:=CN=Administrators,CN=Builtin," + RootDN + "))", new string[] { "MemberOf", "sAMAccountName" } 163 | }); 164 | 165 | 166 | 167 | //Sensitive & Not Delegated 168 | LDAPSearchStringObjectList.Add(new object[] 169 | { 170 | "Sensitive & Not Delegated Accounts", targetDN, @"(userAccountControl:1.2.840.113556.1.4.803:=1048576)", new string[] { "servicePrincipalName", "sAMAccountName" } 171 | }); 172 | 173 | 174 | 175 | //Protected Users 176 | LDAPSearchStringObjectList.Add(new object[] 177 | { 178 | "Protected Users", ("CN=Protected Users,CN=Users,"+RootDN), @"(name=*)", new string[] { "member" }, SearchScope.Base 179 | }); 180 | 181 | 182 | 183 | //AdminSDHolder 184 | LDAPSearchStringObjectList.Add(new object[] 185 | { 186 | "AdminSDHolder Protected Accounts", targetDN, @"(&(adminCount=1)(objectCategory=person))", new string[] { "servicePrincipalName", "sAMAccountName" } 187 | }); 188 | 189 | 190 | 191 | //Password Does Not Expire 192 | LDAPSearchStringObjectList.Add(new object[] 193 | { 194 | "Password Does Not Expire Accounts", targetDN, @"(userAccountControl:1.2.840.113556.1.4.803:=65536)", new string[] { "servicePrincipalName", "sAMAccountName" } 195 | }); 196 | 197 | 198 | 199 | //User Accounts With SPN 200 | LDAPSearchStringObjectList.Add(new object[] 201 | { 202 | "User Accounts With SPN Set", targetDN, @"(&(sAMAccountType=805306368)(servicePrincipalName=*))", new string[] { "servicePrincipalName", "sAMAccountName" } 203 | }); 204 | 205 | 206 | 207 | //Accounts With No Password 208 | LDAPSearchStringObjectList.Add(new object[] 209 | { 210 | "Accounts With No Password", targetDN, @"(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=32))", new string[] { "servicePrincipalName", "sAMAccountName" } 211 | }); 212 | 213 | 214 | 215 | //DontRequirePreauth 216 | LDAPSearchStringObjectList.Add(new object[] 217 | { 218 | "DontRequirePreauth Accounts", targetDN, @"(userAccountControl:1.2.840.113556.1.4.803:=4194304)", new string[] { "servicePrincipalName", "sAMAccountName" } 219 | }); 220 | 221 | 222 | 223 | //Interesting Description 224 | LDAPSearchStringObjectList.Add(new object[] 225 | { 226 | "Interesting Descriptions (default: pass) on User Objects", targetDN, @"(&(sAMAccountType=805306368)(description=*pass*))", new string[] { "description", "servicePrincipalName", "sAMAccountName" } 227 | }); 228 | 229 | 230 | 231 | //Confidential Attributes 232 | LDAPSearchStringObjectList.Add(new object[] 233 | { 234 | "Confidential Attributes", SchemaDN, @"(searchFlags:1.2.840.113556.1.4.803:=128)", new string[] { "" } 235 | }); 236 | 237 | 238 | 239 | //Accounts Allow Reversible Password Encryption 240 | LDAPSearchStringObjectList.Add(new object[] 241 | { 242 | "Accounts Allow Reversible Password Encryption", targetDN, @"(UserAccountControl:1.2.840.113556.1.4.803:=128)", new string[] { "sAMAccountName" } 243 | }); 244 | 245 | 246 | 247 | //Machine Owners 248 | LDAPSearchStringObjectList.Add(new object[] 249 | { 250 | "Machine Owners", targetDN, @"(ms-ds-CreatorSID=*)", new string[] { "ms-ds-CreatorSID", "sAMAccountName" } 251 | }); 252 | 253 | 254 | //LAPS Password 255 | LDAPSearchStringObjectList.Add(new object[] 256 | { 257 | "LAPS Password", targetDN, @"(ms-Mcs-AdmPwd=*)", new string[] { "sAMAccountName", "ms-Mcs-AdmPwd" } 258 | }); 259 | 260 | 261 | //UserPassword 262 | LDAPSearchStringObjectList.Add(new object[] 263 | { 264 | "User Passwords", targetDN, @"(|(userPassword=*)(UnixUserPassword=*)(unicodePwd=*)(msSFU30Password=*)(os400-password=*))", 265 | new string[] { "sAMAccountName", "userPassword", "UnixUserPassword", "unicodePwd", "msSFU30Password", "os400-password" } 266 | }); 267 | 268 | 269 | //SPN: EXCHANGE 270 | LDAPSearchStringObjectList.Add(new object[] 271 | { 272 | "Accounts with Exchange Service SPNs", targetDN, @"(servicePrincipalName=exchange*)", new string[] { "servicePrincipalName", "sAMAccountName" } 273 | }); 274 | 275 | 276 | //SPN: HTTP 277 | LDAPSearchStringObjectList.Add(new object[] 278 | { 279 | "Accounts with HTTP Service SPNs", targetDN, @"(servicePrincipalName=http*)", new string[] { "servicePrincipalName", "sAMAccountName" } 280 | }); 281 | 282 | 283 | //SPN: MSSQL 284 | LDAPSearchStringObjectList.Add(new object[] 285 | { 286 | "Accounts with MSSQL Service SPNs", targetDN, @"(servicePrincipalName=mssql*)", new string[] { "servicePrincipalName", "sAMAccountName" } 287 | }); 288 | } 289 | 290 | 291 | 292 | public void CreateSMBSearchString() 293 | { 294 | SMBSearchStringObjectList.Add(new object[] 295 | { 296 | "Kerberos Policy & System Access", 297 | new List {@"\\" + ldapInfo.DomainController + @"\SYSVOL\" + ldapInfo.DomainName + @"\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" }, 298 | new List{ "System Access", "Kerberos Policy" } 299 | }); 300 | 301 | //SMBSearchStringObjectList.Add(new object[] 302 | //{ 303 | // "Privilege Rights", 304 | // new List {@"\\" + ldapInfo.DomainController + @"\SYSVOL\" + ldapInfo.DomainName + @"\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" }, 305 | // new List{ "Privilege Rights" } 306 | //}); 307 | 308 | List gpoPathList = GPO.GroupPolicies.Keys.Select( 309 | k => $"\\\\{ldapInfo.DomainController}\\SYSVOL\\{ldapInfo.DomainName}\\Policies\\{k}\\MACHINE\\Microsoft\\Windows NT\\SecEdit\\GptTmpl.inf" 310 | ).ToList(); 311 | 312 | 313 | SMBSearchStringObjectList.Add(new object[] 314 | { 315 | "Privilege Rights", 316 | gpoPathList, 317 | new List{ "Privilege Rights" } 318 | }); 319 | 320 | 321 | SMBSearchStringObjectList.Add(new object[] 322 | { 323 | "Restricted Group", 324 | gpoPathList, 325 | new List{ "Group Membership" } 326 | }); 327 | 328 | 329 | } 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /ADCollector3/Collector/CollectAppliedGPO.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices; 5 | using System.DirectoryServices.Protocols; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Security.Principal; 9 | using System.Text; 10 | using System.Text.RegularExpressions; 11 | using System.Threading.Tasks; 12 | 13 | namespace ADCollector3 14 | { 15 | public class CollectAppliedGPO : ICollector 16 | { 17 | Logger _logger { get; set; } 18 | public Dictionary> AppliedGPOs { get; set; } 19 | 20 | //Dependency: Nested Group SID of the user 21 | //It is required to check security filtering with nested security group SID of the user 22 | public CollectAppliedGPO() 23 | { 24 | _logger = LogManager.GetCurrentClassLogger(); 25 | } 26 | 27 | 28 | 29 | //1. Iterate each OU/Domain/Site: "gplink" & "gpoptions" 30 | //2. Find GPOs that are linked to each OU/Domain/Site 31 | //3. Iterate each GPO to find out if they have WMI filters: "gPCWQLFilter" 32 | //4. Find the WMI policy and check if the policy is filtered out: "msWMI-Parm2" 33 | public IResult Collect(SearchString searchstring) 34 | { 35 | AppliedGPOSearchString searchString = (AppliedGPOSearchString)searchstring; 36 | 37 | var ouList = CollectMyOUs(searchString.SAMAccountName); 38 | if (ouList == null) { return null; } 39 | 40 | AppliedGPOs = new Dictionary>(); 41 | Regex gpoRx = new Regex(@"=(\{.+?\}),", RegexOptions.Compiled); 42 | Regex gpoptionRx = new Regex(@";(\d)", RegexOptions.Compiled); 43 | 44 | foreach (string ouDN in ouList) 45 | { 46 | bool isBlocking = false; 47 | bool isEnforced = false; 48 | bool isDenied = false; 49 | var linkedGPOs = new Dictionary(); 50 | 51 | using(var ouEntry = Searcher.GetDirectoryEntry(ouDN)) 52 | { 53 | 54 | //Linked GPOs & Enforcement 55 | if (ouEntry.Properties.Contains("gplink")) 56 | { 57 | string[] gplinkArrary = Regex.Split(ouEntry.Properties["gplink"][0].ToString(), @"\]\["); 58 | if (gplinkArrary == null) { break; } 59 | 60 | foreach (var gplinkString in gplinkArrary) 61 | { 62 | if (gplinkString.Replace(" ", "") == string.Empty) { continue; } 63 | 64 | Match matchGPO = gpoRx.Match(gplinkString); 65 | 66 | Match matchGpoption = gpoptionRx.Match(gplinkString); 67 | 68 | string gpoID = matchGPO.Groups[1].ToString().ToUpper(); 69 | 70 | //[LDAP://cn={E8D8C72C-3AAB-496C-90CD-C5F44F0AF10C},cn=policies,cn=system,DC=corplab,DC=local;0] 71 | //0: Default: The GPO Link is not ignored and is not an enforced GPO. 72 | //1: The GPO Link MUST be ignored. 73 | //2: The GPO Link is an enforced GPO. 74 | isEnforced = (int.Parse(matchGpoption.Groups[1].ToString()) == 2); 75 | 76 | string gpoDn = "CN=" + gpoID + ",CN=Policies,CN=System," + Searcher.LdapInfo.RootDN; 77 | 78 | try//in case the gpo was deleted 79 | { 80 | string gpoName = GPO.GroupPolicies[gpoID]; 81 | //SecurityFiltering: Check if the target GPO applied to the current user 82 | isDenied = IsDeniedPolicy(searchString.SAMAccountName, gpoDn); 83 | gpoName = isDenied ? (gpoName + " [X Denied]") : gpoName; 84 | gpoName = isEnforced ? (gpoName + " [Enforced]") : gpoName; 85 | 86 | linkedGPOs.Add(gpoID, gpoName); 87 | } 88 | catch { _logger.Warn($"GPO {gpoID} was probably deleted."); } 89 | } 90 | } 91 | //If a OU blocks inheritance 92 | if (ouEntry.Properties.Contains("gpOptions")) 93 | { 94 | //OUs that block inheritance will only ignore non-enforecd GPO 95 | //OU Attribute: gPOptions=1 Block Inheritance 96 | isBlocking = (int)ouEntry.Properties["gpOptions"][0] == 1; 97 | } 98 | } 99 | 100 | string ou = isBlocking ? (ouDN + " [Blocking Inheritance]") : ouDN; 101 | 102 | AppliedGPOs.Add(ou, linkedGPOs); 103 | } 104 | return new DDResult { Title = searchString.Title, Result = AppliedGPOs}; 105 | } 106 | 107 | 108 | 109 | 110 | public List CollectMyOUs(string sAMAccountName) 111 | { 112 | _logger.Debug($"Collecting OUs for {sAMAccountName}"); 113 | 114 | List ouList = new List(); 115 | 116 | sAMAccountName = sAMAccountName.ToUpper(); 117 | 118 | var result = Searcher.GetSingleAttributeValue(Searcher.LdapInfo.RootDN, $"(samaccountname={sAMAccountName})", "distinguishedname"); 119 | if (result == null) { return null; } 120 | string myDN = (string)result; 121 | 122 | try 123 | { 124 | myDN = myDN.ToUpper(); 125 | 126 | while (myDN.Contains(",OU")) 127 | { 128 | if (myDN.Contains(("CN=" + sAMAccountName + ","))) 129 | { 130 | myDN = myDN.Replace(("CN=" + sAMAccountName + ","), string.Empty); 131 | } 132 | else 133 | { 134 | myDN = myDN.Substring(myDN.IndexOf(",OU") + 1); 135 | } 136 | ouList.Add(myDN); 137 | } 138 | //add Domain DN 139 | myDN = myDN.Substring(myDN.IndexOf(",DC=") + 1); 140 | ouList.Add(myDN); 141 | //if (sAMAccountName.Contains("$")) 142 | //{ 143 | // //add Site DN 144 | // string site = CollectMySite(sAMAccountName); 145 | // if (site != null) 146 | // { 147 | // string siteDn = "CN=" + site + ",CN=Sites,CN=Configuration," + Searcher.LdapInfo.ForestDN; 148 | // ouList.Add(siteDn); 149 | // } 150 | //} 151 | return ouList; 152 | } 153 | catch (Exception e) 154 | { 155 | _logger.Warn(e.Message); 156 | return null; 157 | } 158 | } 159 | 160 | 161 | 162 | 163 | public bool IsDeniedPolicy(string sAMAccountName, string gpoDn) 164 | { 165 | _logger.Debug($"Checking if ({gpoDn}) is denied by security filtering"); 166 | 167 | var rules = DACL.GetAuthorizationRules(gpoDn, out _); 168 | 169 | bool isDenied = true; 170 | foreach (ActiveDirectoryAccessRule rule in rules) 171 | { 172 | //Security Filtering 173 | //Apply-Group-Policy: edacfd8f-ffb3-11d1-b41d-00a0c968f939 174 | if ((rule.ActiveDirectoryRights.ToString().ToLower() == "extendedright") && 175 | (rule.ObjectType.ToString().ToUpper() == "EDACFD8F-FFB3-11D1-B41D-00A0C968F939")) 176 | { 177 | string groupSID = rule.IdentityReference.Translate(typeof(SecurityIdentifier)).ToString().ToUpper(); 178 | 179 | try 180 | { 181 | //If the target GPO applys to the current user's security groups 182 | var userNestedGSID = CollectNestedGroupMembership.UserSIDNameDictionary[sAMAccountName.ToUpper()]; 183 | 184 | if (userNestedGSID.ContainsKey(groupSID)) 185 | { 186 | isDenied = false; 187 | } 188 | } 189 | catch (Exception e) 190 | { 191 | _logger.Error(e.Message); 192 | } 193 | } 194 | } 195 | 196 | string deny = isDenied ? string.Empty : " not"; 197 | _logger.Debug($"({gpoDn}) is{deny} denied"); 198 | return isDenied; 199 | } 200 | 201 | 202 | 203 | 204 | 205 | //public static string CollectMySite(string sAMAccountName) 206 | //{ 207 | // logger.Debug($"Collecting Site for {sAMAccountName}"); 208 | // IntPtr pBuffer; 209 | // try 210 | // { 211 | // uint result = Natives.DsGetSiteName(sAMAccountName, out pBuffer); 212 | 213 | // if (result == 0) 214 | // { 215 | // string mySiteName = Marshal.PtrToStringAuto(pBuffer); 216 | 217 | // Natives.NetApiBufferFree(pBuffer); 218 | // logger.Debug($"{sAMAccountName} is in {mySiteName}"); 219 | // return mySiteName; 220 | // } 221 | // return null; 222 | // } 223 | // catch (Exception e) 224 | // { 225 | // logger.Warn(e.Message); 226 | // return null; 227 | // } 228 | 229 | //} 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /ADCollector3/Collector/CollectNestedGroupMembership.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices.Protocols; 5 | using System.Linq; 6 | using System.Security.Principal; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ADCollector3 11 | { 12 | class CollectNestedGroupMembership : ICollector 13 | { 14 | Logger _logger { get; set; } 15 | //Dictionary> 16 | public static Dictionary> UserSIDNameDictionary { get; set; } 17 | 18 | public CollectNestedGroupMembership() 19 | { 20 | _logger = LogManager.GetCurrentClassLogger(); 21 | UserSIDNameDictionary = new Dictionary>(); 22 | } 23 | 24 | 25 | public IResult Collect(SearchString searchstring) 26 | { 27 | NestedGMSearchString searchString = (NestedGMSearchString)searchstring; 28 | 29 | _logger.Debug($"Collecting Nested Group Membership for {searchString.SAMAccountName}"); 30 | List groupList = new List(); 31 | Dictionary groupMap = new Dictionary(); 32 | 33 | string nameFilter = $"(sAMAccountName={searchString.SAMAccountName})"; 34 | 35 | var ldapSearchString = new LDAPSearchString { DN = Searcher.LdapInfo.RootDN, Filter = nameFilter, Scope = SearchScope.Subtree }; 36 | var resultEntry = Searcher.GetResultEntry(ldapSearchString); 37 | if (resultEntry == null) { return null; } 38 | 39 | using (var userEntry = (Searcher.GetDirectoryEntry(resultEntry.DistinguishedName))) 40 | { 41 | //https://www.morgantechspace.com/2015/08/active-directory-tokengroups-vs-memberof.html 42 | //Use RefreshCach to get the constructed attribute tokenGroups. 43 | userEntry.RefreshCache(new string[] { "tokenGroups" }); 44 | 45 | foreach (byte[] sid in userEntry.Properties["tokenGroups"]) 46 | { 47 | string groupSID = new SecurityIdentifier(sid, 0).ToString(); 48 | string groupName = Helper.SIDNameSID(groupSID); 49 | groupList.Add(groupName); 50 | groupMap.Add(groupSID, groupName); 51 | } 52 | } 53 | 54 | //Somehow these groups are missing 55 | groupMap.Add("S-1-5-11", @"NT AUTHORITY\Authenticated Users"); 56 | groupMap.Add("S-1-5-15", @"NT AUTHORITY\This Organization"); 57 | UserSIDNameDictionary.Add(searchString.SAMAccountName.ToUpper(), groupMap); 58 | 59 | return new ListResult { Title = searchString.Title, Result = groupList}; 60 | } 61 | 62 | //public static Dictionary> GetUserSIDNameDictionary() 63 | //{ 64 | // return UserSIDNameDictionary; 65 | //} 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ADCollector3/Collector/CollectSYSVOL.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Xml; 6 | using System.Linq; 7 | using static ADCollector3.Helper; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace ADCollector3 11 | { 12 | public class CollectSYSVOL 13 | { 14 | private static Logger _logger { get; set; } = LogManager.GetCurrentClassLogger(); 15 | public static Regex groupMemRX { get; set; } = new Regex("__"); 16 | 17 | public static FileResult Collect(SearchString searchstring) 18 | { 19 | SMBSearchString searchString = (SMBSearchString)searchstring; 20 | if (searchString.FilePath == null | searchString.FilePath == string.Empty) { return null; } 21 | string filePath = searchString.FilePath; 22 | 23 | if (!File.Exists(filePath)){ return null; } 24 | 25 | try 26 | { 27 | switch (Path.GetExtension(filePath).ToLower()) 28 | { 29 | case ".inf": 30 | if (INFObject.EnumeratedINFObjects.ContainsKey(filePath)) 31 | { 32 | return new FileResult { FileObject = CollectFileObject(INFObject.EnumeratedINFObjects[filePath], searchString) }; 33 | } 34 | else 35 | { 36 | var infFile = new INFObject(filePath); 37 | infFile.ParseFile(); 38 | return new FileResult { FileObject = CollectFileObject(infFile, searchString) }; 39 | } 40 | 41 | case ".xml": 42 | var xmlFile = new XMLObject(filePath); 43 | xmlFile.ParseFile(); 44 | return new FileResult { FileObject = CollectFileObject(xmlFile, searchString) }; 45 | default: 46 | var otherFile = new OtherFileObject(filePath); 47 | otherFile.ParseFile(); 48 | return new FileResult { FileObject = CollectFileObject(otherFile, searchString) }; 49 | } 50 | } 51 | catch (Exception e) 52 | { 53 | _logger.Error(e.Message); 54 | return null; 55 | } 56 | 57 | 58 | } 59 | 60 | 61 | public static FileObject CollectFileObject(FileObject fileObject, SearchString searchstring) 62 | { 63 | 64 | SMBSearchString searchString = (SMBSearchString)searchstring; 65 | Dictionary>> Properties = new Dictionary>>(); 66 | try 67 | { 68 | if (fileObject is INFObject) 69 | { 70 | foreach (var property in fileObject.Properties) 71 | { 72 | if (searchString.FileAttributes.Contains(property.Key)) 73 | { 74 | Dictionary attributes = new Dictionary(); 75 | 76 | foreach (var attr in property.Value.First()) 77 | { 78 | attributes.Add(CheckGroupName(attr.Key), CheckGroupName(attr.Value)); 79 | } 80 | Properties.Add(property.Key, new List> { attributes }); 81 | } 82 | } 83 | } 84 | else if (fileObject is XMLObject) 85 | { 86 | foreach (var property in fileObject.Properties) 87 | { 88 | var attrsList = new List>(); 89 | 90 | foreach (var attrDict in property.Value) 91 | { 92 | var attrs = new Dictionary(); 93 | foreach (var attr in attrDict) 94 | { 95 | if (searchString.FileAttributes.Contains(attr.Key)) 96 | { 97 | attrs.Add(attr.Key, attr.Value); 98 | } 99 | } 100 | if (attrs.Count != 0) { attrsList.Add(attrs); } 101 | 102 | } 103 | if (attrsList.Count != 0) { Properties.Add(property.Key, attrsList); } 104 | } 105 | } 106 | }catch(Exception e) 107 | { 108 | logger.Error($"{fileObject.FilePath}: {e.Message}"); 109 | } 110 | 111 | if (Properties.Count == 0) { return null; } 112 | 113 | fileObject.Properties = Properties; 114 | return fileObject; 115 | } 116 | 117 | 118 | public static string CheckGroupName(string name) 119 | { 120 | if (name == string.Empty) { return name; } 121 | 122 | //*S-1-5-21-2964291000-3697813071-3260305335-2606__Memberof = *S-1-5-32-555,*S-1-5-32-544 123 | string value = ""; 124 | try 125 | { 126 | if (name.Contains("__")) 127 | { 128 | string groupName = groupMemRX.Split(name)[0]; 129 | string relation = groupMemRX.Split(name)[1]; 130 | if (groupName.Contains("*")) 131 | { 132 | groupName = ConvertSIDToName(groupName.Replace("*", null)); 133 | } 134 | value = string.Format($"{groupName} ({relation})"); 135 | } 136 | else 137 | { 138 | foreach (var v in name.Split(',')) 139 | { 140 | string sid = v.Replace(",", null); 141 | value += sid.Contains("*") ? (ConvertSIDToName(sid.Replace("*", null)) + ",") : (sid + ","); 142 | } 143 | value = value.Trim(','); 144 | } 145 | } 146 | catch(Exception e) 147 | { 148 | logger.Error($"{name}:{e.Message}"); 149 | } 150 | 151 | return value; 152 | } 153 | 154 | public static bool CanConnectSYSVOL() 155 | { 156 | string sysvolPath = $"\\\\{Searcher.LdapInfo.DomainController}\\SYSVOL\\{Searcher.LdapInfo.DomainName}\\"; 157 | 158 | try 159 | { 160 | var accessControlList = Directory.GetAccessControl(sysvolPath); 161 | if (accessControlList == null) 162 | { 163 | logger.Error("Unable to access SYSVOL"); 164 | return false; 165 | } 166 | } 167 | catch 168 | { 169 | logger.Error("Unable to access SYSVOL"); 170 | return false; 171 | } 172 | 173 | return true; 174 | } 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /ADCollector3/Collector/CollectWithFilter.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices; 5 | using System.DirectoryServices.Protocols; 6 | using System.Linq; 7 | using System.Numerics; 8 | using System.Security.Principal; 9 | using static ADCollector3.Helper; 10 | 11 | namespace ADCollector3 12 | { 13 | public class CollectWithFilter : ICollector 14 | { 15 | public LDAPResult CollectResult { get; set; } 16 | Logger _logger; 17 | public CollectWithFilter() 18 | { 19 | _logger = LogManager.GetCurrentClassLogger(); 20 | } 21 | public IResult Collect(SearchString searchstring) 22 | { 23 | _logger.Debug($"Collecting"); 24 | LDAPSearchString searchString = (LDAPSearchString)searchstring; 25 | List ldapObjects = new List(); 26 | 27 | var results = Searcher.GetResultEntries(searchString).ToList(); 28 | 29 | foreach (var result in results) 30 | { 31 | //If we need User 32 | if (searchString.ReturnAttributes.Contains("sAMAccountName")) 33 | { 34 | var user = User.GetUserObject(result); 35 | ldapObjects.Add(user); 36 | } 37 | else 38 | { 39 | ldapObjects.Add(new LDAPBaseObject { Attributes = ProcessAttributes(result.Attributes), DistinguishedName = result.DistinguishedName }); 40 | } 41 | } 42 | 43 | CollectResult = new LDAPResult { LDAPObjects = ldapObjects, Title = searchstring.Title }; 44 | 45 | _logger.Debug("LDAP Objects Collected"); 46 | return CollectResult; 47 | } 48 | 49 | 50 | //Convert special type attributes to user friendly string 51 | public static Dictionary> ProcessAttributes(SearchResultAttributeCollection collection) 52 | { 53 | Dictionary> attributes = new Dictionary>(); 54 | 55 | 56 | foreach (string attrName in collection.AttributeNames) 57 | { 58 | List attributeValue = new List(); 59 | if (attrName.ToLower() == "whencreated" || attrName.ToLower() == "whenchanged") 60 | { 61 | attributeValue.Add(ConvertWhenCreated(collection[attrName][0].ToString()).ToString()); 62 | } 63 | else if (attrName.ToLower() == "maxpwdage") 64 | { 65 | attributeValue.Add(ConvertLargeInteger(collection[attrName][0].ToString(), true)); 66 | } 67 | else if (attrName.ToLower() == "lockoutduration") 68 | { 69 | attributeValue.Add(ConvertLargeInteger(collection[attrName][0].ToString(), false)); 70 | } 71 | else if (attrName.ToLower() == "objectsid" || attrName.ToLower() == "securityidentifier") 72 | { 73 | attributeValue.Add(ConvertByteArrayToSID((byte[])collection[attrName][0])); 74 | } 75 | else if (attrName.ToLower() == "userpassword" || attrName.ToLower() == "unixuserpassword" 76 | || attrName.ToLower() == "unicodepwd" || attrName.ToLower() == "mssfu30password" 77 | || attrName.ToLower() == "os400-password") 78 | { 79 | attributeValue.Add(ConvertAscii(collection[attrName])); 80 | } 81 | else 82 | { 83 | for (int i = 0; i < collection[attrName].Count; i++) 84 | { 85 | attributeValue.Add(collection[attrName][i].ToString()); 86 | } 87 | } 88 | attributes.Add(attrName, attributeValue); 89 | } 90 | 91 | return attributes; 92 | } 93 | 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ADCollector3/Collector/ICollector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ADCollector3 8 | { 9 | public interface ICollector 10 | { 11 | IResult Collect(SearchString searchstring); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayADCS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ADCollector3 8 | { 9 | public class DisplayADCS : IDisplay 10 | { 11 | public static IDisplay daclDisplayer { get; set; } = new DisplayDACL(); 12 | public void DisplayResult(List certsrvs) 13 | { 14 | if (certsrvs == null || certsrvs.Count == 0) { return; } 15 | foreach (var certsrv in certsrvs) 16 | { 17 | Console.WriteLine(" * CA Name: {0}", certsrv.CAName); 18 | Console.WriteLine(" DNSHostName: {0}", certsrv.dnsHostName); 19 | Console.WriteLine(" WhenCreated: {0}", certsrv.whenCreated); 20 | Console.WriteLine(" Flags: {0}", certsrv.flags); 21 | Console.WriteLine(" Enrollment Servers: {0}", certsrv.enrollServers); 22 | Console.WriteLine(" Certificate Templates: {0}", string.Join(",", certsrv.certTemplates)); 23 | Console.WriteLine(" Enrollment Endpoints: {0}", string.Join(",", certsrv.enrollmentEndpoints.Where(x => x != null).ToList())); 24 | Console.WriteLine(" Supplied SAN Enabled: {0}", certsrv.allowUserSuppliedSAN.ToString().ToUpper()); 25 | 26 | foreach (var cert in certsrv.caCertificates) 27 | { 28 | Console.WriteLine(" Cert SubjectName: {0}", cert.SubjectName.Name); 29 | Console.WriteLine(" Cert Thumbprint: {0}", cert.Thumbprint); 30 | Console.WriteLine(" Cert Start Date: {0}", cert.NotBefore); 31 | Console.WriteLine(" Cert End Date: {0}", cert.NotAfter); 32 | } 33 | 34 | daclDisplayer.DisplayResult(new DACLResult { Result = new List { certsrv.DACL } }); 35 | Console.WriteLine(); 36 | } 37 | } 38 | 39 | 40 | public void DisplayResult(List certTemplates) 41 | { 42 | if (certTemplates == null || certTemplates.Count == 0) { return; } 43 | 44 | foreach (var template in certTemplates.Where(v => v != null).ToList()) 45 | { 46 | if (template.IsPublished) 47 | { 48 | Console.WriteLine(" * CertTemplate: {0}", template.TemplateDisplayName); 49 | Console.WriteLine(" CA Name: {0}", template.PublishedBy); 50 | Console.WriteLine(" CN: {0}", template.TemplateCN); 51 | Console.WriteLine(" Enrollment Flag: {0}", template.EnrollFlag); 52 | Console.WriteLine(" Cert Name Flag: {0}", template.CertNameFlag); 53 | Console.WriteLine(" Extended Key Usage: {0}", string.Join(",", template.ExtendedKeyUsage)); 54 | Console.WriteLine(" RA Signatures: {0}", template.RaSigature); 55 | Console.WriteLine(" DACL:"); 56 | daclDisplayer.DisplayResult(new DACLResult { Result = new List { template.DACL } }); 57 | Console.WriteLine(); 58 | } 59 | } 60 | foreach (var template in certTemplates.Where(v => v != null).ToList()) 61 | { 62 | if (!template.IsPublished) 63 | { 64 | Console.WriteLine(" * The Certificate Template [{0}] is vulnerable but it is not published by any CA ", template.TemplateDisplayName); 65 | Console.WriteLine(" DACL:"); 66 | daclDisplayer.DisplayResult(new DACLResult { Result = new List { template.DACL } }); 67 | Console.WriteLine(); 68 | } 69 | 70 | } 71 | } 72 | public override void DisplayResult(IResult collectResult) 73 | { 74 | throw new NotImplementedException(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayADIDNS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ADCollector3 5 | { 6 | public class DisplayADIDNS : IDisplay 7 | { 8 | public void DisplayResult(Dictionary> dnsDict) 9 | { 10 | foreach (var zone in dnsDict) 11 | { 12 | Console.WriteLine(" * Zone: {0}", zone.Key); 13 | foreach (var dns in zone.Value) 14 | { 15 | Console.WriteLine(" - {0,-20} {1,-25}", dns.Value, dns.Key); 16 | } 17 | } 18 | } 19 | 20 | public override void DisplayResult(IResult collectResult) 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayDACL.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace ADCollector3 5 | { 6 | public class DisplayDACL : IDisplay 7 | { 8 | public override void DisplayResult(IResult collectResult) 9 | { 10 | if (collectResult == null) { return; } 11 | DACLResult collectResults = (DACLResult)collectResult; 12 | if (collectResults.Result == null) { return; } 13 | 14 | foreach (var dacl in collectResults.Result) 15 | { 16 | if (dacl == null) { continue; } 17 | Console.WriteLine(" - {0}", dacl.ObjectName); 18 | 19 | foreach (var ace in dacl.ACEs) 20 | { 21 | int c = 0; 22 | foreach (var attr in ace.Value) 23 | { 24 | if (c == 0) 25 | { 26 | Console.WriteLine(" {0, -36} {1}", ace.Key, attr); 27 | } 28 | else 29 | { 30 | Console.WriteLine(" {0, -36} {1}", string.Empty, attr); 31 | } 32 | c = 1; 33 | } 34 | } 35 | Console.WriteLine(); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayDD.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DisplayDD : IDisplay 6 | { 7 | public override void DisplayResult(IResult collectResult) 8 | { 9 | if (collectResult == null) { return; } 10 | DDResult collectResults = (DDResult)collectResult; 11 | if (collectResults.Result == null) { return; } 12 | 13 | foreach (var dict1 in collectResults.Result) 14 | { 15 | Console.WriteLine(" - {0}", dict1.Key); 16 | foreach(var dict2 in dict1.Value) 17 | { 18 | Console.WriteLine(" {0, -36} {1}", dict2.Key, dict2.Value); 19 | } 20 | Console.WriteLine(); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayDL.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DisplayDL : IDisplay 6 | { 7 | public override void DisplayResult(IResult collectResult) 8 | { 9 | if (collectResult == null) { return; } 10 | DLResult collectResults = (DLResult)collectResult; 11 | if (collectResults.Result == null) { return; } 12 | foreach(var result in collectResults.Result) 13 | { 14 | if (result == null) { return; } 15 | foreach (var dict in result) 16 | { 17 | string key = dict.Key; 18 | int c = 0; 19 | foreach (var attr in dict.Value) 20 | { 21 | if (c == 0) 22 | { 23 | Console.WriteLine(" {0, -36} {1}", key, attr); 24 | } 25 | else 26 | { 27 | Console.WriteLine(" {0, -36} {1}", string.Empty, attr); 28 | } 29 | c = 1; 30 | } 31 | } 32 | Console.WriteLine(); 33 | } 34 | 35 | 36 | 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayFileObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using static ADCollector3.Enums; 5 | 6 | namespace ADCollector3 7 | { 8 | public class DisplayFileObjects : IDisplay 9 | { 10 | public override void DisplayResult(IResult collectResult) 11 | { 12 | if (collectResult == null) { return; } 13 | FileResult collectResults = (FileResult)collectResult; 14 | if (collectResults.FileObject == null) { return; } 15 | if (collectResults.FileObject.Properties.Count == 0) { return; } 16 | Console.WriteLine(" * {0}", collectResults.FileObject.GPO); 17 | foreach (var sections in collectResults.FileObject.Properties) 18 | { 19 | foreach(var section in sections.Value) 20 | { 21 | Console.WriteLine(" - {0}", sections.Key); 22 | foreach (var attr in section) 23 | { 24 | Console.WriteLine(" {0, -36} {1}", attr.Key + " :", attr.Value); 25 | } 26 | Console.WriteLine(); 27 | } 28 | } 29 | 30 | 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayLDAPObjects.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static ADCollector3.Enums; 3 | 4 | namespace ADCollector3 5 | { 6 | public class DisplayLDAPObjects : IDisplay 7 | { 8 | public override void DisplayResult(IResult collectResult) 9 | { 10 | if (collectResult == null) { return; } 11 | LDAPResult collectResults = (LDAPResult)collectResult; 12 | if (collectResults.LDAPObjects.Count == 0) { return; } 13 | 14 | foreach (var ldapObject in collectResults.LDAPObjects) 15 | { 16 | Console.WriteLine(" * {0}", ldapObject.DistinguishedName.ToUpper()); 17 | 18 | if (ldapObject is User u) 19 | { 20 | foreach (var attr in u.Properities) 21 | { 22 | foreach (var value in attr.Value) 23 | { 24 | Console.WriteLine(" - {0, -30} {1}", attr.Key + ":", value); 25 | } 26 | } 27 | } 28 | else if (ldapObject is LDAPBaseObject b) 29 | { 30 | foreach (var dict in b.Attributes) 31 | { 32 | foreach (var attr in dict.Value) 33 | { 34 | Console.WriteLine(" {0, -30} {1}", dict.Key, attr); 35 | } 36 | //Console.WriteLine(); 37 | } 38 | } 39 | //Console.WriteLine(); 40 | } 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DisplayList : IDisplay 6 | { 7 | public override void DisplayResult(IResult collectResult) 8 | { 9 | if (collectResult == null) { return; } 10 | ListResult collectResults = (ListResult)collectResult; 11 | 12 | foreach (string obj in collectResults.Result) 13 | { 14 | Console.WriteLine(" {0, -25}", obj); 15 | //Console.WriteLine(); 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayNativeMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DisplayNativeMethod : IDisplay 6 | { 7 | 8 | public void DisplayNetSession(SESSION_INFO_10[] Results) 9 | { 10 | if (Results == null) { return; } 11 | else 12 | { 13 | foreach (var info in Results) 14 | { 15 | Console.WriteLine(" ------------------------------"); 16 | foreach (var t in info.GetType().GetFields()) 17 | { 18 | Console.WriteLine(" {0, -25} {1,-3}", t.Name.ToUpper() + " : ", t.GetValue(info)); 19 | } 20 | } 21 | } 22 | } 23 | 24 | 25 | 26 | 27 | public void DisplayNetWkstaUserEnum(WKSTA_USER_INFO_1[] Results) 28 | { 29 | if (Results == null) { return; } 30 | else 31 | { 32 | foreach (var info in Results) 33 | { 34 | Console.WriteLine(" ------------------------------"); 35 | foreach (var t in info.GetType().GetFields()) 36 | { 37 | Console.WriteLine(" {0, -25} {1,-3}", t.Name.ToUpper() + " : ", t.GetValue(info)); 38 | } 39 | } 40 | } 41 | } 42 | 43 | 44 | 45 | 46 | public void DisplayNetLocalGroupGetMembers(LOCALGROUP_MEMBERS_INFO_2[] Results) 47 | { 48 | if (Results == null) { return; } 49 | else 50 | { 51 | foreach (var info in Results) 52 | { 53 | Console.WriteLine(" ------------------------------"); 54 | foreach (var t in info.GetType().GetFields()) 55 | { 56 | Console.WriteLine(" {0, -25} {1,-3}", t.Name.ToUpper() + " : ", t.GetValue(info)); 57 | } 58 | } 59 | } 60 | } 61 | 62 | public override void DisplayResult(IResult collectResult) 63 | { 64 | throw new NotImplementedException(); 65 | } 66 | 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using static ADCollector3.Enums; 8 | 9 | namespace ADCollector3 10 | { 11 | class DisplayType 12 | { 13 | public static void DisplayTrust(List domainTrusts) 14 | { 15 | if (domainTrusts == null || domainTrusts.FirstOrDefault() == null) { return; } 16 | foreach (var trust in domainTrusts) 17 | { 18 | foreach (var f in typeof(Trust).GetFields(BindingFlags.Public | BindingFlags.Instance)) 19 | { 20 | Console.WriteLine(" {0, -25} {1,-3}", f.Name, f.GetValue(trust)); 21 | } 22 | Console.WriteLine(); 23 | } 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ADCollector3/Display/DisplayUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using static ADCollector3.Enums; 5 | 6 | namespace ADCollector3 7 | { 8 | public static class DisplayUtil 9 | { 10 | public static void PrintBanner() 11 | { 12 | Console.WriteLine(); 13 | Console.WriteLine(@" _ ____ ____ _ _ _ "); 14 | Console.WriteLine(@" / \ | _ \ / ___|___ | | | ___ ___ _| |_ ___ _ __ "); 15 | Console.WriteLine(@" / _ \ | | | | | / _ \| | |/ _ \/ __|_ __/ _ \| '__|"); 16 | Console.WriteLine(@" / ___ \| |_| | |__| (_) | | | __/ (__ | || (_) | | "); 17 | Console.WriteLine(@" /_/ \_\____/ \____\___/|_|_|\___|\___| |__/\___/|_| "); 18 | Console.WriteLine(); 19 | Console.WriteLine(" v3.0.1 by dev2null\r\n"); 20 | } 21 | 22 | 23 | public static void Print(string output, PrintColor color) 24 | { 25 | switch (color) 26 | { 27 | case PrintColor.YELLOW: 28 | Console.ForegroundColor = ConsoleColor.Yellow; 29 | Console.WriteLine(output); 30 | Console.ResetColor(); 31 | break; 32 | case PrintColor.GREEN: 33 | Console.ForegroundColor = ConsoleColor.Green; 34 | Console.WriteLine(output); 35 | Console.ResetColor(); 36 | break; 37 | case PrintColor.RED: 38 | Console.ForegroundColor = ConsoleColor.Red; 39 | Console.WriteLine(output); 40 | Console.ResetColor(); 41 | break; 42 | } 43 | } 44 | 45 | 46 | public static void Done() 47 | { 48 | Print("\n[*] Done!\n", PrintColor.GREEN); 49 | } 50 | 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ADCollector3/Display/IDisplay.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static ADCollector3.Enums; 7 | 8 | namespace ADCollector3 9 | { 10 | public abstract class IDisplay 11 | { 12 | public static void DisplayTitle(string Title) 13 | { 14 | DisplayUtil.Print(string.Format("\n[-] {0}:\n", Title), PrintColor.GREEN); 15 | } 16 | public abstract void DisplayResult(IResult collectResult); 17 | } 18 | 19 | public class Display : IDisplay 20 | { 21 | public override void DisplayResult(IResult collectResult) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ADCollector3/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /ADCollector3/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 23 | 24 | 25 | 26 | 27 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 28 | 29 | 30 | 31 | 32 | The order of preloaded assemblies, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | 38 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 39 | 40 | 41 | 42 | 43 | Controls if .pdbs for reference assemblies are also embedded. 44 | 45 | 46 | 47 | 48 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 49 | 50 | 51 | 52 | 53 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 54 | 55 | 56 | 57 | 58 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 59 | 60 | 61 | 62 | 63 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 64 | 65 | 66 | 67 | 68 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 69 | 70 | 71 | 72 | 73 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 74 | 75 | 76 | 77 | 78 | A list of unmanaged 32 bit assembly names to include, delimited with |. 79 | 80 | 81 | 82 | 83 | A list of unmanaged 64 bit assembly names to include, delimited with |. 84 | 85 | 86 | 87 | 88 | The order of preloaded assemblies, delimited with |. 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 97 | 98 | 99 | 100 | 101 | A comma-separated list of error codes that can be safely ignored in assembly verification. 102 | 103 | 104 | 105 | 106 | 'false' to turn off automatic generation of the XML Schema file. 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /ADCollector3/Impersonation.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Security.Principal; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace ADCollector3 12 | { 13 | public class Impersonation 14 | { 15 | public static void RunAs(string domain, string username, string password, Action action) 16 | { 17 | using (var accessToken = GetUserAccessToken(domain, username, password)) 18 | { 19 | WindowsIdentity.RunImpersonated(accessToken, action); 20 | } 21 | } 22 | 23 | 24 | 25 | 26 | internal static SafeAccessTokenHandle GetUserAccessToken(string domain, string username, string password) 27 | { 28 | const int LOGON32_PROVIDER_DEFAULT = 0; 29 | const int LOGON32_LOGON_NETONLY = 9; 30 | 31 | bool isLogonSuccessful = Natives.LogonUser(username, domain, password, LOGON32_LOGON_NETONLY, LOGON32_PROVIDER_DEFAULT, out var safeAccessTokenHandle); 32 | if (!isLogonSuccessful) 33 | { 34 | throw new Win32Exception(Marshal.GetLastWin32Error()); 35 | } 36 | 37 | return safeAccessTokenHandle; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ADCollector3/Logging.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using NLog.Config; 3 | using NLog.Targets; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ADCollector3 11 | { 12 | public class Logging 13 | { 14 | public static void LoadNormalConfig(LoggingConfiguration config) 15 | { 16 | ColoredConsoleTarget consoleTarget = new ColoredConsoleTarget 17 | { 18 | Name = "console", 19 | Layout = "[x] ${message}" 20 | }; 21 | 22 | config.AddRule(LogLevel.Error, LogLevel.Fatal, consoleTarget, "*"); 23 | SetConsoleColor(consoleTarget); 24 | LogManager.Configuration = config; 25 | } 26 | 27 | public static void LoadDebugConfig(LoggingConfiguration config) 28 | { 29 | ColoredConsoleTarget consoleTarget = new ColoredConsoleTarget 30 | { 31 | Name = "console", 32 | Layout = "[!] ${level:uppercase=true} (${callsite}): ${message}" 33 | }; 34 | config.AddRule(LogLevel.Debug, LogLevel.Fatal, consoleTarget, "*"); 35 | SetConsoleColor(consoleTarget); 36 | LogManager.Configuration = config; 37 | } 38 | 39 | public static void SetConsoleColor(ColoredConsoleTarget consoleTarget) 40 | { 41 | consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule { Condition = "level == LogLevel.Info", ForegroundColor = ConsoleOutputColor.Green }); 42 | consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule { Condition = "level == LogLevel.Debug", ForegroundColor = ConsoleOutputColor.Yellow }); 43 | consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule { Condition = "level == LogLevel.Trace", ForegroundColor = ConsoleOutputColor.DarkGreen }); 44 | consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule { Condition = "level == LogLevel.Warn", ForegroundColor = ConsoleOutputColor.Red }); 45 | consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule { Condition = "level == LogLevel.Error", ForegroundColor = ConsoleOutputColor.DarkRed }); 46 | } 47 | 48 | public static void LoadLoggingConfig(bool debug) 49 | { 50 | LoggingConfiguration config = new LoggingConfiguration(); 51 | if (debug) 52 | { 53 | LoadDebugConfig(config); 54 | } 55 | else 56 | { 57 | LoadNormalConfig(config); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ADCollector3/NativeMethod.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using static ADCollector3.Natives; 9 | 10 | namespace ADCollector3 11 | { 12 | public static class NativeMethod 13 | { 14 | static Logger logger { get; set; } = LogManager.GetCurrentClassLogger(); 15 | 16 | 17 | public static DS_DOMAIN_TRUSTS[] GetDsEnumerateDomainTrusts() 18 | { 19 | string SourceDomainName = Searcher.LdapInfo.DomainName; 20 | 21 | logger.Debug($"Enumerating Domain Trust for {SourceDomainName}"); 22 | 23 | uint domainCount = 0; 24 | IntPtr BufferPtr = new IntPtr(); 25 | 26 | try 27 | { 28 | var result = DsEnumerateDomainTrusts(SourceDomainName, 63, out BufferPtr, out domainCount); 29 | logger.Debug($"{domainCount} Trust Domain(s) Enumerated"); 30 | 31 | if ((domainCount > 0) && (result == 0)) 32 | { 33 | var BufferOffset = BufferPtr; 34 | var trustResults = new DS_DOMAIN_TRUSTS[domainCount]; 35 | 36 | for (int i = 0; i < domainCount; i++) 37 | { 38 | trustResults[i] = (DS_DOMAIN_TRUSTS)Marshal.PtrToStructure(BufferOffset, typeof(DS_DOMAIN_TRUSTS)); 39 | 40 | BufferOffset = (IntPtr)(BufferOffset.ToInt64() + (long)Marshal.SizeOf(typeof(DS_DOMAIN_TRUSTS))); 41 | } 42 | 43 | NetApiBufferFree(BufferPtr); 44 | return trustResults; 45 | } 46 | return null; 47 | } 48 | catch (Exception e) 49 | { 50 | logger.Error(e.Message); 51 | return null; 52 | } 53 | } 54 | 55 | 56 | public static SESSION_INFO_10[] GetNetSessionEnum(string hostname) 57 | { 58 | int EntriesRead, TotalEntries, ResumeHandle; 59 | 60 | EntriesRead = TotalEntries = ResumeHandle = 0; 61 | 62 | try 63 | { 64 | var result = NetSessionEnum(hostname, null, null, 10, out IntPtr BufferPtr, -1, ref EntriesRead, ref TotalEntries, ref ResumeHandle); 65 | 66 | if (result != 0) 67 | { 68 | return null; 69 | } 70 | else 71 | { 72 | var BufferOffset = BufferPtr; 73 | 74 | var sessResults = new SESSION_INFO_10[EntriesRead]; 75 | 76 | SESSION_INFO_10 sessionInfo10 = new SESSION_INFO_10(); 77 | 78 | for (int i = 0; i < EntriesRead; i++) 79 | { 80 | sessResults[i] = (SESSION_INFO_10)Marshal.PtrToStructure(BufferOffset, sessionInfo10.GetType()); 81 | 82 | BufferOffset = (IntPtr)(BufferOffset.ToInt64() + Marshal.SizeOf(sessionInfo10)); 83 | } 84 | 85 | NetApiBufferFree(BufferPtr); 86 | 87 | return sessResults; 88 | } 89 | } 90 | catch (Exception e) 91 | { 92 | logger.Error(e.Message); 93 | 94 | return null; 95 | } 96 | 97 | } 98 | 99 | 100 | 101 | 102 | 103 | public static WKSTA_USER_INFO_1[] GetNetWkstaUserEnum(string hostname) 104 | { 105 | int EntriesRead, TotalEntries, ResumeHandle; 106 | 107 | EntriesRead = TotalEntries = ResumeHandle = 0; 108 | 109 | try 110 | { 111 | var result = NetWkstaUserEnum(hostname, 1, out IntPtr BufferPtr, -1, out EntriesRead, out TotalEntries, ref ResumeHandle); 112 | 113 | if (result != 0) 114 | { 115 | return null; 116 | } 117 | else 118 | { 119 | var BufferOffset = BufferPtr; 120 | 121 | var wkResults = new WKSTA_USER_INFO_1[EntriesRead]; 122 | 123 | WKSTA_USER_INFO_1 userInfo1 = new WKSTA_USER_INFO_1(); 124 | 125 | 126 | for (int i = 0; i < EntriesRead; i++) 127 | { 128 | wkResults[i] = (WKSTA_USER_INFO_1)Marshal.PtrToStructure(BufferOffset, userInfo1.GetType()); 129 | 130 | BufferOffset = (IntPtr)(BufferOffset.ToInt64() + Marshal.SizeOf(userInfo1)); 131 | } 132 | 133 | NetApiBufferFree(BufferPtr); 134 | 135 | return wkResults; 136 | } 137 | } 138 | catch (Exception e) 139 | { 140 | logger.Error(e.Message); 141 | return null; 142 | } 143 | } 144 | 145 | 146 | 147 | 148 | 149 | public static LOCALGROUP_MEMBERS_INFO_2[] GetNetLocalGroupGetMembers(string hostname, string localgroup) 150 | { 151 | int EntriesRead, TotalEntries; 152 | 153 | IntPtr ResumeHandle = IntPtr.Zero; 154 | 155 | try 156 | { 157 | var result = NetLocalGroupGetMembers(hostname, localgroup, 2, out IntPtr BufferPtr, -1, out EntriesRead, out TotalEntries, ResumeHandle); 158 | 159 | if (EntriesRead > 0) 160 | { 161 | var BufferOffset = BufferPtr; 162 | 163 | var Results = new LOCALGROUP_MEMBERS_INFO_2[EntriesRead]; 164 | 165 | LOCALGROUP_MEMBERS_INFO_2 groupInfo = new LOCALGROUP_MEMBERS_INFO_2(); 166 | 167 | for (int i = 0; i < EntriesRead; i++) 168 | { 169 | Results[i] = (LOCALGROUP_MEMBERS_INFO_2)Marshal.PtrToStructure(BufferOffset, groupInfo.GetType()); 170 | 171 | BufferOffset = (IntPtr)(BufferOffset.ToInt64() + Marshal.SizeOf(groupInfo)); 172 | } 173 | 174 | NetApiBufferFree(BufferPtr); 175 | 176 | return Results; 177 | } 178 | else 179 | { 180 | return null; 181 | } 182 | } 183 | catch (Exception e) 184 | { 185 | logger.Error(e.Message); 186 | return null; 187 | } 188 | } 189 | 190 | 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /ADCollector3/Objects/ADCS.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using NLog; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.DirectoryServices; 6 | using System.DirectoryServices.Protocols; 7 | using System.Linq; 8 | using System.Security.AccessControl; 9 | using System.Security.Cryptography.X509Certificates; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using static ADCollector3.Enums; 13 | 14 | namespace ADCollector3 15 | { 16 | public class ADCS 17 | { 18 | public string CAName; 19 | public string whenCreated; 20 | public string dnsHostName; 21 | public string enrollServers; 22 | public PkiCertificateAuthorityFlags flags; 23 | public bool allowUserSuppliedSAN; 24 | public List caCertificates; 25 | public DACL DACL; 26 | public List certTemplates; 27 | public List enrollmentEndpoints; 28 | public static List CertificateServices { get; set; } 29 | static Logger logger { get; set; } = LogManager.GetCurrentClassLogger(); 30 | 31 | public static ADCS GetADCS(SearchResultEntry csEntry) 32 | { 33 | logger.Debug("Collecting ADCS"); 34 | 35 | string enrollServers = null; 36 | List certTemplates = new List(); 37 | List caCertificates = new List(); 38 | DACL acl; 39 | string caHostname = csEntry.Attributes["dnshostname"][0].ToString(); 40 | string caName = csEntry.Attributes["name"][0].ToString(); 41 | string whenCreated = Helper.ConvertWhenCreated(csEntry.Attributes["whencreated"][0].ToString()).ToString(); 42 | 43 | var enrollmentEndpoints = AsyncCollection.TestEnrollmentEndpointsAsync(caName, caHostname).Result; 44 | 45 | PkiCertificateAuthorityFlags flags = (PkiCertificateAuthorityFlags)Enum.Parse(typeof(PkiCertificateAuthorityFlags), csEntry.Attributes["flags"][0].ToString()); 46 | 47 | //The target attribute may not exist 48 | foreach (string attribute in csEntry.Attributes.AttributeNames) 49 | { 50 | if (attribute == "certificatetemplates") 51 | { 52 | foreach (var certTemp in csEntry.Attributes[attribute]) 53 | { 54 | certTemplates.Add(Encoding.UTF8.GetString((byte[])certTemp)); 55 | } 56 | } 57 | if (attribute == "mspki-enrollment-servers") 58 | { 59 | enrollServers = csEntry.Attributes[attribute][0].ToString().Replace("\n", ","); 60 | } 61 | if (attribute == "cacertificate") 62 | { 63 | caCertificates = GetCaCertificate(csEntry.Attributes[attribute]); 64 | } 65 | } 66 | 67 | 68 | bool allowSuppliedSAN = false; 69 | bool usingLDAP; 70 | 71 | var remoteReg = Helper.ReadRemoteReg(caHostname, 72 | RegistryHive.LocalMachine, 73 | $"SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\{caName}\\PolicyModules\\CertificateAuthority_MicrosoftDefault.Policy"); 74 | 75 | //If the remote registry cannot be accessed, using LDAP to retrieve security descriptor instead 76 | usingLDAP = remoteReg == null ? true : false; 77 | 78 | if (usingLDAP) 79 | { 80 | acl = DACL.GetACLOnObject(csEntry.DistinguishedName); 81 | } 82 | else 83 | { 84 | int editFlags = (remoteReg == null) ? 0 : (int)(remoteReg).GetValue("EditFlags"); 85 | allowSuppliedSAN = ((editFlags & 0x00040000) == 0x00040000); 86 | 87 | //Reading DACL from the remote registry, nTSecurityDescriptor from LDAP does not have the necessary information 88 | var regSec = (byte[])(Helper.ReadRemoteReg(caHostname, 89 | RegistryHive.LocalMachine, 90 | $"SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\{caName}")).GetValue("Security"); 91 | 92 | var regSecDescriptor = new ActiveDirectorySecurity(); 93 | regSecDescriptor.SetSecurityDescriptorBinaryForm(regSec, AccessControlSections.All); 94 | 95 | acl = DACL.GetCSACL($"{caHostname}:{RegistryHive.LocalMachine}:SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\{caName}", regSecDescriptor, out _, false); 96 | } 97 | 98 | return new ADCS() 99 | { 100 | flags = flags, 101 | caCertificates = caCertificates, 102 | allowUserSuppliedSAN = allowSuppliedSAN, 103 | CAName = caName, 104 | whenCreated = whenCreated, 105 | dnsHostName = caHostname, 106 | enrollServers = enrollServers, 107 | DACL = acl, 108 | certTemplates = certTemplates, 109 | enrollmentEndpoints = enrollmentEndpoints 110 | }; 111 | 112 | } 113 | 114 | 115 | public static List GetCaCertificate(DirectoryAttribute caCert) 116 | { 117 | var certs = new List(); 118 | foreach (var certBytes in caCert) 119 | { 120 | var cert = new X509Certificate2((byte[])certBytes); 121 | certs.Add(cert); 122 | } 123 | return certs; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /ADCollector3/Objects/CertificateTemplate.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices; 5 | using System.DirectoryServices.Protocols; 6 | using System.Linq; 7 | using System.Security.AccessControl; 8 | using System.Security.Cryptography; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using static ADCollector3.Enums; 12 | using static ADCollector3.Natives; 13 | 14 | namespace ADCollector3 15 | { 16 | public class CertificateTemplate 17 | { 18 | public string TemplateCN; 19 | public string TemplateDisplayName; 20 | public List ExtendedKeyUsage; 21 | public int RaSigature; 22 | public bool IsPublished; 23 | public string PublishedBy; 24 | public msPKICertificateNameFlag CertNameFlag; 25 | public msPKIEnrollmentFlag EnrollFlag; 26 | public DACL DACL; 27 | static Logger logger { get; set; } = LogManager.GetCurrentClassLogger(); 28 | 29 | public static CertificateTemplate GetAllCertTemplates(SearchResultEntry certTemplateResultEntry) 30 | { 31 | var enrollFlag = (msPKIEnrollmentFlag)Enum.Parse(typeof(msPKIEnrollmentFlag), certTemplateResultEntry.Attributes["mspki-enrollment-flag"][0].ToString()); 32 | var raSig = int.Parse(certTemplateResultEntry.Attributes["mspki-ra-signature"][0].ToString()); 33 | var certNameFlag = (msPKICertificateNameFlag)Enum.Parse(typeof(msPKICertificateNameFlag), (unchecked((uint)(Convert.ToInt32(certTemplateResultEntry.Attributes["mspki-certificate-name-flag"][0].ToString())))).ToString()); 34 | List ekus = new List(); 35 | List ekuNames = new List(); 36 | 37 | if (certTemplateResultEntry.Attributes.Contains("pkiextendedkeyusage")) 38 | { 39 | foreach (byte[] eku in certTemplateResultEntry.Attributes["pkiextendedkeyusage"]) 40 | { 41 | string ekuStr = Encoding.UTF8.GetString(eku); 42 | ekus.Add(ekuStr); 43 | ekuNames.Add(new Oid(ekuStr).FriendlyName); 44 | } 45 | } 46 | 47 | return new CertificateTemplate 48 | { 49 | IsPublished = true, 50 | //PublishedBy = "CA",//publishedBy, 51 | CertNameFlag = certNameFlag, 52 | RaSigature = raSig, 53 | EnrollFlag = enrollFlag, 54 | TemplateCN = certTemplateResultEntry.Attributes["cn"][0].ToString(), 55 | TemplateDisplayName = certTemplateResultEntry.Attributes["displayName"][0].ToString(), 56 | ExtendedKeyUsage = ekuNames, 57 | DACL = DACL.GetACLOnObject(certTemplateResultEntry.DistinguishedName)//retrieve the complete DACL instead of interesting ACEs 58 | }; 59 | 60 | } 61 | public static CertificateTemplate GetInterestingCertTemplates(SearchResultEntry certTemplateResultEntry) 62 | { 63 | bool isPublished = false; 64 | string publishedBy = null; 65 | ActiveDirectorySecurity adRights = new ActiveDirectorySecurity(); 66 | 67 | byte[] ldapSecBytes = (byte[])certTemplateResultEntry.Attributes["ntsecuritydescriptor"][0]; 68 | 69 | adRights.SetSecurityDescriptorBinaryForm(ldapSecBytes, AccessControlSections.All); 70 | var acl = DACL.GetCSACL(certTemplateResultEntry.DistinguishedName, 71 | adRights, 72 | out bool hasControlRights, true); 73 | 74 | var enrollFlag = (msPKIEnrollmentFlag)Enum.Parse(typeof(msPKIEnrollmentFlag), certTemplateResultEntry.Attributes["mspki-enrollment-flag"][0].ToString()); 75 | var raSig = int.Parse(certTemplateResultEntry.Attributes["mspki-ra-signature"][0].ToString()); 76 | var certNameFlag = (msPKICertificateNameFlag)Enum.Parse(typeof(msPKICertificateNameFlag), (unchecked((uint)(Convert.ToInt32(certTemplateResultEntry.Attributes["mspki-certificate-name-flag"][0].ToString())))).ToString()); 77 | List ekus = new List(); 78 | List ekuNames = new List(); 79 | 80 | if (certTemplateResultEntry.Attributes.Contains("pkiextendedkeyusage")) 81 | { 82 | foreach (byte[] eku in certTemplateResultEntry.Attributes["pkiextendedkeyusage"]) 83 | { 84 | string ekuStr = Encoding.UTF8.GetString(eku); 85 | ekus.Add(ekuStr); 86 | ekuNames.Add(new Oid(ekuStr).FriendlyName); 87 | } 88 | } 89 | 90 | //If a low priv user has control rights over the templates 91 | if (hasControlRights) 92 | { 93 | foreach (var ca in ADCS.CertificateServices) 94 | { 95 | var certInCa = ca.certTemplates.FirstOrDefault(caCerts => caCerts.Contains(certTemplateResultEntry.Attributes["name"][0].ToString())); 96 | if (certInCa != null) 97 | { 98 | isPublished = true; 99 | publishedBy = ca.CAName; 100 | } 101 | } 102 | return new CertificateTemplate 103 | { 104 | IsPublished = isPublished, 105 | PublishedBy = publishedBy, 106 | CertNameFlag = certNameFlag, 107 | RaSigature = raSig, 108 | EnrollFlag = enrollFlag, 109 | TemplateCN = certTemplateResultEntry.Attributes["cn"][0].ToString(), 110 | TemplateDisplayName = certTemplateResultEntry.Attributes["displayName"][0].ToString(), 111 | ExtendedKeyUsage = ekuNames, 112 | DACL = DACL.GetACLOnObject(certTemplateResultEntry.DistinguishedName)//retrieve the complete DACL instead of interesting ACEs 113 | }; 114 | } 115 | //If a low priv user can enroll 116 | else if (acl != null) 117 | { 118 | logger.Debug("Checking manager approval..."); 119 | //Check if manager approval is enabled 120 | if (!enrollFlag.HasFlag(msPKIEnrollmentFlag.PEND_ALL_REQUESTS)) 121 | { 122 | logger.Debug(certTemplateResultEntry.DistinguishedName); 123 | logger.Debug("Checking authorized signatures..."); 124 | //Check if authorized signatures are required 125 | if (raSig <= 0) 126 | { 127 | logger.Debug(certTemplateResultEntry.DistinguishedName); 128 | logger.Debug("Checking EKUs & ENROLLEE_SUPPLIES_SUBJECT ..."); 129 | //Check if ENROLLEE_SUPPLIES_SUBJECT is enabled and a low priv user can request a cert for authentication 130 | //Check if the template has dangerous EKUs 131 | logger.Debug(certTemplateResultEntry.DistinguishedName); 132 | if ((certNameFlag.HasFlag(msPKICertificateNameFlag.ENROLLEE_SUPPLIES_SUBJECT) && HasAuthenticationEKU(ekus)) || HasDanagerousEKU(ekus)) 133 | { 134 | logger.Debug(certTemplateResultEntry.DistinguishedName); 135 | foreach (var ca in ADCS.CertificateServices) 136 | { 137 | var certInCa = ca.certTemplates.FirstOrDefault(caCerts => caCerts.Contains(certTemplateResultEntry.Attributes["name"][0].ToString())); 138 | 139 | if (certInCa != null) 140 | { 141 | isPublished = true; 142 | publishedBy = ca.CAName; 143 | } 144 | } 145 | if (acl.ACEs.Count != 0) 146 | { 147 | return new CertificateTemplate 148 | { 149 | IsPublished = isPublished, 150 | PublishedBy = publishedBy, 151 | CertNameFlag = certNameFlag, 152 | RaSigature = raSig, 153 | EnrollFlag = enrollFlag, 154 | TemplateCN = certTemplateResultEntry.Attributes["cn"][0].ToString(), 155 | TemplateDisplayName = certTemplateResultEntry.Attributes["displayName"][0].ToString(), 156 | ExtendedKeyUsage = ekuNames, 157 | DACL = DACL.GetACLOnObject(certTemplateResultEntry.DistinguishedName)//retrieve the complete DACL instead of interesting ACEs 158 | }; 159 | } 160 | } 161 | } 162 | } 163 | } 164 | return null; 165 | } 166 | 167 | public static bool HasAuthenticationEKU(List oids) 168 | { 169 | if (!oids.Any()) 170 | { 171 | return false; 172 | } 173 | foreach (var oid in oids) 174 | { 175 | // SmartcardLogon || ClientAuthentication || PKINITClientAuthentication 176 | if (oid == "1.3.6.1.4.1.311.20.2.2" || oid == "1.3.6.1.5.5.7.3.2" || oid == "1.3.6.1.5.2.3.4") { return true; } 177 | } 178 | return false; 179 | } 180 | 181 | public static bool HasDanagerousEKU(List oids) 182 | { 183 | //Empty == AnyPurpose 184 | if (!oids.Any()) 185 | { 186 | return true; 187 | } 188 | foreach (var oid in oids) 189 | { 190 | // AnyPurpose || CertificateRequestAgent 191 | if (oid == "2.5.29.37.0" || oid == "1.3.6.1.4.1.311.20.2.1"){ return true; } 192 | } 193 | return false; 194 | } 195 | 196 | 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /ADCollector3/Objects/FileObject.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ADCollector3 6 | { 7 | public abstract class FileObject 8 | { 9 | public Logger logger; 10 | public string FilePath { get; set; } 11 | public string GPO { get; set; } 12 | public string Content { get; set; } 13 | public Dictionary>> Properties { get; set; } = new Dictionary>>(); 14 | 15 | public FileObject(string filePath) 16 | { 17 | logger = LogManager.GetCurrentClassLogger(); 18 | FilePath = filePath; 19 | string gpoID = FilePath.Split('{')[1].Split('}')[0].ToUpper(); 20 | try 21 | { 22 | 23 | GPO = ADCollector3.GPO.GroupPolicies["{"+ gpoID+"}"] + " {"+ gpoID + "}"; 24 | } 25 | catch { logger.Warn($"GPO GUID {gpoID} Does not Exist"); } 26 | 27 | //ParseFile(); 28 | //if (HasCondition()) { ParseFile(); } 29 | } 30 | 31 | public abstract void ParseFile(); 32 | //public virtual bool HasCondition() { return true; } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ADCollector3/Objects/GPO.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices.Protocols; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | 10 | namespace ADCollector3 11 | { 12 | public class GPO 13 | { 14 | private static Logger _logger { get; set; } = LogManager.GetCurrentClassLogger(); 15 | public static Dictionary WMIPolicies = new Dictionary(); 16 | public static Dictionary GroupPolicies = new Dictionary(); 17 | public string OU { get; set; } 18 | public string Name { get; set; } 19 | public string GUID { get; set; } 20 | public bool IsEnforced { get; set; } 21 | 22 | public GPO() 23 | { 24 | 25 | } 26 | 27 | public static void GetAllGPOs() 28 | { 29 | GetWMIPolicies(); 30 | 31 | _logger.Debug("Collecting GPOs"); 32 | Regex filterRx = new Regex(@";(\{.+?\});", RegexOptions.Compiled); 33 | 34 | string gpoRootDN = "CN=Policies,CN=System," + Searcher.LdapInfo.RootDN;//"CN=System," + rootDn; 35 | string gpoForestDN = "CN=Policies,CN=System," + Searcher.LdapInfo.ForestDN; 36 | string[] gpoDNs = (Searcher.LdapInfo.RootDN != Searcher.LdapInfo.ForestDN) ? new string[] { gpoRootDN, gpoForestDN } : new string[] { gpoRootDN }; 37 | 38 | string gpoFilter = @"(objectCategory=groupPolicyContainer)"; 39 | string[] gpoAttrs = { "displayName", "cn", "gPCWQLFilter", "nTSecurityDescriptor" }; 40 | //string extrightsDn = "CN=Extended-Rights,CN=Configuration," + forestDn; 41 | 42 | try 43 | { 44 | foreach (string gpodn in gpoDNs) 45 | { 46 | _logger.Debug($"Enumerateing GPOs in {gpodn}"); 47 | 48 | var gpoEntries = Searcher.GetResultEntries(new LDAPSearchString {DN = gpodn, Filter = gpoFilter, ReturnAttributes = gpoAttrs, Scope = SearchScope.Subtree }).ToList(); 49 | 50 | foreach (var entry in gpoEntries) 51 | { 52 | string dn = entry.Attributes["cn"][0].ToString().ToUpper(); 53 | string displayname = entry.Attributes["displayName"][0].ToString().ToUpper(); 54 | 55 | //WMI Filtering 56 | if (entry.Attributes.Contains("gPCWQLFilter")) 57 | { 58 | string filterAttr = entry.Attributes["gPCWQLFilter"][0].ToString(); 59 | //Could be empty " " 60 | if (filterAttr.Length > 2) 61 | { 62 | Match filterM = filterRx.Match(filterAttr); 63 | string filter = filterM.Groups[1].ToString(); 64 | string wmiName = WMIPolicies[filter]; 65 | displayname += " [EvaluateWMIPolicy: " + wmiName + " - " + filter + "]"; 66 | } 67 | } 68 | if (!GroupPolicies.ContainsKey(dn)) { GroupPolicies.Add(dn, displayname); } 69 | } 70 | } 71 | } 72 | catch (Exception e) 73 | { 74 | _logger.Error(e.Message); 75 | } 76 | } 77 | 78 | 79 | 80 | public static void GetWMIPolicies() 81 | { 82 | _logger.Debug("Collecting WMI Policies"); 83 | 84 | string wmiRootDn = "CN=SOM,CN=WMIPolicy,CN=System," + Searcher.LdapInfo.RootDN; 85 | string wmiForestDn = "CN=SOM,CN=WMIPolicy,CN=System," + Searcher.LdapInfo.ForestDN; 86 | string[] wmiDNs = (Searcher.LdapInfo.RootDN != Searcher.LdapInfo.ForestDN) ? new string[] { wmiRootDn, wmiForestDn } : new string[] { wmiRootDn }; 87 | 88 | string wmiFilter = @"(objectClass=msWMI-Som)"; 89 | string[] wmiAttrs = { "msWMI-Name", "msWMI-ID"}; 90 | 91 | foreach(var wmiDn in wmiDNs) 92 | { 93 | var resultEntries = Searcher.GetResultEntries(new LDAPSearchString { DN = wmiDn, Filter = wmiFilter, ReturnAttributes = wmiAttrs, Scope = SearchScope.Subtree }).ToList(); 94 | 95 | foreach (var entry in resultEntries) 96 | { 97 | WMIPolicies.Add(entry.Attributes["msWMI-ID"][0].ToString().ToUpper(), entry.Attributes["msWMI-Name"][0].ToString()); 98 | } 99 | } 100 | } 101 | 102 | 103 | public static List GetAllGPODNList() 104 | { 105 | return GroupPolicies.Keys.Select(gpo => $"CN={gpo},CN=Policies,CN=System,{Searcher.LdapInfo.RootDN}").ToList(); 106 | } 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ADCollector3/Objects/ILDAPObject.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace ADCollector3 4 | { 5 | public interface ILDAPObject 6 | { 7 | string DistinguishedName { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ADCollector3/Objects/INFObject.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace ADCollector3 8 | { 9 | public class INFObject : FileObject 10 | { 11 | public INFObject(string filePath) : base(filePath) { } 12 | public static Dictionary EnumeratedINFObjects { get; set; } = new Dictionary(); 13 | 14 | public override void ParseFile() 15 | { 16 | try 17 | { 18 | //logger.Debug($"Reading {FilePath}..."); 19 | Content = File.ReadAllText(FilePath); 20 | } 21 | catch 22 | { 23 | logger.Warn($"Unable to parse {FilePath}..."); 24 | return; 25 | } 26 | 27 | string content = Regex.Replace(Content, @"^\s*;.*$", "", RegexOptions.Multiline); 28 | 29 | //^\[(.+?)\](\r\n.*)+ 30 | //char[] removeSquare = new char[] { '[', ']' }; 31 | 32 | foreach (var section in Regex.Split(content, @"\r\n\[")) 33 | { 34 | 35 | var tempSection = section + "\r\n"; 36 | 37 | Dictionary lines = new Dictionary(); 38 | 39 | var sectionName = (Regex.Match(tempSection, @"(.*?)\]\r\n", RegexOptions.Singleline)).Value.Trim('\r', '\n', '[', ']'); 40 | 41 | foreach (Match m in Regex.Matches(tempSection, @"^\s*(.*?)\s*=(\s(.*?)\s)|(\r\n)$", RegexOptions.Multiline)) 42 | { 43 | string key = m.Groups[1].Value.Trim(' ', '\r', '\n'); 44 | string value = m.Groups[2].Value.Trim(' ', '\r', '\n'); 45 | 46 | if (!lines.ContainsKey(key) && (!string.IsNullOrEmpty(key))) { lines[key] = value; } 47 | } 48 | 49 | if (!Properties.ContainsKey(sectionName)) { Properties[sectionName] = new List> { lines }; } 50 | } 51 | 52 | EnumeratedINFObjects.Add(FilePath, new INFObject(FilePath) { Properties = Properties}); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ADCollector3/Objects/LDAPBaseObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ADCollector3 9 | { 10 | public class LDAPBaseObject : ILDAPObject 11 | { 12 | public string DistinguishedName { get; set; } 13 | public Dictionary> Attributes { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ADCollector3/Objects/OtherFileObject.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace ADCollector3 8 | { 9 | public class OtherFileObject : FileObject 10 | { 11 | public OtherFileObject(string filePath) : base(filePath) { } 12 | 13 | public override void ParseFile() 14 | { 15 | try 16 | { 17 | logger.Debug($"Reading {FilePath}..."); 18 | Content = File.ReadAllText(FilePath); 19 | } 20 | catch 21 | { 22 | logger.Warn($"Unable to parse {FilePath}..."); 23 | return; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ADCollector3/Objects/Trust.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Runtime.Serialization; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using static ADCollector3.Enums; 10 | using static ADCollector3.Natives; 11 | 12 | namespace ADCollector3 13 | { 14 | class Trust 15 | { 16 | public string SourceDomainName; 17 | public string TargetDomainName; 18 | public string NetBIOSName; 19 | public bool IsTransitive; 20 | public bool FilteringSID; 21 | public TrustDirection TrustDirection; 22 | public TrustType TrustType; 23 | private List Trusts { get; set; } = new List(); 24 | private static Logger _logger; 25 | public Trust() 26 | { 27 | _logger = LogManager.GetCurrentClassLogger(); 28 | SourceDomainName = Searcher.LdapInfo.DomainName; 29 | } 30 | 31 | 32 | public List AnalyzeTrust(DS_DOMAIN_TRUSTS[] trustResults) 33 | { 34 | if (trustResults == null) { return null; } 35 | 36 | DS_DOMAIN_TRUSTS currentDomain = new DS_DOMAIN_TRUSTS(); 37 | 38 | foreach (var domain in trustResults) 39 | { 40 | if (domain.DnsDomainName.ToUpper() == SourceDomainName.ToUpper()) 41 | { 42 | currentDomain = domain; 43 | break; 44 | } 45 | } 46 | 47 | foreach (var trust in trustResults) 48 | { 49 | var dnsDomainName = trust.DnsDomainName; 50 | 51 | if (dnsDomainName.ToUpper() == SourceDomainName.ToUpper()) { continue; } 52 | 53 | var trustAttributes = (TrustAttributes)trust.TrustAttributes; 54 | var trustFlags = (TrustFlags)trust.Flags; 55 | var netbiosName = trust.NetbiosDomainName; 56 | //var domainSid = (new SecurityIdentifier(trust.DomainSid)).ToString(); 57 | bool sidFiltering = trustAttributes.HasFlag(TrustAttributes.FilterSids) ? true : false; 58 | bool isTransitive = trustAttributes.HasFlag(TrustAttributes.NonTransitive) ? false : true; 59 | 60 | TrustDirection trustDirection; 61 | 62 | if (trustFlags.HasFlag(TrustFlags.DirectInBound) && trustFlags.HasFlag(TrustFlags.DirectOutBound)) 63 | { 64 | trustDirection = TrustDirection.BiDirectional; 65 | 66 | } 67 | else if (trustFlags.HasFlag(TrustFlags.DirectInBound)) 68 | { 69 | trustDirection = TrustDirection.InBound; 70 | } 71 | else if (trustFlags.HasFlag(TrustFlags.DirectOutBound)) 72 | { 73 | trustDirection = TrustDirection.OutBound; 74 | } 75 | else 76 | { 77 | trustDirection = TrustDirection.Disable; 78 | } 79 | 80 | TrustType trustType; 81 | 82 | //If the target domain is the current tree root or if target domain is a child domain of the current domain 83 | if ((trustFlags.HasFlag(TrustFlags.TreeRoot) && 84 | trustFlags.HasFlag(TrustFlags.InForest) && 85 | (currentDomain.DnsDomainName.ToUpper().Contains(dnsDomainName.ToUpper()))) || 86 | (trustResults[trust.ParentIndex].DnsDomainName.ToUpper() == SourceDomainName.ToUpper())) 87 | { 88 | trustType = TrustType.ParentChild; 89 | } 90 | else if (trustFlags.HasFlag(TrustFlags.TreeRoot) && trustFlags.HasFlag(TrustFlags.InForest)) 91 | { 92 | trustType = TrustType.TreeRoot; 93 | } 94 | else if (trustFlags.HasFlag(TrustFlags.InForest)) 95 | { 96 | trustType = TrustType.ShortCut; 97 | } 98 | else if (trustAttributes.HasFlag(TrustAttributes.ForestTransitive)) 99 | { 100 | trustType = TrustType.Forest; 101 | } 102 | else 103 | { 104 | trustType = TrustType.External; 105 | } 106 | 107 | 108 | Trusts.Add(new Trust() 109 | { 110 | SourceDomainName = SourceDomainName, 111 | NetBIOSName = netbiosName, 112 | TargetDomainName = dnsDomainName, 113 | //DomainSid = domainSid, 114 | IsTransitive = isTransitive, 115 | TrustDirection = trustDirection, 116 | TrustType = trustType, 117 | FilteringSID = sidFiltering 118 | }); 119 | } 120 | 121 | return Trusts; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ADCollector3/Objects/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.DirectoryServices.Protocols; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ADCollector3 9 | { 10 | public class User : LDAPBaseObject 11 | { 12 | public Dictionary> Properities { get; set; } 13 | 14 | public static User GetUserObject(SearchResultEntry resultEntry) 15 | { 16 | User user = new User { DistinguishedName = resultEntry.DistinguishedName, Properities = new Dictionary>() }; 17 | 18 | foreach (string attrName in resultEntry.Attributes.AttributeNames) 19 | { 20 | DirectoryAttribute attr = resultEntry.Attributes[attrName]; 21 | List attrList = new List(); 22 | 23 | if (attr[0] is string) 24 | { 25 | for (int i = 0; i < attr.Count; i++) 26 | { 27 | attrList.Add(attr[i].ToString()); 28 | } 29 | } 30 | else if (attr[0] is byte[]) 31 | { 32 | if (IsSecurityDescriptorAttribute(attrName)) 33 | { 34 | attrList = Helper.ConvertSecurityDescriptor(attr); 35 | } 36 | else 37 | { 38 | attrList = Helper.ConvertByteArrayToSID(attr); 39 | } 40 | } 41 | user.Properities.Add(attrName, attrList); 42 | } 43 | return user; 44 | } 45 | 46 | public static bool IsSecurityDescriptorAttribute(string attribute) 47 | { 48 | string[] attributes = new string[] { "msds-allowedtoactonbehalfofotheridentity" }; 49 | 50 | return attributes.Contains(attribute.ToLower()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ADCollector3/Objects/XMLObject.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Xml; 6 | 7 | namespace ADCollector3 8 | { 9 | public class XMLObject : FileObject 10 | { 11 | public XMLObject(string filePath) : base(filePath) { } 12 | public XmlDocument Doc { get; set; } = new XmlDocument(); 13 | 14 | 15 | public override void ParseFile() 16 | { 17 | logger.Debug($"Parsing {FilePath}..."); 18 | 19 | try 20 | { 21 | Doc.Load(FilePath); 22 | Content = Doc.InnerXml; 23 | } 24 | catch 25 | { 26 | logger.Warn($"Unable to access {FilePath}"); 27 | } 28 | 29 | string xmlNode = GetXMLNode(FilePath.Split('\\').Last()); 30 | 31 | if (xmlNode == null) { return; } 32 | XmlNodeList nodes = Doc.DocumentElement.SelectNodes(xmlNode); 33 | List> attrsList = new List>(); 34 | try 35 | { 36 | foreach (XmlNode node in nodes) 37 | { 38 | Dictionary attrs = new Dictionary(); 39 | foreach (XmlAttribute attr in node.Attributes) 40 | { 41 | attrs.Add(attr.Name, attr.Value); 42 | } 43 | attrsList.Add(attrs); 44 | } 45 | }catch(Exception e) 46 | { 47 | Console.WriteLine(e.Message + FilePath); 48 | } 49 | //foreach (XmlNode node in nodes) 50 | //{ 51 | // foreach (XmlAttribute attr in node.Attributes) 52 | // { 53 | // attrs.Add(attr.Name, attr.Value); 54 | // } 55 | //} 56 | 57 | Properties.Add(FilePath.Split('\\').Last(), attrsList); 58 | } 59 | 60 | 61 | 62 | //https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Get-GPPPassword.ps1 63 | //Search for groups.xml, scheduledtasks.xml, services.xml, datasources.xml, printers.xml and drives.xml 64 | //findstr /S /I cpassword \\\sysvol\\policies\*.xml 65 | public static string GetXMLNode(string file) 66 | { 67 | var gppDict = new Dictionary(); 68 | gppDict.Add("Groups.xml", "/Groups/User/Properties"); 69 | gppDict.Add("Services.xml", "/NTServices/NTService/Properties"); 70 | gppDict.Add("Scheduledtasks.xml", "/ScheduledTasks/Task/Properties"); 71 | gppDict.Add("Datasources.xml", "/DataSources/DataSource/Properties"); 72 | gppDict.Add("Printers.xml", "/Printers/SharedPrinter/Properties"); 73 | gppDict.Add("Drives.xml", "/Drives/Drive/Properties"); 74 | 75 | if (gppDict.ContainsKey(file)) 76 | { 77 | return gppDict[file]; 78 | } 79 | return null; 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ADCollector3/Options.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | 3 | namespace ADCollector3 4 | { 5 | public class Options 6 | { 7 | public static Options Instance { get; set; } 8 | 9 | [Option("Domain", Default = null, HelpText = "Domain to enumerate")] 10 | public string Domain { get; set; } 11 | 12 | [Option("LDAPS", Default = false, HelpText = "LDAP over SSL/TLS")] 13 | public bool Ldaps { get; set; } 14 | 15 | [Option("DisableSigning", Default = false, HelpText = "Disable Kerberos Encryption (with -LDAPS flag)")] 16 | public bool DisableSigning { get; set; } 17 | 18 | [Option("UserName", Default = null, HelpText = "Alternative UserName")] 19 | public string Username { get; set; } 20 | 21 | [Option("Password", Default = null, HelpText = "Alternative Credential")] 22 | public string Password { get; set; } 23 | 24 | [Option("DC", Default = null, HelpText = "Alternative Domain Controller (Hostname/IP) to connect to")] 25 | public string DC { get; set; } 26 | 27 | [Option("OU", Default = null, HelpText = "Perform the Search under a specific Organizational Unit")] 28 | public string OU { get; set; } 29 | 30 | [Option("LDAPONLY", Default = false, HelpText = "Only Enumerate LDAP")] 31 | public bool LDAPONLY { get; set; } 32 | 33 | [Option("ACLScan", Default = null, HelpText = "Perform ACL scan for an Identity")] 34 | public string ACLScan { get; set; } 35 | 36 | [Option("ADCS", Default = false, HelpText = "Only Perform AD Certificate Service Check")] 37 | public bool ADCS { get; set; } 38 | 39 | [Option("TEMPLATES", Default = false, HelpText = "Only Enumerate All AD Certificate Templates")] 40 | public bool TEMPLATES { get; set; } 41 | 42 | [Option("ADIDNS", Default = false, HelpText = "Only Collect ADIDNS Records")] 43 | public bool ADIDNS { get; set; } 44 | 45 | [Option("NGAGP", Default = null, HelpText = "Only enumerate Nested Group Membership and Applied Group Policies on the target object")] 46 | public string NGAGP { get; set; } 47 | 48 | [Option("SCHEMA", Default = false, HelpText = "Count all attributes in the Schema")] 49 | public bool SCHEMA { get; set; } 50 | 51 | [Option("DACL", Default = null, HelpText = "Enumerate DACL on the target object (with DistinguishedName)")] 52 | public string DACL { get; set; } 53 | 54 | [Option("SessionEnum", Default = false, HelpText = "Enumerate session information on the target host")] 55 | public bool SessionEnum { get; set; } 56 | 57 | [Option("UserEnum", Default = false, HelpText = "Enumerate user information on the target host")] 58 | public bool UserEnum { get; set; } 59 | 60 | [Option("LocalGMEnum", Default = false, HelpText = "Enumerate local group members on the target host")] 61 | public bool LocalGMEnum { get; set; } 62 | 63 | [Option("Host", Default = "Localhost", HelpText = "Hostname for Session/User/Groupmember Enumeration")] 64 | public string Host { get; set; } 65 | 66 | [Option("Group", Default = "Administrators", HelpText = "Local Group Name for Local GroupMember Enumeration")] 67 | public string Group { get; set; } 68 | 69 | [Option("Debug", Default = false, HelpText = "Debug Mode")] 70 | public bool Debug { get; set; } 71 | 72 | public static void GetHelp() 73 | { 74 | var help = @" 75 | --Domain Domain to enumerate 76 | --LDAPS (Default: false) LDAP over SSL/TLS 77 | --DisableSigning (Default: false) Disable Kerberos Encryption (with -LDAPS flag) 78 | --UserName Alternative UserName 79 | --Password Alternative Credential 80 | --DC Alternative Domain Controller (Hostname/IP) to connect to 81 | --OU Perform the Search under a specific Organizational Unit 82 | --LDAPONLY Only Enumearte Objects in LDAP 83 | --ACLScan Perform ACL scan for an Identity 84 | --ADCS (Default: false) Only Perform AD Certificate Service Check 85 | --TEMPLATES (Default: false) Only Enumerate All Certificate Templates with their DACL 86 | --SCHEMA (Default: false) Count Schema Attributes in the default naming context 87 | --ADIDNS (Default: false) Only Collect ADIDNS Records 88 | --NGAGP Only enumerate Nested Group Membership and Applied Group Policies on the target object 89 | --DACL Enumerate DACL on the target object (with DistinguishedName) 90 | --SessionEnum (Default: false) Enumerate session information on the target host 91 | --UserEnum (Default: false) Enumerate user information on the target host 92 | --LocalGMEnum (Default: false) Enumerate local group members on the target host 93 | --Host (Default: Localhost) Hostname for Session/User/Groupmember Enumeration 94 | --Group (Default: Administrators) Local Group Name for Local GroupMember Enumeration 95 | --Debug (Default: false) Debug Mode 96 | --help Display this help screen. 97 | 98 | Example: .\ADCollector.exe 99 | .\ADCollector.exe --LDAPs --DisableSigning 100 | .\ADCollector.exe --OU IT 101 | .\ADCollector.exe --OU OU=IT,DC=domain,DC=local 102 | .\ADCollector.exe --ADCS 103 | .\ADCollector.exe --TEMPLATES 104 | .\ADCollector.exe --LDAPOnly 105 | .\ADCollector.exe --SCHEMA 106 | .\ADCollector.exe --ADIDNS 107 | .\ADCollector.exe --NGAGP samaccountname 108 | .\ADCollector.exe --DACL DC=domain,DC=net 109 | .\ADCollector.exe --ACLScan user --OU OU=IT,DC=domain,DC=local 110 | .\ADCollector.exe --SessionEnum --Host targetHost 111 | .\ADCollector.exe --UserEnum --Host targetHost 112 | .\ADCollector.exe --LocalGMEnum --Host targetHost --Group 'Remote Desktop Users' 113 | .\ADCollector.exe --Domain domain.local --Username user --Password pass 114 | .\ADCollector.exe --Domain domain.local --DC 10.10.10.1 115 | "; 116 | System.Console.WriteLine(help); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /ADCollector3/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using CommandLine; 4 | 5 | namespace ADCollector3 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | DisplayUtil.PrintBanner(); 12 | 13 | var parser = new Parser(with => 14 | { 15 | with.CaseInsensitiveEnumValues = true; 16 | with.CaseSensitive = false; 17 | with.HelpWriter = null; 18 | }); 19 | 20 | parser.ParseArguments(args).WithParsed(o => {Options.Instance = o; }).WithNotParsed(error => { }); 21 | parser.Dispose(); 22 | 23 | var options = Options.Instance; 24 | if (options == null) { Options.GetHelp(); return; } 25 | 26 | Logging.LoadLoggingConfig(options.Debug); 27 | 28 | if (options.Username != null) 29 | { 30 | Impersonation.RunAs(options.Domain, options.Username, options.Password, () => 31 | { 32 | ChooseOption(options); 33 | }); 34 | } 35 | else{ ChooseOption(options); } 36 | 37 | 38 | DisplayUtil.Done(); 39 | } 40 | 41 | public static void ChooseOption(Options options) 42 | { 43 | if (options.SessionEnum) 44 | { 45 | ADCollector.GetHostSession(options.Host); 46 | } 47 | else if (options.UserEnum) 48 | { 49 | ADCollector.GetHostUser(options.Host); 50 | } 51 | else if (options.LocalGMEnum) 52 | { 53 | ADCollector.GetHostGroupMember(options.Host, options.Group); 54 | } 55 | else 56 | { 57 | var adcollector = new ADCollector(); 58 | if (options.LDAPONLY) 59 | { 60 | adcollector.GetLDAPOnly(); 61 | } 62 | else if (options.SCHEMA) 63 | { 64 | adcollector.GetSchemaCount(); 65 | } 66 | else if (options.TEMPLATES) 67 | { 68 | adcollector.GetTemplates(); 69 | } 70 | else if (options.ADCS) 71 | { 72 | adcollector.GetADCS(); 73 | } 74 | else if (options.ADIDNS) 75 | { 76 | adcollector.GetADIDNS(); 77 | } 78 | else if (options.NGAGP != null) 79 | { 80 | adcollector.GetNGAGP(new List { options.NGAGP }); 81 | } 82 | else if (options.DACL != null) 83 | { 84 | adcollector.GetACL(options.DACL); 85 | } 86 | else if (options.ACLScan != null) 87 | { 88 | adcollector.InvokeACLScan(options.ACLScan); 89 | } 90 | else 91 | { 92 | adcollector.Run(); 93 | } 94 | } 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /ADCollector3/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ADCollector3")] 9 | [assembly: AssemblyDescription("by dev2null")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ADCollector3")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d1ae1acf-8aa2-4935-acdf-ec22bae2df76")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("3.0.0")] 36 | [assembly: AssemblyFileVersion("3.0.0")] 37 | -------------------------------------------------------------------------------- /ADCollector3/Results/DACLResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DACLResult : IResult 6 | { 7 | public string Title { get; set; } 8 | public List Result { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ADCollector3/Results/DDResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DDResult : IResult 6 | { 7 | public string Title { get; set; } 8 | public Dictionary> Result { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ADCollector3/Results/DLResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public class DLResult : IResult 6 | { 7 | public string Title { get; set; } 8 | public List>> Result { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ADCollector3/Results/FileResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ADCollector3 8 | { 9 | public class FileResult: IResult 10 | { 11 | public string Title { get; set ; } 12 | public FileObject FileObject { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ADCollector3/Results/IResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public interface IResult 6 | { 7 | string Title { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ADCollector3/Results/LDAPResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public class LDAPResult : IResult 6 | { 7 | public string Title { get; set; } 8 | public List LDAPObjects { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ADCollector3/Results/ListResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public class ListResult : IResult 6 | { 7 | public string Title { get; set; } 8 | public List Result { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ADCollector3/Rights.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices.Protocols; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ADCollector3 10 | { 11 | public static class Rights 12 | { 13 | public static Dictionary ExtendedRightsList { get; set; } = new Dictionary(); 14 | public static Dictionary SchemaList { get; set; } = new Dictionary(); 15 | 16 | static Logger _logger { get; set; } = LogManager.GetCurrentClassLogger(); 17 | 18 | public static void BuildExtendedRightsDict() 19 | { 20 | _logger.Debug("Building an Extended Rights List"); 21 | string extendedRightsDn = "CN=Extended-Rights," + Searcher.LdapInfo.ConfigDN; 22 | 23 | var rightsResult = Searcher.GetResultEntries(new LDAPSearchString 24 | { 25 | DN = extendedRightsDn, 26 | Filter = "(rightsGuid=*)", 27 | ReturnAttributes = new string[] { "rightsGuid", "cn" }, 28 | Scope = SearchScope.Subtree, 29 | //UseGlobalCatalog = true 30 | }).ToList(); 31 | 32 | foreach (var rights in rightsResult) 33 | { 34 | //Ignore duplicated rightsGuid DNS-Host-Name-Attributes & Validated-DNS-Host-Name: "72e39547-7b18-11d1-adef-00c04fd8d5cd" 35 | string rightsGuid = rights.Attributes["rightsGuid"][0].ToString().ToLower(); 36 | 37 | if (rightsGuid == "72e39547-7b18-11d1-adef-00c04fd8d5cd") { continue; } 38 | 39 | ExtendedRightsList.Add(rightsGuid, rights.Attributes["cn"][0].ToString()); 40 | 41 | } 42 | ExtendedRightsList.Add("72e39547-7b18-11d1-adef-00c04fd8d5cd", "DNS-Host-Name-Attributes & Validated-DNS-Host-Name"); 43 | ExtendedRightsList.Add("aa4e1a6d-550d-4e05-8c35-4afcb917a9fe", "ms-TPM-OwnerInformation"); 44 | ExtendedRightsList.Add("00000000-0000-0000-0000-000000000000", "All"); 45 | } 46 | 47 | 48 | 49 | public static void BuildSchemaDict() 50 | { 51 | _logger.Debug("Building an Schema List"); 52 | 53 | var rightsResult = Searcher.GetResultEntries(new LDAPSearchString 54 | { 55 | DN = Searcher.LdapInfo.SchemaDN, 56 | Filter = "(schemaIDGUID=*)", 57 | ReturnAttributes = new string[] { "schemaIDGUID", "cn" }, 58 | Scope = SearchScope.OneLevel, 59 | //UseGlobalCatalog = true 60 | }).ToList(); 61 | 62 | foreach (var rights in rightsResult) 63 | { 64 | string schemaGUID = Helper.GetStringFromByte((byte[])rights.Attributes["schemaIDGUID"][0]).ToLower(); 65 | 66 | SchemaList.Add(schemaGUID, rights.Attributes["cn"][0].ToString()); 67 | } 68 | SchemaList.Add("00000000-0000-0000-0000-000000000000", "All properties"); 69 | // Active Directory includes predefined property sets: 70 | // https://docs.microsoft.com/en-us/windows/desktop/adschema/property-sets 71 | var predefinedProp = new Dictionary(); 72 | predefinedProp.Add("72e39547-7b18-11d1-adef-00c04fd8d5cd", "DNS Host Name Attributes"); 73 | predefinedProp.Add("b8119fd0-04f6-4762-ab7a-4986c76b3f9a", "Other Domain Parameters"); 74 | predefinedProp.Add("c7407360-20bf-11d0-a768-00aa006e0529", "Domain Password and Lockout Policies"); 75 | predefinedProp.Add("e45795b2-9455-11d1-aebd-0000f80367c1", "Phone and Mail Options"); 76 | predefinedProp.Add("59ba2f42-79a2-11d0-9020-00c04fc2d3cf", "General Information"); 77 | predefinedProp.Add("bc0ac240-79a9-11d0-9020-00c04fc2d4cf", "Group Membership"); 78 | predefinedProp.Add("ffa6f046-ca4b-4feb-b40d-04dfee722543", "MS-TS-GatewayAccess"); 79 | predefinedProp.Add("77b5b886-944a-11d1-aebd-0000f80367c1", "Personal Information"); 80 | predefinedProp.Add("91e647de-d96f-4b70-9557-d63ff4f3ccd8", "Private Information"); 81 | predefinedProp.Add("e48d0154-bcf8-11d1-8702-00c04fb96050", "Public Information"); 82 | predefinedProp.Add("5805bc62-bdc9-4428-a5e2-856a0f4c185e", "Terminal Server License Server"); 83 | predefinedProp.Add("4c164200-20c0-11d0-a768-00aa006e0529", "Account Restrictions"); 84 | predefinedProp.Add("5f202010-79a5-11d0-9020-00c04fc2d4cf", "Logon Information"); 85 | predefinedProp.Add("e45795b3-9455-11d1-aebd-0000f80367c1", "Web Information"); 86 | predefinedProp.Add("9b026da6-0d3c-465c-8bee-5199d7165cba", "DS-Validated-Write-Computer"); 87 | predefinedProp.Add("037088f8-0ae1-11d2-b422-00a0c968f939", "RAS-Information"); 88 | foreach(var prop in predefinedProp) 89 | { 90 | if (!SchemaList.ContainsKey(prop.Key)) 91 | { 92 | SchemaList.Add(prop.Key, prop.Value); 93 | } 94 | } 95 | 96 | } 97 | 98 | //It does not work well with Task 99 | public static string ResolveRightsGuid(string rightsGuid, bool isExtendedRights = true) 100 | { 101 | if (isExtendedRights) 102 | { 103 | if (ExtendedRightsList.ContainsKey(rightsGuid.ToLower())) 104 | { 105 | return ExtendedRightsList[rightsGuid.ToLower()]; 106 | } 107 | //ms-TPM-OwnerInformation:aa4e1a6d-550d-4e05-8c35-4afcb917a9fe (this is a schema attribute...) 108 | else 109 | { 110 | _logger.Warn($"{rightsGuid} is extended rights but cannot be resolved"); 111 | return rightsGuid; 112 | } 113 | } 114 | else 115 | { 116 | if (SchemaList.ContainsKey(rightsGuid.ToLower())) 117 | { 118 | return SchemaList[rightsGuid.ToLower()]; 119 | } 120 | else 121 | { 122 | _logger.Warn($"{rightsGuid} is a schema attribute but cannot be resolved"); 123 | return rightsGuid; 124 | } 125 | } 126 | 127 | //string partition = isExtendedRights ? "CN=Extended-Rights,CN=Configuration," : "CN=Schema,CN=Configuration,"; 128 | //string partition = "CN=Schema,CN=Configuration,"; 129 | 130 | //No SPACE near "=" 131 | //From The .Net Developer Guide to Directory Services Programming Searching for Binary Data 132 | 133 | //resolve schema attributes / extended rights 134 | //string searchFilter = isExtendedRights ? @"(rightsGuid=" + rightsGuid + @")" : 135 | // @"(schemaIDGUID=" + BuildFilterOctetString(new Guid(rightsGuid).ToByteArray()) + @")"; 136 | 137 | } 138 | 139 | 140 | 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /ADCollector3/SearchString/AppliedGPOSearchString.cs: -------------------------------------------------------------------------------- 1 | namespace ADCollector3 2 | { 3 | public class AppliedGPOSearchString : SAMAccountNameSearchString 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ADCollector3/SearchString/LDAPSearchString.cs: -------------------------------------------------------------------------------- 1 | using System.DirectoryServices.Protocols; 2 | 3 | namespace ADCollector3 4 | { 5 | public class LDAPSearchString : SearchString 6 | { 7 | public string Title { get; set; } 8 | public string DN { get; set; } 9 | public string Filter { get; set; } 10 | public string[] ReturnAttributes { get; set; } 11 | public SearchScope Scope { get; set; } 12 | public bool UseGlobalCatalog { get; set; } 13 | public int PageSize = 500; 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ADCollector3/SearchString/NestedGMSearchString.cs: -------------------------------------------------------------------------------- 1 | namespace ADCollector3 2 | { 3 | public class NestedGMSearchString : SAMAccountNameSearchString 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ADCollector3/SearchString/SAMAccountNameSearchString.cs: -------------------------------------------------------------------------------- 1 | namespace ADCollector3 2 | { 3 | public class SAMAccountNameSearchString : SearchString 4 | { 5 | public string Title { get; set; } 6 | 7 | public string SAMAccountName { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ADCollector3/SearchString/SMBSearchString.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ADCollector3 4 | { 5 | public class SMBSearchString : SearchString 6 | { 7 | public string Title { get; set; } 8 | public string FilePath { get; set; } 9 | public List FilePathList { get; set; } 10 | 11 | //filePath: sections/properties 12 | public List FileAttributes { get; set; } 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ADCollector3/SearchString/SearchString.cs: -------------------------------------------------------------------------------- 1 | namespace ADCollector3 2 | { 3 | public interface SearchString 4 | { 5 | string Title { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ADCollector3/Utilities/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ADCollector3 8 | { 9 | public class Enums 10 | { 11 | [Flags] 12 | public enum GPO_LIST_FLAG 13 | { 14 | /// The gpo list flag machine 15 | GPO_LIST_FLAG_MACHINE = 0x00000001, 16 | /// The gpo list flag siteonly 17 | GPO_LIST_FLAG_SITEONLY = 0x00000002, 18 | /// Ignore WMI filters when filtering GPO's 19 | GPO_LIST_FLAG_NO_WMIFILTERS = 0x00000004, 20 | /// Ignore security filters 21 | GPO_LIST_FLAG_NO_SECURITYFILTERS = 0x00000008 22 | } 23 | 24 | 25 | 26 | 27 | [Flags] 28 | public enum Functionality 29 | { 30 | DS_BEHAVIOR_WIN2000 = 0, 31 | DS_BEHAVIOR_WIN2003_WITH_MIXED_DOMAINS = 1, 32 | DS_BEHAVIOR_WIN2003 = 2, 33 | DS_BEHAVIOR_WIN2008 = 3, 34 | DS_BEHAVIOR_WIN2008R2 = 4, 35 | DS_BEHAVIOR_WIN2012 = 5, 36 | DS_BEHAVIOR_WIN2012R2 = 6, 37 | DS_BEHAVIOR_WIN2016 = 7 38 | } 39 | 40 | 41 | 42 | 43 | //userAccountControl attribute ([MS-ADTS] section 2.2.16) TD flag 44 | [Flags] 45 | public enum UACFlags 46 | { 47 | SCRIPT = 0x1, 48 | ACCOUNT_DISABLE = 0x2, 49 | HOMEDIR_REQUIRED = 0x8, 50 | LOCKOUT = 0x10, 51 | PASSWD_NOTREQD = 0x20, 52 | PASSWD_CANT_CHANGE = 0x40, 53 | ENCRYPTED_TEXT_PASSWORD_ALLOWED = 0x80, 54 | NORMAL_ACCOUNT = 0x200, 55 | INTERDOMAIN_TRUST_ACCOUNT = 0x800, 56 | WORKSTATION_TRUST_ACCOUNT = 0x1000, 57 | SERVER_TRUST_ACCOUNT = 0x2000, 58 | DONT_EXPIRE_PASSWD = 0x10000, 59 | SMARTCART_REQUIRED = 0x40000, 60 | TRUSTED_FOR_DELEGATION = 0x80000, 61 | NOT_DELEGATED = 0x100000, 62 | USE_DES_KEY_ONLY = 0x200000, 63 | DONT_REQUIRE_PREAUTH = 0x400000, 64 | PASSWORD_EXPIRED = 0x800000, 65 | TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 0x1000000, 66 | NO_AUTH_DATA_REQUIRED = 0x2000000, 67 | PARTIAL_SECRETS_ACCOUNT = 0x4000000 68 | } 69 | 70 | 71 | 72 | 73 | [Flags] 74 | public enum TrustFlags : uint 75 | { 76 | InForest = 0x0001, // Domain is a member of the forest 77 | DirectOutBound = 0x0002, // Domain is directly trusted 78 | TreeRoot = 0x0004, // Domain is root of a tree in the forest 79 | Primary = 0x0008, // Domain is the primary domain of queried server 80 | NativeMode = 0x0010, // Primary domain is running in native mode 81 | DirectInBound = 0x0020 // Domain is directly trusting 82 | } 83 | 84 | 85 | 86 | //[MS-ADTS] 6.1.6.7.9 87 | [Flags] 88 | public enum TrustAttributes : uint 89 | { 90 | NonTransitive = 0x1, 91 | UplevelOnly = 0x2, 92 | FilterSids = 0x4, 93 | ForestTransitive = 0x8, 94 | CrossOrganization = 0x10, 95 | WithinForest = 0x20, 96 | TreatAsExternal = 0x40, 97 | TrustUsesRc4 = 0x80, 98 | TrustUsesAes = 0x100, 99 | CrossOrganizationNoTGTDelegation = 0x200, 100 | PIMTrust = 0x400, 101 | CrossOrganizationEnableTGTDelegation = 0x800 102 | } 103 | 104 | 105 | 106 | 107 | // ([MS-ADTS] section 6.1.6.7.12) trustDirection 108 | [Flags] 109 | public enum TrustDirection 110 | { 111 | Disable = 0, 112 | InBound = 1, 113 | OutBound = 2, 114 | BiDirectional = 3 115 | } 116 | 117 | 118 | 119 | 120 | [Flags] 121 | public enum TrustType 122 | { 123 | TreeRoot = 0, 124 | ParentChild = 1, 125 | ShortCut = 2, 126 | External = 3, 127 | Forest = 4, 128 | Kerberos = 5, 129 | Unknown = 6 130 | } 131 | 132 | 133 | 134 | [Flags] 135 | public enum LDAPTrustType 136 | { 137 | WindowsNonAD = 1, 138 | WindowsAD = 2, 139 | NonWindowsKerberos = 3 140 | } 141 | 142 | // From https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-csra/509360cf-9797-491e-9dd1-795f63cb1538 143 | [Flags] 144 | public enum CertificationAuthorityRights : uint 145 | { 146 | ManageCA = 1, // Administrator 147 | ManageCertificates = 2, // Officer 148 | Auditor = 4, 149 | Operator = 8, 150 | Read = 256, 151 | Enroll = 512, 152 | } 153 | 154 | [Flags] 155 | public enum PkiCertificateAuthorityFlags 156 | { 157 | NO_TEMPLATE_SUPPORT = 1, 158 | SUPPORTS_NT_AUTHENTICATION = 2, 159 | CA_SUPPORTS_MANUAL_AUTHENTICATION = 4, 160 | CA_SERVERTYPE_ADVANCED = 8, 161 | } 162 | 163 | // https://github.com/GhostPack/Certify/blob/2b1530309c0c5eaf41b2505dfd5a68c83403d031/Certify/Domain/CertificateTemplate.cs#L33-L58 164 | // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/ec71fd43-61c2-407b-83c9-b52272dec8a1 165 | // and from certutil.exe -v -dstemplate 166 | [Flags] 167 | public enum msPKIEnrollmentFlag : uint 168 | { 169 | NONE = 0x00000000, 170 | INCLUDE_SYMMETRIC_ALGORITHMS = 0x00000001, 171 | PEND_ALL_REQUESTS = 0x00000002, 172 | PUBLISH_TO_KRA_CONTAINER = 0x00000004, 173 | PUBLISH_TO_DS = 0x00000008, 174 | AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE = 0x00000010, 175 | AUTO_ENROLLMENT = 0x00000020, 176 | CT_FLAG_DOMAIN_AUTHENTICATION_NOT_REQUIRED = 0x80, 177 | PREVIOUS_APPROVAL_VALIDATE_REENROLLMENT = 0x00000040, 178 | USER_INTERACTION_REQUIRED = 0x00000100, 179 | ADD_TEMPLATE_NAME = 0x200, 180 | REMOVE_INVALID_CERTIFICATE_FROM_PERSONAL_STORE = 0x00000400, 181 | ALLOW_ENROLL_ON_BEHALF_OF = 0x00000800, 182 | ADD_OCSP_NOCHECK = 0x00001000, 183 | ENABLE_KEY_REUSE_ON_NT_TOKEN_KEYSET_STORAGE_FULL = 0x00002000, 184 | NOREVOCATIONINFOINISSUEDCERTS = 0x00004000, 185 | INCLUDE_BASIC_CONSTRAINTS_FOR_EE_CERTS = 0x00008000, 186 | ALLOW_PREVIOUS_APPROVAL_KEYBASEDRENEWAL_VALIDATE_REENROLLMENT = 0x00010000, 187 | ISSUANCE_POLICIES_FROM_REQUEST = 0x00020000, 188 | SKIP_AUTO_RENEWAL = 0x00040000 189 | } 190 | 191 | // https://github.com/GhostPack/Certify/blob/2b1530309c0c5eaf41b2505dfd5a68c83403d031/Certify/Domain/CertificateTemplate.cs#L10-L31 192 | // from https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/1192823c-d839-4bc3-9b6b-fa8c53507ae1 193 | // and from certutil.exe -v -dstemplate 194 | [Flags] 195 | public enum msPKICertificateNameFlag : uint 196 | { 197 | ENROLLEE_SUPPLIES_SUBJECT = 0x00000001, 198 | ADD_EMAIL = 0x00000002, 199 | ADD_OBJ_GUID = 0x00000004, 200 | OLD_CERT_SUPPLIES_SUBJECT_AND_ALT_NAME = 0x00000008, 201 | ADD_DIRECTORY_PATH = 0x00000100, 202 | ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME = 0x00010000, 203 | SUBJECT_ALT_REQUIRE_DOMAIN_DNS = 0x00400000, 204 | SUBJECT_ALT_REQUIRE_SPN = 0x00800000, 205 | SUBJECT_ALT_REQUIRE_DIRECTORY_GUID = 0x01000000, 206 | SUBJECT_ALT_REQUIRE_UPN = 0x02000000, 207 | SUBJECT_ALT_REQUIRE_EMAIL = 0x04000000, 208 | SUBJECT_ALT_REQUIRE_DNS = 0x08000000, 209 | SUBJECT_REQUIRE_DNS_AS_CN = 0x10000000, 210 | SUBJECT_REQUIRE_EMAIL = 0x20000000, 211 | SUBJECT_REQUIRE_COMMON_NAME = 0x40000000, 212 | SUBJECT_REQUIRE_DIRECTORY_PATH = 0x80000000, 213 | } 214 | 215 | 216 | 217 | 218 | //// ([MS-KILE section 2.2.7) 219 | //[Flags] 220 | //public enum EncryptionType 221 | //{ 222 | // DES_CBC_CRC = 1, 223 | // DES_CBC_MD5 = 2, 224 | // RC4_HMAC_MD5 = 4, 225 | // AES128_CTS_HMAC_SHA1_96 = 8, 226 | // AES256_CTS_HMAC_SHA1_96 = 16 227 | //} 228 | 229 | 230 | public enum GPO_LINK 231 | { 232 | /// No link information is available. 233 | GPLinkUnknown = 0, 234 | 235 | /// The GPO is linked to a computer (local or remote). 236 | GPLinkMachine, 237 | 238 | /// The GPO is linked to a site. 239 | GPLinkSite, 240 | 241 | /// The GPO is linked to a domain. 242 | GPLinkDomain, 243 | 244 | /// The GPO is linked to an organizational unit. 245 | GPLinkOrganizationalUnit 246 | } 247 | 248 | [Flags] 249 | public enum PrintColor 250 | { 251 | YELLOW = 0, 252 | GREEN = 1, 253 | RED = 2 254 | 255 | } 256 | 257 | 258 | 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /ADCollector3/Utilities/Natives.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using static ADCollector3.Enums; 9 | 10 | namespace ADCollector3 11 | { 12 | class Natives 13 | { 14 | [DllImport("NETAPI32.dll", SetLastError = true)] 15 | internal static extern int NetApiBufferFree(IntPtr Buffer); 16 | 17 | 18 | 19 | [DllImport("NetApi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 20 | internal static extern UInt32 DsGetSiteName( 21 | [MarshalAs(UnmanagedType.LPTStr)] 22 | string ComputerName, 23 | out IntPtr SiteNameBuffer); 24 | 25 | 26 | 27 | 28 | //https://www.pinvoke.net/default.aspx/ntdsapi/DsGetDomainControllerInfo.html 29 | [DllImport("ntdsapi.dll", CharSet = CharSet.Auto)] 30 | internal static extern uint DsGetDomainControllerInfo( 31 | IntPtr hDs, 32 | string DomainName, 33 | uint InfoLevel, 34 | out uint InfoCount, 35 | out IntPtr pInf); 36 | 37 | 38 | 39 | 40 | [DllImport("NTDSAPI.dll", CharSet = CharSet.Auto)] 41 | internal static extern void DsFreeDomainControllerInfo( 42 | uint InfoLevel, 43 | uint cInfo, 44 | IntPtr pInf); 45 | 46 | 47 | 48 | 49 | //https://www.pinvoke.net/default.aspx/netapi32/DsEnumerateDomainTrusts.html 50 | [DllImport("Netapi32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Auto)] 51 | internal static extern uint DsEnumerateDomainTrusts( 52 | [MarshalAs(UnmanagedType.LPWStr)] string ServerName, 53 | uint Flags, 54 | out IntPtr Domains, 55 | out uint DomainCount); 56 | 57 | 58 | 59 | 60 | //https://www.pinvoke.net/default.aspx/netapi32/NetSessionEnum.html 61 | [DllImport("netapi32.dll", SetLastError = true)] 62 | internal static extern int NetSessionEnum( 63 | [In, MarshalAs(UnmanagedType.LPWStr)] string ServerName, 64 | [In, MarshalAs(UnmanagedType.LPWStr)] string UncClientName, 65 | [In, MarshalAs(UnmanagedType.LPWStr)] string UserName, 66 | Int32 Level, 67 | out IntPtr bufptr, 68 | int prefmaxlen, 69 | ref Int32 entriesread, 70 | ref Int32 totalentries, 71 | ref Int32 resume_handle); 72 | 73 | 74 | 75 | 76 | //https://www.pinvoke.net/default.aspx/netapi32/NetLocalGroupGetMembers.html 77 | [DllImport("NetAPI32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 78 | internal static extern int NetLocalGroupGetMembers( 79 | [MarshalAs(UnmanagedType.LPWStr)] string servername, 80 | [MarshalAs(UnmanagedType.LPWStr)] string localgroupname, 81 | int level, 82 | out IntPtr bufptr, 83 | int prefmaxlen, 84 | out int entriesread, 85 | out int totalentries, 86 | IntPtr resume_handle); 87 | 88 | 89 | [DllImport("ADVAPI32.DLL", SetLastError = true, CharSet = CharSet.Unicode)] 90 | internal static extern bool LogonUser( 91 | string lpszUsername, 92 | string lpszDomain, 93 | string lpszPassword, 94 | int dwLogonType, 95 | int dwLogonProvider, 96 | out SafeAccessTokenHandle phToken); 97 | 98 | 99 | 100 | 101 | //https://www.pinvoke.net/default.aspx/netapi32/NetWkstaUserEnum.html 102 | //https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstauserenum 103 | [DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 104 | internal static extern int NetWkstaUserEnum( 105 | [In, MarshalAs(UnmanagedType.LPWStr)] string servername, 106 | int level, 107 | // 0: users currently logged on to the workstation 108 | // 1: current users and the domains accessed by the workstation 109 | out IntPtr bufptr, // WKSTA_USER_INFO_0/WKSTA_USER_INFO_1 110 | int prefmaxlen, 111 | out int entriesread, 112 | out int totalentries, 113 | ref int resume_handle); 114 | 115 | 116 | 117 | 118 | //https://www.pinvoke.net/default.aspx/userenv/GetAppliedGPOList.html 119 | //https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getappliedgpolista 120 | [DllImport("userenv.dll", SetLastError = true)] 121 | internal static extern int GetAppliedGPOList( 122 | int dwFlags, 123 | string pMachineName, 124 | IntPtr pSidUser, 125 | IntPtr pGuidExtension, 126 | out IntPtr ppGPOList); 127 | 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /ADCollector3/Utilities/SchemaUtil.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.DirectoryServices.Protocols; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ADCollector3 10 | { 11 | public class SchemaUtil 12 | { 13 | public static Logger logger { get; set; } = LogManager.GetCurrentClassLogger(); 14 | public static List GetSchemaAttributes() 15 | { 16 | List attributes = new List(); 17 | 18 | var ldapInfo = Searcher.LdapInfo; 19 | var schemaEntries = Searcher.GetResultEntries(new LDAPSearchString 20 | { 21 | DN = ldapInfo.SchemaDN, 22 | Filter = "(ldapdisplayname=*)", 23 | Scope = SearchScope.Subtree, 24 | ReturnAttributes = new string[] { "ldapdisplayname" } 25 | }).ToList(); 26 | 27 | foreach (SearchResultEntry entry in schemaEntries) 28 | { 29 | attributes.Add(entry.Attributes["ldapdisplayname"][0].ToString()); 30 | } 31 | 32 | return attributes; 33 | } 34 | 35 | //Get rid of some common attributes that are applied on most of the AD objects 36 | //Also some attributes that are not applied to the matching rule 37 | public static List GetUncommonSchemaAttributes(List attributes) 38 | { 39 | List uncommonAttributes = new List(); 40 | string[] commonAttrs = new string[] { "msds-supportedencryptiontypes", "operatingsystem", "operatingsystemversion", "lastlogontimestamp", "o", "primarygroupid", "pwdlastset", "accountexpires", "objectsid", "ntsecuritydescriptor", "distinguishedname", "objectclass", "objectcategory", "objectguid", "badpasswordtime", "badpwdcount", "mail", "codepage", "name", "replpropertymetadata", "showinadvancedviewonly", "countrycode", "mobile", "displayname", "serviceprincipalname", "dnshostname", "sn", "samaccountname", "givenname", "cn", "samaccounttype", "createtimestamp", "iscriticalsystemobject", "grouptype", "userprincipalname", "lastlogoff", "useraccountcontrol", "dscorepropagationdata", "lastlogon", "memberof", "localpolicyflags", "l", "lockouttime", "division", "department", "employeeid", "instancetype", "company", "usncreated", "usnchanged", "member", "msds-authenticatedatdc", "whenchanged", "whencreated", "modifytimestamp", "msds-parentdistname", "usercertificate", 41 | "msds-isrodc","msds-keyversionnumber","msds-resultantpso","entryttl","msds-sitename","msds-topquotausage","msds-principalname","msds-quotaeffective","msds-quotaused","msds-ncreplcursors","msds-ncreplinboundneighbors","msds-ncreploutboundneighbors","msds-replvaluemetadata","msds-replattributemetadata","msds-tokengroupnames","msds-userpasswordexpirytimecomputed","msds-tokengroupnamesglobalanduniversal","msds-user-account-control-computed","msds-tokengroupnamesnogcacceptable","msds-revealedlist","msds-isusercachableatrodc","msds-revealedlistbl","objectclasses","parentguid","possibleinferiors","primarygrouptoken","allowedattributes","allowedchildclasses","allowedattributeseffective","allowedchildclasseseffective","tokengroups","tokengroupsglobalanduniversal","tokengroupsnogcacceptable","attributetypes","canonicalname","sdrightseffective","ditcontentrules","structuralobjectclass","msds-managedpassword","extendedattributeinfo","subschemasubentry","extendedclassinfo","fromentry","msds-memberoftransitive","msds-membertransitive","msds-replvaluemetadataext","msds-auxiliary-classes","msds-approx-immed-subordinates","msds-localeffectiverecycletime","msds-localeffectivedeletiontime","msds-isgc"}; 42 | foreach (var attribute in attributes) 43 | { 44 | string attr = attribute.ToLower(); 45 | if (!commonAttrs.Contains(attr)) 46 | { 47 | uncommonAttributes.Add(attr); 48 | } 49 | } 50 | return uncommonAttributes; 51 | } 52 | 53 | 54 | //Only search in the default naming context 55 | public static string GetAttributeCount(string attr) 56 | { 57 | logger.Debug($"Counting attribute {attr}"); 58 | var ldapInfo = Searcher.LdapInfo; 59 | 60 | var schemaEntries = Searcher.GetResultEntries(new LDAPSearchString 61 | { 62 | DN = ldapInfo.RootDN, 63 | Filter = $"({attr}=*)", 64 | Scope = SearchScope.Subtree, 65 | ReturnAttributes = new string[] { "cn" }, 66 | PageSize = 1000 67 | });//.ToList(); 68 | 69 | var attrCount = schemaEntries.Count(); 70 | 71 | logger.Debug($"{attr}:{attrCount}"); 72 | return attrCount > 0 ? $"{attr}:{attrCount}" : null; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ADCollector3/Utilities/Struct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static ADCollector3.Enums; 7 | 8 | namespace ADCollector3 9 | { 10 | 11 | public struct LDAPInfo 12 | { 13 | public string RootDN { get; set; } 14 | public string ForestDN { get; set; } 15 | public string SchemaDN { get; set; } 16 | public string ConfigDN { get; set; } 17 | public string DomainName { get; set; } 18 | public string ForestName { get; set; } 19 | public string DomainController { get; set; } 20 | public string TargetSearchBase { get; set; } 21 | public string DomainSID { get; set; } 22 | } 23 | 24 | 25 | [StructLayout(LayoutKind.Sequential)] 26 | public struct GUID 27 | { 28 | public int a; 29 | public short b; 30 | public short c; 31 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 32 | public byte[] d; 33 | } 34 | 35 | 36 | 37 | 38 | //https://github.com/dahall/Vanara/blob/82f474e7416b159f25be466cf7f397e0bda0857e/PInvoke/UserEnv/UserEnv.cs#L1452 39 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 40 | public struct GROUP_POLICY_OBJECT 41 | { 42 | /// 43 | /// Specifies link options. This member can be one of the following values. 44 | /// GPO_FLAG_DISABLE 45 | /// This GPO is disabled. 46 | /// GPO_FLAG_FORCE 47 | /// Do not override the policy settings in this GPO with policy settings in a subsequent GPO. 48 | /// 49 | public uint dwOptions; 50 | 51 | /// Specifies the version number of the GPO. 52 | public uint dwVersion; 53 | 54 | /// Pointer to a string that specifies the path to the directory service portion of the GPO. 55 | [MarshalAs(UnmanagedType.LPTStr)] 56 | public string lpDSPath; 57 | 58 | /// Pointer to a string that specifies the path to the file system portion of the GPO. 59 | [MarshalAs(UnmanagedType.LPTStr)] 60 | public string lpFileSysPath; 61 | 62 | /// Pointer to the display name of the GPO. 63 | [MarshalAs(UnmanagedType.LPTStr)] 64 | public string lpDisplayName; 65 | 66 | /// Pointer to a string that specifies a unique name that identifies the GPO. 67 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)] 68 | public string szGPOName; 69 | 70 | /// 71 | /// Specifies the link information for the GPO. This member may be one of the following values. 72 | /// GPLinkUnknown 73 | /// No link information is available. 74 | /// GPLinkMachine 75 | /// The GPO is linked to a computer (local or remote). 76 | /// GPLinkSite 77 | /// The GPO is linked to a site. 78 | /// GPLinkDomain 79 | /// The GPO is linked to a domain. 80 | /// GPLinkOrganizationalUnit 81 | /// The GPO is linked to an organizational unit. 82 | /// 83 | public GPO_LINK GPOLink; 84 | 85 | /// User-supplied data. 86 | public IntPtr lParam; 87 | 88 | /// Pointer to the next GPO in the list. 89 | public IntPtr pNext; 90 | 91 | /// Pointer to the previous GPO in the list. 92 | public IntPtr pPrev; 93 | 94 | /// 95 | /// Extensions that have stored data in this GPO. The format is a string of GUID s grouped in brackets. For more 96 | /// information, see the following Remarks section. 97 | /// 98 | [MarshalAs(UnmanagedType.LPTStr)] 99 | public string lpExtensions; 100 | 101 | /// User-supplied data. 102 | public IntPtr lParam2; 103 | 104 | /// 105 | /// Path to the Active Directory site, domain, or organization unit to which this GPO is linked. If the GPO is linked to the 106 | /// local GPO, this member is "Local". 107 | /// 108 | [MarshalAs(UnmanagedType.LPTStr)] 109 | public string lpLink; 110 | } 111 | 112 | 113 | 114 | 115 | //[StructLayout(LayoutKind.Sequential)] 116 | //public struct GROUP_POLICY_OBJECT 117 | //{ 118 | // public int dwOptions; 119 | // public int dwVersion; 120 | // public string lpDSPath; 121 | // public string lpDisplayName; 122 | // [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] 123 | // public byte[] szGPOName; 124 | // public int GPOLink; 125 | // public int lParam; 126 | // public IntPtr pNext; // point to a GPO 127 | // public IntPtr pPrev; 128 | // public string lpExtensions; 129 | // public int lParam2; 130 | // public string lpLink; 131 | //} 132 | 133 | 134 | 135 | 136 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 137 | public struct DS_DOMAIN_CONTROLLER_INFO_2 138 | { 139 | [MarshalAs(UnmanagedType.LPTStr)] 140 | public string NetbiosName; 141 | [MarshalAs(UnmanagedType.LPTStr)] 142 | public string DnsHostName; 143 | [MarshalAs(UnmanagedType.LPTStr)] 144 | public string SiteName; 145 | [MarshalAs(UnmanagedType.LPTStr)] 146 | public string SiteObjectName; 147 | [MarshalAs(UnmanagedType.LPTStr)] 148 | public string ComputerObjectName; 149 | [MarshalAs(UnmanagedType.LPTStr)] 150 | public string ServerObjectName; 151 | [MarshalAs(UnmanagedType.LPTStr)] 152 | public string NtdsDsaObjectName; 153 | [MarshalAs(UnmanagedType.Bool)] 154 | public bool fIsPdc; 155 | [MarshalAs(UnmanagedType.Bool)] 156 | public bool fDsEnabled; 157 | [MarshalAs(UnmanagedType.Bool)] 158 | public bool fIsGc; 159 | public GUID SiteObjectGuid; 160 | public GUID ComputerObjectGuid; 161 | public GUID ServerObjectGuid; 162 | public GUID NtdsDsaObjectGuid; 163 | } 164 | 165 | 166 | 167 | 168 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 169 | public struct WKSTA_USER_INFO_1 170 | { 171 | [MarshalAs(UnmanagedType.LPWStr)] public string username; 172 | [MarshalAs(UnmanagedType.LPWStr)] public string logon_domain; 173 | [MarshalAs(UnmanagedType.LPWStr)] public string os_domains; 174 | [MarshalAs(UnmanagedType.LPWStr)] public string logon_server; 175 | } 176 | 177 | 178 | 179 | 180 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 181 | public struct LOCALGROUP_MEMBERS_INFO_2 182 | { 183 | public int sid; 184 | public int sidusage; 185 | [MarshalAs(UnmanagedType.LPWStr)] 186 | public string domainandname; 187 | } 188 | 189 | 190 | 191 | 192 | //https://www.pinvoke.net/default.aspx/Structures/SESSION_INFO_10.html 193 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 194 | public struct SESSION_INFO_10 195 | { 196 | [MarshalAs(UnmanagedType.LPWStr)] public string cname; 197 | [MarshalAs(UnmanagedType.LPWStr)] public string username; 198 | public uint session_time; 199 | public uint idle_time; 200 | } 201 | 202 | 203 | 204 | 205 | 206 | [StructLayout(LayoutKind.Sequential)] 207 | public struct DS_DOMAIN_TRUSTS 208 | { 209 | [MarshalAs(UnmanagedType.LPTStr)] 210 | public string NetbiosDomainName; 211 | [MarshalAs(UnmanagedType.LPTStr)] 212 | public string DnsDomainName; 213 | public uint Flags; 214 | public uint ParentIndex; 215 | public uint TrustType; 216 | public uint TrustAttributes; 217 | public IntPtr DomainSid; 218 | public Guid DomainGuid; 219 | } 220 | 221 | 222 | } 223 | -------------------------------------------------------------------------------- /ADCollector3/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, dev2null 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADCollector 2 | ADCollector is a lightweight tool that enumerates the Active Directory environment to identify possible attack vectors. It will give you a basic understanding of the configuration/deployment of the environment as a starting point. 3 | 4 | #### Notes: 5 | ADCollector is not an alternative to the powerful PowerView, it just automates enumeration to quickly identify juicy information without thinking too much at the early Recon stage. Functions implemented in ADCollector are ideal for enumeration in a large Enterprise environment with lots of users/computers, without generating lots of traffic and taking a large amount of time. It only focuses on extracting useful attributes/properties/ACLs from the most valuable targets instead of enumerating all available attributes from all the user/computer objects in the domain. ~~You will definitely need PowerView to do more detailed enumeration later.~~ You can use ADSI instead of PowerView to enumerate the domain as long as you know what you want to enumerate, see . 6 | 7 | The aim of developing this tool is to help me learn more about Active Directory security in a different perspective as well as to figure out what's behind the scenes of those PowerView functions. 8 | 9 | 10 | It uses S.DS namespace to retrieve domain/forest information from the domain controller(LDAP server). It also utilizes S.DS.P namespace for LDAP searching. 11 | 12 | _**This tool is still under construction. Features will be implemented can be seen from my [project page](https://github.com/dev-2null/ADCollector/projects/1)**_ 13 | 14 | Make sure you have access to the SYSVOL if you run it from a non domain joined host. You may need to run the following command if harden UNC policy is applied: 15 | 16 | ```batch 17 | reg add HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkProvider\HardenedPaths /v "\\*\SYSVOL" /d "RequireMutualAuthentication=0" /t REG_SZ 18 | ``` 19 | 20 | ## Enumeration 21 | * Current Domain/Forest information 22 | * Domains in the current forest (with domain SIDs) 23 | * Domain Controllers in the current domain \[GC/RODC] 24 | * Domain/Forest trusts as well as trusted domain objects[SID filtering status] 25 | * Privileged users (currently in DA and EA group) 26 | * Unconstrained delegation accounts (Excluding DCs) 27 | * Constrained Delegation (S4U2Self, S4U2Proxy) 28 | * Resources-based constrained delegation 29 | * MSSQL/Exchange(/RDP/PS) Remoting SPN accounts 30 | * User accounts with SPN set & password does not expire account 31 | * Protected Users 32 | * Confidential attributes 33 | * ASREQROAST (DontRequirePreAuth accounts) 34 | * AdminSDHolder protected accounts 35 | * Domain attributes (MAQ, minPwdLength, maxPwdAge lockoutThreshold, gpLink[group policies that linked to the current domain object]) 36 | * LDAP basic info(supportedLDAPVersion, supportedSASLMechanisms, domain/forest/DC Functionality) 37 | * Kerberos Policy 38 | * Interesting ACLs on the domain object, resolving GUIDs (User defined object in the future) 39 | * Interesting ACLs on GPOs 40 | * Interesting descriptions on user objects 41 | * Sensitive & Not delegate account 42 | * Group Policy Preference cpassword in SYSVOL 43 | * Effective GPOs on the current user/computer 44 | * Nested Group Membership 45 | * Restricted Group 46 | * LAPS Password View Access 47 | * ADCS Configurations 48 | * Certificate Templates 49 | * Machine Owner 50 | * ACL Scan 51 | * Privileges Rights defined in Group Policies 52 | * User Credentials stored in LDAP 53 | 54 | 55 | ## Usage 56 | ``` 57 | PS C:\> .\ADCollector.exe --help 58 | 59 | _ ____ ____ _ _ _ 60 | / \ | _ \ / ___|___ | | | ___ ___ _| |_ ___ _ __ 61 | / _ \ | | | | | / _ \| | |/ _ \/ __|_ __/ _ \| '__| 62 | / ___ \| |_| | |__| (_) | | | __/ (__ | || (_) | | 63 | /_/ \_\____/ \____\___/|_|_|\___|\___| |__/\___/|_| 64 | 65 | v3.0.1 by dev2null 66 | 67 | 68 | --Domain Domain to enumerate 69 | --LDAPS (Default: false) LDAP over SSL/TLS 70 | --DisableSigning (Default: false) Disable Kerberos Encryption (with -LDAPS flag) 71 | --UserName Alternative UserName 72 | --Password Alternative Credential 73 | --DC Alternative Domain Controller (Hostname/IP) to connect to 74 | --OU Perform the Search under a specific Organizational Unit 75 | --LDAPONLY Only Enumearte Objects in LDAP 76 | --ACLScan Perform ACL scan for an Identity 77 | --ADCS (Default: false) Only Perform AD Certificate Service Check 78 | --TEMPLATES (Default: false) Only Enumerate All Certificate Templates with their DACL 79 | --SCHEMA (Default: false) Count Schema Attributes in the default naming context 80 | --ADIDNS (Default: false) Only Collect ADIDNS Records 81 | --NGAGP Only enumerate Nested Group Membership and Applied Group Policies on the target object 82 | --DACL Enumerate DACL on the target object (with DistinguishedName) 83 | --SessionEnum (Default: false) Enumerate session information on the target host 84 | --UserEnum (Default: false) Enumerate user information on the target host 85 | --LocalGMEnum (Default: false) Enumerate local group members on the target host 86 | --Host (Default: Localhost) Hostname for Session/User/Groupmember Enumeration 87 | --Group (Default: Administrators) Local Group Name for Local GroupMember Enumeration 88 | --Debug (Default: false) Debug Mode 89 | --help Display this help screen. 90 | 91 | Example: .\ADCollector.exe 92 | .\ADCollector.exe --LDAPs --DisableSigning 93 | .\ADCollector.exe --OU IT 94 | .\ADCollector.exe --OU OU=IT,DC=domain,DC=local 95 | .\ADCollector.exe --ADCS 96 | .\ADCollector.exe --TEMPLATES 97 | .\ADCollector.exe --LDAPOnly 98 | .\ADCollector.exe --SCHEMA 99 | .\ADCollector.exe --ADIDNS 100 | .\ADCollector.exe --NGAGP samaccountname 101 | .\ADCollector.exe --DACL DC=domain,DC=net 102 | .\ADCollector.exe --ACLScan user --OU OU=IT,DC=domain,DC=local 103 | .\ADCollector.exe --SessionEnum --Host targetHost 104 | .\ADCollector.exe --UserEnum --Host targetHost 105 | .\ADCollector.exe --LocalGMEnum --Host targetHost --Group 'Remote Desktop Users' 106 | .\ADCollector.exe --Domain domain.local --Username user --Password pass 107 | .\ADCollector.exe --Domain domain.local --DC 10.10.10.1 108 | 109 | ``` 110 | 111 | 112 | ## Changelog 113 | 114 | ##### v 3.0.1: 115 | 1. Added enumeration for certificate templates, schema and user credentials 116 | 2. Added a few flags 117 | ##### v 3.0.0: 118 | 1. Code Refactoring & Bug fix 119 | 2. Added privielge rights and object DACL enumeration 120 | 3. Added Debug mode 121 | 4. Merged interactive menu into command line and removed some simple LDAP enum (use ADSI, see [ADSI Enum](https://dev-2null.github.io/Easy-Domain-Enumeration-with-ADSI/)) 122 | ##### v 2.1.2: 123 | 1. Bug fix with some improvements 124 | 2. New implementation logic for LAPS & Restricted Group enum 125 | 3. Use Task to handle some heavy enumeration functions (much faster for large domain) 126 | 4. Remove GPP cache and DCSync accounts enumeration 127 | ##### v 2.1.1: 128 | 1. Search under a specific OU 129 | 2. LAPS detailed view 130 | 3. Machine Owners 131 | 4. Restricted Groups 132 | 5. ADCS Configurations 133 | 6. ACL Scan 134 | 7. Bug Fix: SYSVOL access, Nested Group Membership 135 | 8. Replace external readINF dependency with custom implementation 136 | 9. Protected Users 137 | ##### v 2.0.0: 138 | 1. Complete Rewrite (more extensible) 139 | 2. Add Interactive Menu with command line choice 140 | 3. Use direct API call to enumerate Trust relationship 141 | 4. Update Applied GPO Enumeration with Security Filtering and WMI Filtering (WMIFilter needs to be checked manually) 142 | 5. Add LDAP DNS Record Enumeration 143 | 6. RunAs: Run ADCollector under another user context 144 | 7. Flexiable SPN Scan, DNS Records, Nested Group Membership, ACL Enumeration 145 | 8. Add NetSessionEnum, NetLocalGroupGetMembers and NetWkstaUserEnum 146 | ##### v 1.1.4: 147 | 1. Some bugs are killed and some details are improved 148 | 2. SPN scanning is now optional 149 | 3. GPP cpassword in SYSVOL/Cache 150 | 4. Interesting ACLs on GPOs; Interesting descriptions on user objects; 151 | 5. Unusual DCSync accounts; Sensitive & not delegate accounts 152 | 6. Effective GPOs on user/computer 153 | 7. Restricted groups 154 | 8. Nested Group Membership 155 | 9. LAPS Password View Access 156 | ##### v 1.1.3: 157 | 1. Fixed SPN scanning result, privilege accounts group membership 158 | 2. Password does not expire accounts; User accounts with SPN set; 159 | 3. Kerberos Policy 160 | 4. Interesting ACLs enumeration for the domain object, resolving GUIDs 161 | 5. DC info is back 162 | ##### v 1.1.2: 163 | 1. Separated into three classes. 164 | 2. Dispose ldap connection properly. 165 | 3. Enumerations: AdminSDHolder, Domain attributes(MAQ, minPwdLengthm maxPwdAge, lockOutThreshold, GP linked to the domain object), accounts don't need pre-authentication. 166 | 4. LDAP basic info (supportedLDAPVersion, supportedSASLMechanisms, domain/forest/DC Functionality) 167 | 5. SPN scanning (SPNs for MSSQL,Exchange,RDP and PS Remoting) 168 | 6. Constrained Delegation enumerations (S4U2Self, S4U2Proxy as well as Resources-based constrained delegation) 169 | 7. RODC (group that administers the RODC) 170 | ##### v 1.1.1: 171 | 1. It now uses S.DS.P namespace to perform search operations, making searches faster and easier to implement. (It also supports paged search. ) 172 | 2. It now supports searching in other domains. (command line parser is not implemented yet). 173 | 3. The code logic is reconstructed, less code, more understandable and cohesive. 174 | 175 | ## Project 176 | For more information (current progress/Todo list/etc) about this tool, you can visit my [project page](https://github.com/dev-2null/ADCollector/projects/1) 177 | 178 | --------------------------------------------------------------------------------