Example #1
0
        private static LinkTableRow[] EnumerateLinkTable(JetDb db)
        {
            Stopwatch stopwatch = null;

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateLinkTable)}");
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }

            using (var table = db.OpenJetDbTable(LINKTABLE))
            {
                // Get a dictionary mapping column names to column ids
                var columnDictionary = table.GetColumnDictionary();

                var linktable        = new List <LinkTableRow>();
                var deletedLinkCount = 0;

                // Loop over the table
                table.MoveBeforeFirst();
                while (table.TryMoveNext())
                {
                    var linkDelTimeColumn = new DateTimeColumnValue {
                        Columnid = columnDictionary["link_deltime"]
                    };
                    var linkDntColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["link_DNT"]
                    };
                    var backlinkDnt = new Int32ColumnValue {
                        Columnid = columnDictionary["backlink_DNT"]
                    };
                    table.RetrieveColumns(linkDelTimeColumn, linkDntColumn, backlinkDnt);

                    // Ignore deleted links
                    if (linkDelTimeColumn.Error == JET_wrn.Success)
                    {
                        deletedLinkCount++;
                        continue;
                    }

                    linktable.Add(new LinkTableRow
                    {
                        LinkDnt     = linkDntColumn.Value.Value,
                        BacklinkDnt = backlinkDnt.Value.Value,
                    });
                }

                if (ShowDebugOutput)
                {
                    ConsoleEx.WriteDebug($"  Ignored {deletedLinkCount} deleted backlinks");
                    ConsoleEx.WriteDebug($"  Found {linktable.Count} backlinks");

                    stopwatch.Stop();
                    ConsoleEx.WriteDebug($"  Completed in {stopwatch.Elapsed}");
                }

                return(linktable.ToArray());
            }
        }
Example #2
0
        private static MSysObjectsRow[] EnumerateMSysObjects(JetDb db)
        {
            Stopwatch stopwatch = null;

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateMSysObjects)}");
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }

            var mSysObjects = new List <MSysObjectsRow>();

            using (var table = db.OpenJetDbTable(MSYSOBJECTS))
            {
                // Get a dictionary mapping column names to column ids
                var columnDictionary = table.GetColumnDictionary();

                // Loop over the table adding attribute ids and column names to the dictionary
                table.MoveBeforeFirst();
                while (table.TryMoveNext())
                {
                    var nameColumn = new Utf8StringColumnValue {
                        Columnid = columnDictionary["Name"]
                    };
                    table.RetrieveColumns(nameColumn);
                    if (nameColumn.Value.StartsWith("ATT", StringComparison.Ordinal))
                    {
                        mSysObjects.Add(new MSysObjectsRow
                        {
                            AttributeId = int.Parse(Regex.Replace(nameColumn.Value, "[A-Za-z-]", string.Empty, RegexOptions.None), CultureInfo.InvariantCulture),
                            ColumnName  = nameColumn.Value,
                        });
                    }
                }
            }

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"  Found {mSysObjects.Count} datatable column names");

                stopwatch.Stop();
                ConsoleEx.WriteDebug($"  Completed in {stopwatch.Elapsed}");
            }

            return(mSysObjects.ToArray());
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NtdsAudit"/> class.
        /// </summary>
        /// <param name="ntdsPath">The path to the NTDS file.</param>
        /// <param name="dumphashes">A value indicating whether to dump hashes.</param>
        /// <param name="includeHistoryHashes">A value indicating whether to include history hashes</param>
        /// <param name="systemHivePath">The path to the System hive.</param>
        /// <param name="wordlistPath">The path to a wordlist for simple hash cracking.</param>
        public NtdsAudit(string ntdsPath, bool dumphashes, bool includeHistoryHashes, string systemHivePath, string wordlistPath)
        {
            ntdsPath = ntdsPath ?? throw new ArgumentNullException(nameof(ntdsPath));

            ProgressBar progress = null;

            if (!ShowDebugOutput)
            {
                progress = new ProgressBar("Performing audit...");
            }

            try
            {
                using (var db = new JetDb(ntdsPath))
                {
                    _mSysObjects = EnumerateMSysObjects(db);
                    if (!ShowDebugOutput)
                    {
                        progress.Report(8 / (double)100);
                    }

                    _linkTable = EnumerateLinkTable(db);
                    if (!ShowDebugOutput)
                    {
                        progress.Report(16 / (double)100);
                    }

                    _ldapDisplayNameToDatatableColumnNameDictionary = EnumerateDatatableTableLdapDisplayNames(db, _mSysObjects);
                    if (!ShowDebugOutput)
                    {
                        progress.Report(24 / (double)100);
                    }

                    _datatable = EnumerateDatatableTable(db, _ldapDisplayNameToDatatableColumnNameDictionary, dumphashes, includeHistoryHashes);
                    if (!ShowDebugOutput)
                    {
                        progress.Report(32 / (double)100);
                    }
                }

                if (dumphashes)
                {
                    DecryptSecretData(systemHivePath, includeHistoryHashes);
                    if (!ShowDebugOutput)
                    {
                        progress.Report(40 / (double)100);
                    }
                }

                CalculateDnsForDatatableRows();
                if (!ShowDebugOutput)
                {
                    progress.Report(48 / (double)100);
                }

                CalculateObjectCategoryStringForDatableRows();
                if (!ShowDebugOutput)
                {
                    progress.Report(56 / (double)100);
                }

                Domains = CalculateDomainInfo();
                if (!ShowDebugOutput)
                {
                    progress.Report(64 / (double)100);
                }

                Users = CalculateUserInfo();
                if (!ShowDebugOutput)
                {
                    progress.Report(72 / (double)100);
                }

                if (dumphashes)
                {
                    if (!string.IsNullOrWhiteSpace(wordlistPath))
                    {
                        var ntlmHashToPasswordDictionary = PrecomputeHashes(wordlistPath);
                        CheckUsersForWeakPasswords(ntlmHashToPasswordDictionary);
                    }
                }

                if (!ShowDebugOutput)
                {
                    progress.Report(80 / (double)100);
                }

                Groups = CalculateSecurityGroupInfo();
                if (!ShowDebugOutput)
                {
                    progress.Report(88 / (double)100);
                }

                Computers = CalculateComputerInfo();
                if (!ShowDebugOutput)
                {
                    progress.Report(96 / (double)100);
                }

                CalculateGroupMembership();
                if (!ShowDebugOutput)
                {
                    progress.Report(100 / (double)100);
                }
            }
            finally
            {
                (progress as IDisposable)?.Dispose();
            }
        }
Example #4
0
        private static IReadOnlyDictionary <string, string> EnumerateDatatableTableLdapDisplayNames(JetDb db, MSysObjectsRow[] mSysObjects)
        {
            Stopwatch stopwatch = null;

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateDatatableTableLdapDisplayNames)}");
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }

            var ldapDisplayNameToColumnNameDictionary = new Dictionary <string, string>();
            var unmatchedCount = 0;

            using (var table = db.OpenJetDbTable(DATATABLE))
            {
                // Get a dictionary mapping column names to column ids
                var columnDictionary = table.GetColumnDictionary();

                // Loop over the table
                table.MoveBeforeFirst();
                while (table.TryMoveNext())
                {
                    var ldapDisplayNameColumn = new StringColumnValue {
                        Columnid = columnDictionary["ATTm131532"]
                    };
                    var attributeIdColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["ATTc131102"]
                    };
                    table.RetrieveColumns(attributeIdColumn, ldapDisplayNameColumn);

                    if (attributeIdColumn.Value != null)
                    {
                        if (Array.Find(mSysObjects, x => x.AttributeId == attributeIdColumn.Value) == null)
                        {
                            unmatchedCount++;
                        }
                        else
                        {
                            ldapDisplayNameToColumnNameDictionary.Add(ldapDisplayNameColumn.Value, mSysObjects.First(x => x.AttributeId == attributeIdColumn.Value).ColumnName);
                        }
                    }
                }
            }

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"  Failed to match {unmatchedCount} LDAP display names to datatable column names");
                ConsoleEx.WriteDebug($"  Matched {ldapDisplayNameToColumnNameDictionary.Count} LDAP display names to datatable column names");

                stopwatch.Stop();
                ConsoleEx.WriteDebug($"  Completed in {stopwatch.Elapsed}");
            }

            return(new ReadOnlyDictionary <string, string>(ldapDisplayNameToColumnNameDictionary));
        }
