Created
February 12, 2017 21:57
-
-
Save RavuAlHemio/c387645897c35e5b263854313b31c23b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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