Skip to content

Commit

Permalink
Add ability to filter results by containers/OUs
Browse files Browse the repository at this point in the history
  • Loading branch information
ph1ll committed Sep 12, 2019
1 parent 44ddc14 commit 6f475db
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 31 deletions.
65 changes: 41 additions & 24 deletions src/NtdsAudit/NtdsAudit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ internal class NtdsAudit
private readonly IReadOnlyDictionary<string, string> _ldapDisplayNameToDatatableColumnNameDictionary;
private readonly LinkTableRow[] _linkTable;
private readonly MSysObjectsRow[] _mSysObjects;
private readonly bool _useOUFilter;
private readonly IEnumerable<string> _ouFilter;

/// <summary>
/// Initializes a new instance of the <see cref="NtdsAudit"/> class.
Expand All @@ -33,7 +35,7 @@ internal class NtdsAudit
/// <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)
public NtdsAudit(string ntdsPath, bool dumphashes, bool includeHistoryHashes, string systemHivePath, string wordlistPath, string ouFilterFilePath)
{
ntdsPath = ntdsPath ?? throw new ArgumentNullException(nameof(ntdsPath));

Expand All @@ -43,6 +45,12 @@ public NtdsAudit(string ntdsPath, bool dumphashes, bool includeHistoryHashes, st
progress = new ProgressBar("Performing audit...");
}

if (!string.IsNullOrWhiteSpace(ouFilterFilePath))
{
_useOUFilter = true;
_ouFilter = File.ReadAllLines(ouFilterFilePath).Where(x => !string.IsNullOrWhiteSpace(x));
}

try
{
using (var db = new JetDb(ntdsPath))
Expand Down Expand Up @@ -618,6 +626,12 @@ private ComputerInfo[] CalculateComputerInfo()
Disabled = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) == (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE,
LastLogon = row.LastLogon ?? DateTime.Parse("01.01.1601 00:00:00", CultureInfo.InvariantCulture),
};

if (_useOUFilter && !_ouFilter.Any(filterOU => computerInfo.Dn.EndsWith(filterOU)))
{
continue;
}

computers.Add(computerInfo);
}
}
Expand Down Expand Up @@ -898,32 +912,35 @@ private UserInfo[] CalculateUserInfo()
var users = new List<UserInfo>();
foreach (var row in _datatable)
{
if ((row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT) == (int)ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT)
if ((row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT) == (int)ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT && row.ObjectCategory.Equals("Person"))
{
if (row.ObjectCategory.Equals("Person"))
var userInfo = new UserInfo
{
var userInfo = new UserInfo
{
Dnt = row.Dnt.Value,
Name = row.Name,
Dn = row.Dn,
DomainSid = row.Sid.AccountDomainSid,
Disabled = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) == (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE,
LastLogon = row.LastLogon ?? DateTime.Parse("01.01.1601 00:00:00", CultureInfo.InvariantCulture),
PasswordNotRequired = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) == (int)ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD,
PasswordNeverExpires = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD) == (int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD,
Expires = GetAccountExpiresDateTimeFromByteArray(row.AccountExpires),
PasswordLastChanged = row.LastPasswordChange ?? DateTime.Parse("01.01.1601 00:00:00", CultureInfo.InvariantCulture),
SamAccountName = row.SamAccountName,
Rid = row.Rid,
LmHash = row.LmHash,
NtHash = row.NtHash,
LmHistory = row.LmHistory,
NtHistory = row.NtHistory,
ClearTextPassword = row.SupplementalCredentials?.ContainsKey("Primary:CLEARTEXT") ?? false ? Encoding.Unicode.GetString(row.SupplementalCredentials["Primary:CLEARTEXT"]) : null
};
users.Add(userInfo);
Dnt = row.Dnt.Value,
Name = row.Name,
Dn = row.Dn,
DomainSid = row.Sid.AccountDomainSid,
Disabled = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) == (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE,
LastLogon = row.LastLogon ?? DateTime.Parse("01.01.1601 00:00:00", CultureInfo.InvariantCulture),
PasswordNotRequired = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) == (int)ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD,
PasswordNeverExpires = (row.UserAccountControlValue & (int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD) == (int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD,
Expires = GetAccountExpiresDateTimeFromByteArray(row.AccountExpires),
PasswordLastChanged = row.LastPasswordChange ?? DateTime.Parse("01.01.1601 00:00:00", CultureInfo.InvariantCulture),
SamAccountName = row.SamAccountName,
Rid = row.Rid,
LmHash = row.LmHash,
NtHash = row.NtHash,
LmHistory = row.LmHistory,
NtHistory = row.NtHistory,
ClearTextPassword = row.SupplementalCredentials?.ContainsKey("Primary:CLEARTEXT") ?? false ? Encoding.Unicode.GetString(row.SupplementalCredentials["Primary:CLEARTEXT"]) : null
};

if (_useOUFilter && !_ouFilter.Any(filterOU => userInfo.Dn.EndsWith(filterOU)))
{
continue;
}

users.Add(userInfo);
}
}

Expand Down
17 changes: 12 additions & 5 deletions src/NtdsAudit/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ private static void Main(string[] args)
var includeHistoryHashes = commandLineApplication.Option("--history-hashes", "Include history hashes in the pdwump output.", CommandOptionType.NoValue);
var dumpReversiblePath = commandLineApplication.Option("--dump-reversible <file>", "The path to output clear text passwords, if reversible encryption is enabled.", CommandOptionType.SingleValue);
var wordlistPath = commandLineApplication.Option("--wordlist", "The path to a wordlist of weak passwords for basic hash cracking. Warning, using this option is slow, the use of a dedicated password cracker, such as 'john', is recommended instead.", CommandOptionType.SingleValue);
var ouFilterFilePath = commandLineApplication.Option("--ou-filter-file <file>", "The path to file containing a line separated list of OUs to which to limit user and computer results.", CommandOptionType.SingleValue);
var baseDate = commandLineApplication.Option("--base-date <yyyyMMdd>", "Specifies a custom date to be used as the base date in statistics. The last modified date of the NTDS file is used by default.", CommandOptionType.SingleValue);
var debug = commandLineApplication.Option("--debug", "Show debug output.", CommandOptionType.NoValue);

Expand Down Expand Up @@ -116,14 +117,20 @@ private static void Main(string[] args)
argumentsValid = false;
}

if (ouFilterFilePath.HasValue() && !File.Exists(ouFilterFilePath.Value()))
{
ConsoleEx.WriteError($"OU filter file \"{ouFilterFilePath.Value()}\" does not exist.");
argumentsValid = false;
}

if (showHelp)
{
commandLineApplication.ShowHelp();
}

if (!showHelp && argumentsValid)
{
var ntdsAudit = new NtdsAudit(ntdsPath.Value, pwdumpPath.HasValue(), includeHistoryHashes.HasValue(), systemHivePath.Value(), wordlistPath.Value());
var ntdsAudit = new NtdsAudit(ntdsPath.Value, pwdumpPath.HasValue(), includeHistoryHashes.HasValue(), systemHivePath.Value(), wordlistPath.Value(), ouFilterFilePath.Value());

var baseDateTime = baseDate.HasValue() ? DateTime.ParseExact(baseDate.Value(), "yyyyMMdd", null, DateTimeStyles.AssumeUniversal) : new FileInfo(ntdsPath.Value).LastWriteTimeUtc;

Expand Down Expand Up @@ -219,11 +226,11 @@ private static void WriteComputersCsvFile(string computersCsvPath, NtdsAudit ntd
{
using (var file = new StreamWriter(computersCsvPath, false))
{
file.WriteLine("Domain,Computer,Disabled,Last Logon");
file.WriteLine("Domain,Computer,Disabled,Last Logon,DN");
foreach (var computer in ntdsAudit.Computers)
{
var domain = ntdsAudit.Domains.Single(x => x.Sid == computer.DomainSid);
file.WriteLine($"{domain.Fqdn},{computer.Name},{computer.Disabled},{computer.LastLogon}");
file.WriteLine($"{domain.Fqdn},{computer.Name},{computer.Disabled},{computer.LastLogon},\"{computer.Dn}\"");
}
}
}
Expand Down Expand Up @@ -349,11 +356,11 @@ private static void WriteUsersCsvFile(string usersCsvPath, NtdsAudit ntdsAudit,
{
using (var file = new StreamWriter(usersCsvPath, false))
{
file.WriteLine("Domain,Username,Administrator,Domain Admin,Enterprise Admin,Disabled,Expired,Password Never Expires,Password Not Required,Password Last Changed,Last Logon");
file.WriteLine("Domain,Username,Administrator,Domain Admin,Enterprise Admin,Disabled,Expired,Password Never Expires,Password Not Required,Password Last Changed,Last Logon,DN");
foreach (var user in ntdsAudit.Users)
{
var domain = ntdsAudit.Domains.Single(x => x.Sid == user.DomainSid);
file.WriteLine($"{domain.Fqdn},{user.SamAccountName},{user.RecursiveGroupSids.Contains(domain.AdministratorsSid)},{user.RecursiveGroupSids.Contains(domain.DomainAdminsSid)},{user.RecursiveGroupSids.Intersect(ntdsAudit.Domains.Select(x => x.EnterpriseAdminsSid)).Any()},{user.Disabled},{!user.Disabled && user.Expires.HasValue && user.Expires.Value < baseDateTime},{user.PasswordNeverExpires},{user.PasswordNotRequired},{user.PasswordLastChanged},{user.LastLogon}");
file.WriteLine($"{domain.Fqdn},{user.SamAccountName},{user.RecursiveGroupSids.Contains(domain.AdministratorsSid)},{user.RecursiveGroupSids.Contains(domain.DomainAdminsSid)},{user.RecursiveGroupSids.Intersect(ntdsAudit.Domains.Select(x => x.EnterpriseAdminsSid)).Any()},{user.Disabled},{!user.Disabled && user.Expires.HasValue && user.Expires.Value < baseDateTime},{user.PasswordNeverExpires},{user.PasswordNotRequired},{user.PasswordLastChanged},{user.LastLogon},\"{user.Dn}\"");
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/NtdsAudit/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.6.0")]
[assembly: AssemblyFileVersion("2.0.6.0")]
[assembly: AssemblyVersion("2.0.7.0")]
[assembly: AssemblyFileVersion("2.0.7.0")]
[assembly: CLSCompliant(true)]

0 comments on commit 6f475db

Please sign in to comment.