Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ The key incantations are:

`-p` Path to a directory full of .toml formatted rules. Snaffler will load all of these in place of the default ruleset.

`--username` Username of an account that you want to impersonate

`--password` Password of an account that you want to impersonate

## What does any of this log output mean?

Hopefully this annotated example will help:
Expand Down
4 changes: 2 additions & 2 deletions SnaffCore/ActiveDirectory/AdData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private string GetNetBiosDomainName()
{
string ldapBase = $"CN=Partitions,CN=Configuration,DC={_targetDomain.Replace(".", ",DC=")}";

DirectorySearch ds = new DirectorySearch(_targetDomain, _targetDc, ldapBase, null, null, 0, false);
DirectorySearch ds = new DirectorySearch(_targetDomain, _targetDc, ldapBase, MyOptions.Username, MyOptions.Password, 0, false);

string[] ldapProperties = new string[] { "netbiosname"};
string ldapFilter = string.Format("(&(objectcategory=Crossref)(dnsRoot={0})(netBIOSName=*))",_targetDomain);
Expand Down Expand Up @@ -107,7 +107,7 @@ private void SetDirectorySearch()
}

_targetDomainNetBIOSName = GetNetBiosDomainName();
DirectorySearch directorySearch = new DirectorySearch(_targetDomain, _targetDc);
DirectorySearch directorySearch = new DirectorySearch(_targetDomain, _targetDc, null, MyOptions.Username, MyOptions.Password);
_directorySearch = directorySearch;
}

Expand Down
7 changes: 2 additions & 5 deletions SnaffCore/ActiveDirectory/DirectorySearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,10 @@ public class DirectorySearch
//Thread-safe storage for our Ldap Connection Pool
private readonly ConcurrentBag<LdapConnection> _connectionPool = new ConcurrentBag<LdapConnection>();

public DirectorySearch(string domainName, string domainController, string ldapUserName = null, string ldapPassword = null, int ldapPort = 0, bool secureLdap = false) :
this(domainName, domainController, $"DC={domainName.Replace(".", ",DC=")}", ldapUserName, ldapPassword, ldapPort, secureLdap){ }

public DirectorySearch(string domainName, string domainController, string baseLdapPath, string ldapUserName = null, string ldapPassword = null, int ldapPort = 0, bool secureLdap = false)
public DirectorySearch(string domainName, string domainController, string baseLdapPath = null, string ldapUserName = null, string ldapPassword = null, int ldapPort = 0, bool secureLdap = false)
{
_domainName = domainName;
_baseLdapPath = baseLdapPath;
_baseLdapPath = baseLdapPath ?? $"DC={domainName.Replace(".", ",DC=")}";
_domainController = domainController;
_domainGuidMap = new Dictionary<string, string>();
_ldapUsername = ldapUserName;
Expand Down
22 changes: 21 additions & 1 deletion SnaffCore/Concurrency/BlockingTaskScheduler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -62,7 +63,26 @@ public void New(Action action)
// okay, let's add the thing
proceed = true;

_taskFactory.StartNew(action, _cancellationSource.Token);
void actionWithImpersonation()
{
bool impersonateResult = Impersonator.StartImpersonating();
if (!impersonateResult)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception($"[Error Code {errorCode}] Failed to impersonate {Impersonator.GetUsername()}.");
}

try
{
action();
}
finally
{
Impersonator.StopImpersonating();
}
}

_taskFactory.StartNew(actionWithImpersonation, _cancellationSource.Token);
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions SnaffCore/Config/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ public partial class Options
public string TargetDc { get; set; }
public bool LogDeniedShares { get; set; } = false;

// User Authentication Options
public string Username { get; set; }
public string Password { get; set; }

// FileScanner Options
public bool DomainUserRules { get; set; } = false;
public int DomainUserMinLen { get; set; } = 6;
Expand Down
76 changes: 76 additions & 0 deletions SnaffCore/Impersonator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Runtime.InteropServices;

namespace SnaffCore
{
public class Impersonator
{
private static IntPtr _userHandle = IntPtr.Zero;
private static string _username = string.Empty;

public static bool Login(string domain, string username, string password)
{
if (_userHandle != IntPtr.Zero)
{
return true;
}

_username = username;
return LogonUser(username, domain, password, 2, 0, ref _userHandle);
}

public static bool StartImpersonating()
{
if (_userHandle == IntPtr.Zero)
{
return true;
}

return ImpersonateLoggedOnUser(_userHandle);
}

public static bool StopImpersonating()
{
if (_userHandle == IntPtr.Zero)
{
return true;
}

return RevertToSelf();
}

public static bool Free()
{
if (_userHandle == IntPtr.Zero)
{
return true;
}

return CloseHandle(_userHandle);
}

public static string GetUsername()
{
return _username;
}

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken
);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool ImpersonateLoggedOnUser(IntPtr hToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
private static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool CloseHandle(IntPtr handle);
}
}
10 changes: 10 additions & 0 deletions SnaffCore/SnaffCon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using static SnaffCore.Config.Options;
using Timer = System.Timers.Timer;
using System.Net;
using System.Runtime.InteropServices;

namespace SnaffCore
{
Expand Down Expand Up @@ -82,6 +83,13 @@ public static BlockingStaticTaskScheduler GetFileTaskScheduler()

public void Execute()
{
bool impersonateResult = Impersonator.StartImpersonating();
if (!impersonateResult)
{
int errorCode = Marshal.GetLastWin32Error();
Mq.Error($"[Error Code {errorCode}] Failed to impersonate {Impersonator.GetUsername()}.");
}

StartTime = DateTime.Now;
// This is the main execution thread.
Timer statusUpdateTimer =
Expand Down Expand Up @@ -157,6 +165,8 @@ public void Execute()
Mq.Info("Finished at " + finished.ToLocalTime());
Mq.Info("Snafflin' took " + runSpan);
Mq.Finish();

Impersonator.StopImpersonating();
}

private void DomainDfsDiscovery()
Expand Down
1 change: 1 addition & 0 deletions SnaffCore/SnaffCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
<Compile Include="Config\ClassifierOptions.cs" />
<Compile Include="Config\Options.cs" />
<Compile Include="Classifiers\ClassifierRule.cs" />
<Compile Include="Impersonator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShareFind\ShareFinder.cs" />
<Compile Include="FileScan\FileScanner.cs" />
Expand Down
15 changes: 15 additions & 0 deletions Snaffler/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ private static Options ParseImpl(string[] args)
"Interval between status updates (in minutes) also acts as a timeout for AD data to be gathered via LDAP. Turn this knob up if you aren't getting any computers from AD when you run Snaffler through a proxy or other slow link. Default = 5");
// list of letters i haven't used yet: gnqw

ValueArgument<string> UsernameArg = new ValueArgument<string>("username");
ValueArgument<string> PasswordArg = new ValueArgument<string>("password");

CommandLineParser.CommandLineParser parser = new CommandLineParser.CommandLineParser();
parser.Arguments.Add(timeOutArg);
parser.Arguments.Add(configFileArg);
Expand All @@ -132,6 +135,8 @@ private static Options ParseImpl(string[] args)
parser.Arguments.Add(ruleDirArg);
parser.Arguments.Add(logType);
parser.Arguments.Add(compExclusionArg);
parser.Arguments.Add(UsernameArg);
parser.Arguments.Add(PasswordArg);

// extra check to handle builtin behaviour from cmd line arg parser
if ((args.Contains("--help") || args.Contains("/?") || args.Contains("help") || args.Contains("-h") || args.Length == 0))
Expand Down Expand Up @@ -380,6 +385,16 @@ private static Options ParseImpl(string[] args)
}
}

if (UsernameArg.Parsed)
{
parsedConfig.Username = UsernameArg.Value;
}

if (PasswordArg.Parsed)
{
parsedConfig.Password = PasswordArg.Value;
}

if (!parsedConfig.LogToConsole && !parsedConfig.LogToFile)
{
Mq.Error(
Expand Down
28 changes: 28 additions & 0 deletions Snaffler/SnaffleRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Threading;
using System.Runtime.InteropServices;

namespace Snaffler
{
Expand Down Expand Up @@ -192,6 +193,28 @@ public void Run(string[] args)

//-------------------------------------------

// Check if user credentials were specified
if (Options.Username != null)
{
Mq.Info($"Impersonating {Options.Username}.");

bool loginResult = Impersonator.Login(Options.TargetDomain ?? Environment.UserDomainName, Options.Username, Options.Password ?? "");
if (!loginResult)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception($"[Error Code {errorCode}] Failed to log in to {Impersonator.GetUsername()}.");
}

bool impersonateResult = Impersonator.StartImpersonating();
if (!impersonateResult)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception($"[Error Code {errorCode}] Failed to impersonate {Impersonator.GetUsername()}.");
}

Options.CurrentUser = Options.Username;
}

if (Options.Snaffle && (Options.SnafflePath.Length > 4))
{
Directory.CreateDirectory(Options.SnafflePath);
Expand All @@ -218,6 +241,11 @@ public void Run(string[] args)
Console.WriteLine(e.ToString());
DumpQueue();
}
finally
{
Impersonator.StopImpersonating();
Impersonator.Free();
}
}

private void DumpQueue()
Expand Down