Example #5
0
        private static DatatableRow[] EnumerateDatatableTable(JetDb db, IReadOnlyDictionary <string, string> ldapDisplayNameToDatatableColumnNameDictionary, bool dumpHashes, bool includeHistoryHashes)
        {
            Stopwatch stopwatch = null;

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateDatatableTable)}");
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }

            var datatable    = new List <DatatableRow>();
            var deletedCount = 0;

            using (var table = db.OpenJetDbTable(DATATABLE))
            {
                // Get a dictionary mapping column names to column ids
                var columnDictionary = table.GetColumnDictionary();

                // Loop over the table
                table.MoveBeforeFirst();
                while (table.TryMoveNext())
                {
                    var accountExpiresColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["accountExpires"]]
                    };
                    var displayNameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["displayName"]]
                    };
                    var distinguishedNameTagColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["DNT_col"]
                    };
                    var groupTypeColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["groupType"]]
                    };
                    var isDeletedColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["isDeleted"]]
                    };
                    var lastLogonColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lastLogonTimestamp"]]
                    };
                    var lmColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["dBCSPwd"]]
                    };
                    var lmHistoryColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lmPwdHistory"]]
                    };
                    var nameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["name"]]
                    };
                    var ntColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["unicodePwd"]]
                    };
                    var ntHistoryColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["ntPwdHistory"]]
                    };
                    var objColumn = new BoolColumnValue {
                        Columnid = columnDictionary["OBJ_col"]
                    };
                    var objectCategoryColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectCategory"]]
                    };
                    var objectSidColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectSid"]]
                    };
                    var parentDistinguishedNameTagColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["PDNT_col"]
                    };
                    var passwordLastSetColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pwdLastSet"]]
                    };
                    var pekListColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pekList"]]
                    };
                    var primaryGroupIdColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["primaryGroupID"]]
                    };
                    var rdnTypeColumn = new Int32ColumnValue {
                        Columnid = columnDictionary["RDNtyp_col"]
                    };                                                                                      // The RDNTyp_col holds the Attribute-ID for the attribute being used as the RDN, such as CN, OU, DC
                    var samAccountNameColumn = new StringColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["sAMAccountName"]]
                    };
                    var timeColumn = new LdapDateTimeColumnValue {
                        Columnid = columnDictionary["time_col"]
                    };
                    var userAccountControlColumn = new Int32ColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["userAccountControl"]]
                    };
                    var supplementalCredentialsColumn = new BytesColumnValue {
                        Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["supplementalCredentials"]]
                    };

                    var columns = new List <ColumnValue>
                    {
                        accountExpiresColumn,
                        displayNameColumn,
                        distinguishedNameTagColumn,
                        groupTypeColumn,
                        isDeletedColumn,
                        lastLogonColumn,
                        nameColumn,
                        objColumn,
                        objectCategoryColumn,
                        objectSidColumn,
                        parentDistinguishedNameTagColumn,
                        passwordLastSetColumn,
                        primaryGroupIdColumn,
                        rdnTypeColumn,
                        samAccountNameColumn,
                        timeColumn,
                        userAccountControlColumn,
                    };

                    if (dumpHashes)
                    {
                        columns.Add(pekListColumn);
                        columns.Add(lmColumn);
                        columns.Add(ntColumn);
                        columns.Add(supplementalCredentialsColumn);

                        if (includeHistoryHashes)
                        {
                            columns.Add(lmHistoryColumn);
                            columns.Add(ntHistoryColumn);
                        }
                    }

                    table.RetrieveColumns(columns.ToArray());

                    // Skip deleted objects
                    if (isDeletedColumn.Value.HasValue && isDeletedColumn.Value != 0)
                    {
                        deletedCount++;
                        continue;
                    }

                    // Some deleted objects do not have the isDeleted flag but do have a string appended to the DN (https://support.microsoft.com/en-us/help/248047/phantoms--tombstones-and-the-infrastructure-master)
                    if (nameColumn.Value?.Contains("\nDEL:") ?? false)
                    {
                        deletedCount++;
                        continue;
                    }

                    SecurityIdentifier sid = null;
                    uint rid = 0;
                    if (objectSidColumn.Error == JET_wrn.Success)
                    {
                        var sidBytes = objectSidColumn.Value;
                        var ridBytes = sidBytes.Skip(sidBytes.Length - sizeof(int)).Take(sizeof(int)).Reverse().ToArray();
                        sidBytes = sidBytes.Take(sidBytes.Length - sizeof(int)).Concat(ridBytes).ToArray();
                        rid      = BitConverter.ToUInt32(ridBytes, 0);
                        sid      = new SecurityIdentifier(sidBytes, 0);
                    }

                    var row = new DatatableRow
                    {
                        AccountExpires          = accountExpiresColumn.Value,
                        DisplayName             = displayNameColumn.Value,
                        Dnt                     = distinguishedNameTagColumn.Value,
                        GroupType               = groupTypeColumn.Value,
                        LastLogon               = lastLogonColumn.Value,
                        Name                    = nameColumn.Value,
                        ObjectCategoryDnt       = objectCategoryColumn.Value,
                        Rid                     = rid,
                        Sid                     = sid,
                        ParentDnt               = parentDistinguishedNameTagColumn.Value,
                        Phantom                 = objColumn.Value == false,
                        LastPasswordChange      = passwordLastSetColumn.Value,
                        PrimaryGroupDnt         = primaryGroupIdColumn.Value,
                        RdnType                 = rdnTypeColumn.Value,
                        SamAccountName          = samAccountNameColumn.Value,
                        UserAccountControlValue = userAccountControlColumn.Value,
                    };

                    if (dumpHashes)
                    {
                        if (pekListColumn.Value != null)
                        {
                            row.PekList = pekListColumn.Value;
                        }

                        if (lmColumn.Value != null)
                        {
                            row.EncryptedLmHash = lmColumn.Value;
                        }

                        if (ntColumn.Value != null)
                        {
                            row.EncryptedNtHash = ntColumn.Value;
                        }

                        if (includeHistoryHashes)
                        {
                            if (lmHistoryColumn.Value != null)
                            {
                                row.EncryptedLmHistory = lmHistoryColumn.Value;
                            }

                            if (ntHistoryColumn.Value != null)
                            {
                                row.EncryptedNtHistory = ntHistoryColumn.Value;
                            }
                        }

                        if (supplementalCredentialsColumn.Value != null)
                        {
                            row.SupplementalCredentialsBlob = supplementalCredentialsColumn.Value;
                        }
                    }

                    datatable.Add(row);
                }
            }

            if (ShowDebugOutput)
            {
                ConsoleEx.WriteDebug($"  Skipped {deletedCount} deleted objects");
                ConsoleEx.WriteDebug($"  Enumerated {datatable.Count} objects");

                stopwatch.Stop();
                ConsoleEx.WriteDebug($"  Completed in {stopwatch.Elapsed}");
            }

            return(datatable.ToArray());
        }