internal static async Task <LdapWrapper> CheckSMBOpen(LdapWrapper wrapper) { //Only perform checks if this is a Computer object if (wrapper is Computer computer) { //Stealth targetting - we've already determined our stealth targets, so if its not a stealth target, return if (Options.Instance.Stealth && !computer.IsStealthTarget) { return(wrapper); } if (Options.Instance.WindowsOnly) { //If the WindowsOnly flag is set, check the operatingsystem attribute var os = wrapper.SearchResult.GetProperty("operatingsystem"); //Perform a search for the term windows in the operatingsystem string if (!(os?.IndexOf("windows", StringComparison.CurrentCultureIgnoreCase) > -1)) { //If this isn't a windows computer, we'll mark is as such and we'll skip the following port scan since its not necessary computer.IsWindows = false; //Add a computer status message to note why we skipped this computer OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "NotWindows", Task = "SMBCheck" }); return(wrapper); } } //If we're skipping port scan, just return the wrapper. PingFailed is set to false by default if (Options.Instance.SkipPortScan) { return(wrapper); } //Do a check on port 445 and save the result computer.PingFailed = Helpers.CheckPort(computer.APIName, 445) == false; if (computer.PingFailed && Options.Instance.DumpComputerStatus) { //If the port check failed, add a computer status note OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "SMBNotAvailable", Task = "SMBCheck" }); } //Do jitter/delay if specified await Helpers.DoDelay(); } return(wrapper); }
private static IEnumerable <Session> GetLoggedOnUsersRegistry(Computer computer) { if (Options.Instance.NoRegistryLoggedOn) { yield break; } RegistryKey key = null; IEnumerable <string> filteredKeys; try { //Try to open the remote base key key = RegistryKey.OpenRemoteBaseKey(RegistryHive.Users, computer.APIName); //Find subkeys where the regex matches filteredKeys = key.GetSubKeyNames().Where(subkey => SidRegex.IsMatch(subkey)); } catch (Exception e) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = e.Message, Task = "RegistryLoggedOn" }); } yield break; } finally { //Ensure we dispose of the registry key key?.Dispose(); } foreach (var sid in filteredKeys) { yield return(new Session { ComputerId = computer.ObjectIdentifier, UserId = sid }); } if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Success", Task = "RegistryLoggedOn" }); } }
internal static async Task <LdapWrapper> CheckSMBOpen(LdapWrapper wrapper) { if (wrapper is Computer computer) { if (Options.Instance.Stealth && !computer.IsStealthTarget) { return(wrapper); } computer.PingFailed = Helpers.CheckPort(computer.APIName, 445) == false; if (computer.PingFailed && Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "SMBNotAvailable", Task = "SMBCheck" }); } await Helpers.DoDelay(); } return(wrapper); }
/// <summary> /// Wraps the GetNetLocalGroupMembers call with a timeout, and then processes the results into objects /// </summary> /// <param name="computer"></param> /// <param name="rid">The relative ID of the group we want to query</param> /// <returns></returns> private static async Task <List <GenericMember> > GetNetLocalGroupMembers(Computer computer, LocalGroupRids rid) { var sids = new IntPtr[0]; var groupMemberList = new List <GenericMember>(); var task = Task.Run(() => CallLocalGroupApi(computer, rid, out sids)); //Run the API call along with a 10 second timeout if (await Task.WhenAny(task, Task.Delay(10000)) != task) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Timeout", Task = $"GetNetLocalGroup-{rid}" }); return(groupMemberList); } //Check the result of the task var taskResult = task.Result; if (!taskResult) { return(groupMemberList); } if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Success", Task = $"GetNetLocalGroup-{rid}" }); } //Take our pointers to sids and convert them into string sids for matching var convertedSids = new List <string>(); for (var i = 0; i < sids.Length; i++) { try { var sid = new SecurityIdentifier(sids[i]).Value; convertedSids.Add(sid); } catch { //SID Resolution failed for some reason, so ignore it } finally { //Set the IntPtr to zero, so we can GC those sids[i] = IntPtr.Zero; } } //Null out sids, so garbage collection takes care of it sids = null; //Extract the domain SID from the computer's sid, to avoid creating more SecurityIdentifier objects var domainSid = computer.ObjectIdentifier.Substring(0, computer.ObjectIdentifier.LastIndexOf('-')); // The first account in our list should always be the default RID 500 for the machine, but we'll take some extra precautions var machineSid = convertedSids.DefaultIfEmpty("DUMMYSTRING").FirstOrDefault(x => x.EndsWith("-500") && !x.StartsWith(domainSid)) ?? "DUMMYSTRING"; //If we found a machine sid, strip the ending bit off if (machineSid.StartsWith("S-1-5-21")) { machineSid = machineSid.Substring(0, machineSid.LastIndexOf('-')); } foreach (var sid in convertedSids) { //Filter out local accounts if (sid.StartsWith(machineSid)) { continue; } var(finalSid, type) = await ResolutionHelpers.ResolveSidAndGetType(sid, computer.Domain); //Filter out null sids, usually from deconflictions if (finalSid == null) { continue; } groupMemberList.Add(new GenericMember { MemberType = type, MemberId = finalSid }); } return(groupMemberList); }
/// <summary> /// Modified version of GetNetLocalGroupMembers which eliminates several unnecessary LSA/SAMRPC calls /// </summary> /// <param name="computer"></param> /// <param name="rid"></param> /// <param name="sids"></param> /// <returns></returns> private static bool CallLocalGroupApi(Computer computer, LocalGroupRids rid, out IntPtr[] sids) { //Initialize pointers for later var serverHandle = IntPtr.Zero; var domainHandle = IntPtr.Zero; var aliasHandle = IntPtr.Zero; var members = IntPtr.Zero; sids = new IntPtr[0]; //Create some objects required for SAMRPC calls var server = new UNICODE_STRING(computer.APIName); var objectAttributes = new OBJECT_ATTRIBUTES(); try { //Step 1: Call SamConnect to open a handle to the computer's SAM //0x1 = SamServerLookupDomain, 0x20 = SamServerConnect var status = SamConnect(ref server, out serverHandle, 0x1 | 0x20, ref objectAttributes); switch (status) { case NtStatus.StatusRpcServerUnavailable: if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = status.ToString(), Task = $"GetNetLocalGroup-{rid}" }); } return(false); case NtStatus.StatusSuccess: break; default: if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = status.ToString(), Task = $"GetNetLocalGroup-{rid}" }); } return(false); } //Step 2 - Open the built in domain, which is identified by the SID S-1-5-32 //0x200 = Lookup status = SamOpenDomain(serverHandle, 0x200, LocalSidBytes.Value, out domainHandle); if (status != NtStatus.StatusSuccess) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = status.ToString(), Task = $"GetNetLocalGroup-{rid}" }); } return(false); } //Step 3 - Open the alias that corresponds to the group we want to enumerate. //0x4 = ListMembers status = SamOpenAlias(domainHandle, 0x4, (int)rid, out aliasHandle); if (status != NtStatus.StatusSuccess) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = status.ToString(), Task = $"GetNetLocalGroup-{rid}" }); } } //Step 4 - Get the members of the alias we opened in step 3. status = SamGetMembersInAlias(aliasHandle, out members, out var count); if (status != NtStatus.StatusSuccess) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = status.ToString(), Task = $"GetNetLocalGroup-{rid}" }); } return(false); } //If we didn't get any objects, just return false if (count == 0) { return(false); } //Copy the IntPtr to an array so we can loop over it sids = new IntPtr[count]; Marshal.Copy(members, sids, 0, count); return(true); } finally { //Free memory from handles acquired during the process if (serverHandle != IntPtr.Zero) { SamCloseHandle(serverHandle); } if (domainHandle != IntPtr.Zero) { SamCloseHandle(domainHandle); } if (aliasHandle != IntPtr.Zero) { SamCloseHandle(aliasHandle); } if (members != IntPtr.Zero) { SamFreeMemory(members); } } }
/// <summary> /// Wraps the NetWkstaUserEnum API call in a timeout /// </summary> /// <param name="computer"></param> /// <returns></returns> private static async Task <List <Session> > GetLoggedOnUsersAPI(Computer computer) { var resumeHandle = 0; var workstationInfoType = typeof(WKSTA_USER_INFO_1); var ptrInfo = IntPtr.Zero; var entriesRead = 0; var sessionList = new List <Session>(); try { var task = Task.Run(() => NetWkstaUserEnum(computer.APIName, 1, out ptrInfo, -1, out entriesRead, out _, ref resumeHandle)); if (await Task.WhenAny(task, Task.Delay(10000)) != task) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Timeout", Task = "NetWkstaUserEnum" }); } return(sessionList); } var taskResult = task.Result; //Check the result of the task. 234 and 0 are both acceptable. if (taskResult != 0 && taskResult != 234) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = ((NetApiStatus)taskResult).ToString(), Task = "NetWkstaUserEnum" }); } return(sessionList); } var iterator = ptrInfo; if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Success", Task = "NetWkstaUserEnum" }); } for (var i = 0; i < entriesRead; i++) { var data = (WKSTA_USER_INFO_1)Marshal.PtrToStructure(iterator, workstationInfoType); iterator = (IntPtr)(iterator.ToInt64() + Marshal.SizeOf(workstationInfoType)); var domain = data.wkui1_logon_domain; var username = data.wkui1_username; //Remove local accounts if (domain.Equals(computer.SamAccountName, StringComparison.CurrentCultureIgnoreCase)) { continue; } //Remove blank accounts and computer accounts if (username.Trim() == "" || username.EndsWith("$") || username == "ANONYMOUS LOGON" || username == Options.Instance.CurrentUserName) { continue; } //Any domain with a space is unusable (ex: NT AUTHORITY, FONT DRIVER HOST) if (domain.Contains(" ")) { continue; } var(rSuccess, sid, _) = await ResolutionHelpers.ResolveAccountNameToSidAndType(username, domain); if (rSuccess) { sessionList.Add(new Session { UserId = sid, ComputerId = computer.ObjectIdentifier }); } else { sessionList.Add(new Session { UserId = $"{username}@{Helpers.NormalizeDomainName(domain)}".ToUpper(), ComputerId = computer.ObjectIdentifier }); } } return(sessionList); } finally { if (ptrInfo != IntPtr.Zero) { NetApiBufferFree(ptrInfo); } } }
/// <summary> /// Wraps the GetNetLocalGroupMembers call with a timeout, and then processes the results into objects /// </summary> /// <param name="computer"></param> /// <param name="rid">The relative ID of the group we want to query</param> /// <returns></returns> private static async Task <List <GenericMember> > GetNetLocalGroupMembers(Computer computer, LocalGroupRids rid) { var sids = new IntPtr[0]; var groupMemberList = new List <GenericMember>(); var task = Task.Run(() => CallLocalGroupApi(computer, rid, out sids)); if (await Task.WhenAny(task, Task.Delay(10000)) != task) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Timeout", Task = $"GetNetLocalGroup-{rid}" }); return(groupMemberList); } var taskResult = task.Result; if (!taskResult) { return(groupMemberList); } if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Success", Task = $"GetNetLocaGroup-{rid}" }); } //Take our pointers to sids and convert them into string sids for matching var convertedSids = new List <string>(); for (var i = 0; i < sids.Length; i++) { try { var sid = new SecurityIdentifier(sids[i]).Value; convertedSids.Add(sid); } catch { // ignored } finally { //Set the IntPtr to zero, so we can GC those sids[i] = IntPtr.Zero; } } //Null out sids, so garbage collection takes care of it sids = null; //Extract the domain SID from the computer's sid, to avoid creating more SecurityIdentifier objects var domainSid = computer.ObjectIdentifier.Substring(0, computer.ObjectIdentifier.LastIndexOf('-')); string machineSid; // The first account in our list should always be the default RID 500 for the machine, but we'll take some extra precautions try { machineSid = convertedSids.First(x => x.EndsWith("-500") && !x.StartsWith(domainSid)); } catch { machineSid = "DUMMYSTRING"; } foreach (var sid in convertedSids) { if (sid.StartsWith(machineSid)) { continue; } LdapTypeEnum type; var finalSid = sid; if (CommonPrincipal.GetCommonSid(finalSid, out var common)) { finalSid = Helpers.ConvertCommonSid(sid, null); type = common.Type; } else { type = await Helpers.LookupSidType(sid); } groupMemberList.Add(new GenericMember { MemberType = type, MemberId = finalSid }); } return(groupMemberList); }
private static async Task <List <Session> > GetLoggedOnUsersRegistry(Computer computer) { var sessionList = new List <Session>(); if (Options.Instance.NoRegistryLoggedOn) { return(sessionList); } RegistryKey key = null; try { //Try to open the remote base key var task = Task.Run(() => RegistryKey.OpenRemoteBaseKey(RegistryHive.Users, computer.APIName)); if (await Task.WhenAny(task, Task.Delay(10000)) != task) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Timeout", Task = "RegistryLoggedOn" }); } return(sessionList); } key = task.Result; //Find subkeys where the regex matches var filteredKeys = key.GetSubKeyNames().Where(subkey => SidRegex.IsMatch(subkey)); foreach (var sid in filteredKeys) { sessionList.Add(new Session { ComputerId = computer.ObjectIdentifier, UserId = sid }); } if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Success", Task = "RegistryLoggedOn" }); } return(sessionList); } catch (Exception e) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = e.Message, Task = "RegistryLoggedOn" }); } return(sessionList); } finally { //Ensure we dispose of the registry key key?.Dispose(); } }
/// <summary> /// Wraps the NetSessionEnum API call with a timeout and parses the /// </summary> /// <param name="computer"></param> /// <returns></returns> private static async Task <List <Session> > GetNetSessions(Computer computer) { var resumeHandle = IntPtr.Zero; var sessionInfoType = typeof(SESSION_INFO_10); var entriesRead = 0; var ptrInfo = IntPtr.Zero; var sessionList = new List <Session>(); try { var task = Task.Run(() => NetSessionEnum(computer.APIName, null, null, 10, out ptrInfo, -1, out entriesRead, out _, ref resumeHandle)); if (await Task.WhenAny(task, Task.Delay(10000)) != task) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Timeout", Task = "NetSessionEnum" }); } return(sessionList); } var taskResult = task.Result; if (taskResult != 0) { if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = ((NetApiStatus)taskResult).ToString(), Task = "NetSessionEnum" }); } return(sessionList); } var sessions = new SESSION_INFO_10[entriesRead]; var iterator = ptrInfo; for (var i = 0; i < entriesRead; i++) { sessions[i] = (SESSION_INFO_10)Marshal.PtrToStructure(iterator, sessionInfoType); iterator = (IntPtr)(iterator.ToInt64() + Marshal.SizeOf(sessionInfoType)); } if (Options.Instance.DumpComputerStatus) { OutputTasks.AddComputerStatus(new ComputerStatus { ComputerName = computer.DisplayName, Status = "Success", Task = "NetSessionEnum" }); } foreach (var session in sessions) { var sessionUsername = session.sesi10_username; var computerName = session.sesi10_cname; if (computerName == null) { continue; } string computerSid = null; //Filter out computer accounts, Anonymous Logon, empty users if (sessionUsername.EndsWith( "$") || sessionUsername.Trim() == "" || sessionUsername == "$" || sessionUsername == Options.Instance.CurrentUserName || sessionUsername == "ANONYMOUS LOGON") { continue; } //Remove leading backslashes if (computerName.StartsWith("\\")) { computerName = computerName.TrimStart('\\'); } //If the session is pointing to localhost, we already know what the SID of the computer is if (computerName.Equals("[::1]") || computerName.Equals("127.0.0.1")) { computerSid = computer.ObjectIdentifier; } //Try converting the computer name to a SID computerSid = computerSid ?? await Helpers.TryResolveHostToSid(computerName, computer.Domain); //Try converting the username to a SID var searcher = Helpers.GetDirectorySearcher(computer.Domain); var sids = await searcher.LookupUserInGC(sessionUsername); if (sids.Length > 0) { foreach (var sid in sids) { sessionList.Add(new Session { ComputerId = computerSid, UserId = sid }); } } else { var(sidSuccess, userSid) = await Helpers.AccountNameToSid(sessionUsername, computer.Domain, false); if (sidSuccess) { sessionList.Add(new Session { ComputerId = computerSid, UserId = userSid }); } else { sessionList.Add(new Session { ComputerId = computerSid, UserId = sessionUsername }); } } } return(sessionList); } finally { if (ptrInfo != IntPtr.Zero) { NetApiBufferFree(ptrInfo); } } }