Skip to content

Instantly share code, notes, and snippets.

@RavuAlHemio
Created February 12, 2017 21:57
Show Gist options
  • Save RavuAlHemio/c387645897c35e5b263854313b31c23b to your computer and use it in GitHub Desktop.
Save RavuAlHemio/c387645897c35e5b263854313b31c23b to your computer and use it in GitHub Desktop.
using System;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.Net;
using System.Security.Cryptography;
using System.Text;
public class RebuildDomainTrust
{
static void UsageAndExit(int exitCode)
{
Console.WriteLine("Usage: RebuildDomainTrust.exe DOMAINCONTROLLER USERNAME");
Console.WriteLine();
Console.WriteLine("DOMAINCONTROLLER is the host name or IP address of a domain controller in the domain.");
Console.WriteLine("USERNAME is the username of a user who is allowed to reset this computer's password.");
Console.WriteLine("The password of USERNAME is asked via the command line.");
Console.WriteLine();
Environment.Exit(exitCode);
}
static string ReadPassword()
{
StringBuilder builder = new StringBuilder();
for (;;)
{
ConsoleKeyInfo qui = Console.ReadKey(true);
if (qui.Key == ConsoleKey.Backspace)
{
if (builder.Length > 0)
{
builder.Remove(builder.Length - 1, 1);
Console.Write("\b \b");
}
}
else if (qui.Key == ConsoleKey.Enter)
{
Console.WriteLine();
break;
}
else if (qui.KeyChar != '\u0000')
{
builder.Append(qui.KeyChar);
Console.Write("*");
}
}
return builder.ToString();
}
static string GenerateRandomPassword(int length)
{
const int alphabetLowerBound = (int)' ';
const int alphabetUpperBound = (int)'z';
const int alphabetSize = alphabetUpperBound - alphabetLowerBound + 1;
byte[] randomBytes = new byte[length];
char[] chars = new char[length];
RandomNumberGenerator rng = RandomNumberGenerator.Create();
rng.GetBytes(randomBytes);
for (int i = 0; i < length; ++i)
{
chars[i] = (char)(alphabetLowerBound + randomBytes[i] % alphabetSize);
}
return new string(chars);
}
public static int Main(string[] args)
{
if (args.Length != 2)
{
UsageAndExit(1);
}
string dc = args[0];
string user = args[1];
Console.Write("Enter {0}'s password: ", user);
string password = ReadPassword();
string newPassword;
using (DirectoryEntry domainEntry = new DirectoryEntry("LDAP://" + dc, user, password, AuthenticationTypes.Secure))
{
using (DirectorySearcher searcher = new DirectorySearcher(domainEntry))
{
string myHostname = Dns.GetHostName();
searcher.Filter = "(&(objectClass=computer)(|(cn=" + myHostname + ")(dn=" + myHostname + ")))";
SearchResult result;
try
{
result = searcher.FindOne();
}
catch (COMException exc)
{
switch (exc.ErrorCode)
{
case unchecked((int)0x8007203A):
Console.Error.WriteLine("Failed to reach the domain controller {0}. :-(", dc);
return 1;
case unchecked((int)0x8007052E):
Console.Error.WriteLine("Username or password rejected by the domain controller {0}. :-(", dc);
return 1;
default:
throw;
}
}
if (result == null)
{
Console.WriteLine("Cannot find myself in the domain! :-(");
return 1;
}
using (DirectoryEntry targetEntry = result.GetDirectoryEntry())
{
// apparently, netdom uses this length and "there is a reason behind it"
const int passwordLength = 120;
newPassword = GenerateRandomPassword(passwordLength);
targetEntry.Invoke("SetPassword", new object[] { newPassword });
targetEntry.Properties["LockOutTime"].Value = 0;
}
}
}
// wheeeeeeeeeeeeeeeeeeeeeee
LsaObjectAttributes lsaAttr = new LsaObjectAttributes();
lsaAttr.RootDirectory = IntPtr.Zero;
lsaAttr.ObjectName = IntPtr.Zero;
lsaAttr.Attributes = 0;
lsaAttr.SecurityDescriptor = IntPtr.Zero;
lsaAttr.SecurityQualityOfService = IntPtr.Zero;
lsaAttr.Length = Marshal.SizeOf(typeof(LsaObjectAttributes));
IntPtr policyHandle = IntPtr.Zero;
IntPtr secretHandle = IntPtr.Zero;
IntPtr currentPassword = IntPtr.Zero;
LsaUnicodeString key = new LsaUnicodeString();
key.Buffer = IntPtr.Zero;
LsaUnicodeString newData = new LsaUnicodeString();
newData.Buffer = IntPtr.Zero;
LsaUnicodeString localhost = new LsaUnicodeString();
localhost.Buffer = IntPtr.Zero;
localhost.Length = 0;
localhost.MaximumLength = 0;
try
{
const uint allAccess = 0x00F0FFF;
uint ret = LsaOpenPolicy(ref localhost, ref lsaAttr, allAccess, out policyHandle);
if (ret != 0)
{
Console.WriteLine("Cannot open LSA policy! :-( [{0}]", ret);
return 1;
}
PopulateLsaUnicodeString("$MACHINE.ACC", ref key);
PopulateLsaUnicodeString(newPassword, ref newData);
bool isNewSecret = false;
const uint setSecretValue = 0x1;
const uint querySecretValue = 0x2;
ret = LsaOpenSecret(policyHandle, ref key, setSecretValue | querySecretValue, out secretHandle);
const uint nameNotFound = 0xc0000034;
if (ret == nameNotFound)
{
ret = LsaCreateSecret(policyHandle, ref key, setSecretValue, out secretHandle);
isNewSecret = true;
}
if (ret != 0)
{
Console.WriteLine("Cannot find/create LSA secret! :-( [{0}]", ret);
return 1;
}
LsaUnicodeString currentData;
if (isNewSecret)
{
currentData = newData;
}
else
{
ret = LsaQuerySecret(secretHandle, out currentPassword, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (ret != 0)
{
Console.WriteLine("Failed to query existing LSA password! :-( [{0}]", ret);
return 1;
}
currentData = (LsaUnicodeString) Marshal.PtrToStructure(currentPassword, typeof(LsaUnicodeString));
}
ret = LsaSetSecret(secretHandle, ref newData, ref currentData);
if (ret != 0)
{
Console.WriteLine("Failed to set new LSA password! :-( [{0}]", ret);
return 1;
}
}
finally
{
if (currentPassword != IntPtr.Zero)
{
LsaFreeMemory(currentPassword);
}
if (policyHandle != IntPtr.Zero)
{
LsaClose(policyHandle);
}
if (secretHandle != IntPtr.Zero)
{
LsaClose(secretHandle);
}
FreeLsaUnicodeString(ref key);
FreeLsaUnicodeString(ref newData);
}
Console.WriteLine("Computer password reset. :-)");
return 0;
}
static void PopulateLsaUnicodeString(string s, ref LsaUnicodeString lus)
{
lus.Buffer = Marshal.StringToHGlobalUni(s);
lus.Length = (ushort)(s.Length * UnicodeEncoding.CharSize);
lus.MaximumLength = (ushort)((s.Length + 1) * UnicodeEncoding.CharSize);
}
static void FreeLsaUnicodeString(ref LsaUnicodeString lus)
{
if (lus.Buffer == IntPtr.Zero)
{
return;
}
Marshal.FreeHGlobal(lus.Buffer);
lus.Buffer = IntPtr.Zero;
}
[StructLayout(LayoutKind.Sequential)]
struct LsaObjectAttributes
{
public int Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public int Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct LsaUnicodeString
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern uint LsaOpenPolicy(ref LsaUnicodeString systemName, ref LsaObjectAttributes objectAttributes,
uint desiredAccess, out IntPtr policyHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern uint LsaOpenSecret(IntPtr policyHandle, ref LsaUnicodeString secretName, uint accessMask,
out IntPtr secretHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern uint LsaCreateSecret(IntPtr policyHandle, ref LsaUnicodeString secretName, uint desiredAccess,
out IntPtr secretHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern uint LsaQuerySecret(IntPtr secretHandle, out IntPtr currentValue, IntPtr currentValueSetTime,
IntPtr oldValue, IntPtr oldValueSetTime);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern uint LsaSetSecret(IntPtr secretHandle, ref LsaUnicodeString currentValue,
ref LsaUnicodeString oldValue);
[DllImport("advapi32.dll")]
static extern int LsaFreeMemory(IntPtr buffer);
[DllImport("advapi32.dll")]
static extern int LsaClose(IntPtr buffer);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment