public string SolvePart2() { var workGroup = new WorkGroup(5); var completedSteps = new List <Node?>(); var availableSteps = new List <Node>(_graph.Roots); availableSteps.Sort(); // Initialise foreach (Worker worker in workGroup.Workers) { if (availableSteps.Any()) { worker.AssignStep(availableSteps.First()); availableSteps.RemoveAt(0); } } // Tick var totalTime = 0; while (completedSteps.Count < _graph.Nodes.Count) { List <Node?> workResult = workGroup.DoWork(); if (workResult.Any()) { completedSteps.AddRange(workResult); // Get newly available steps foreach (var stepsReadyToComplete in completedSteps.Where(c => c != null).Select(completedStep => _graph.GetStepsReadyToComplete(completedStep !.Name, string.Join(string.Empty, completedSteps.Select(c => c !.Name))))) { availableSteps.AddRange(stepsReadyToComplete.Select(s => new Node { Name = s })); } availableSteps = DistinctByExtension.DistinctBy(availableSteps, s => s.Name).ToList(); availableSteps.Sort(); // Need to account for "in progress" steps. foreach (int index in workGroup.BusyWorkers .Select(worker => availableSteps.FindIndex(s => s.Name == worker.WorkingOn?.Name)) .Where(index => index >= 0)) { availableSteps.RemoveAt(index); } } foreach (Worker worker in workGroup.AvailableWorkers.Where(_ => availableSteps.Any())) { worker.AssignStep(availableSteps.First()); availableSteps.RemoveAt(0); } totalTime++; } return($"Part 2: {totalTime}"); }
public List <DeviceModel> ScanVulnerabilities(List <Host> devices) { RestClient restClient = new RestClient("https://exploits.shodan.io/"); RestRequest restRequest = new RestRequest("api/search", Method.GET); restRequest.AddParameter("key", _APIKEY); var vulnerableDevices = new List <DeviceModel>(); foreach (var device in devices) { var vulnerabilities = new List <VulnerabilityModel>(); var openPorts = new List <int>(); foreach (var port in device.Ports.Where(p => p.Service.Product != null)) { var product = ""; var version = ""; openPorts.Add(port.PortNumber); if (port.Service.Version == null || port.Service.Product == null) { continue; } if (port.Service.Product != null) { if (port.Service.Product.Contains(" ")) { product = port.Service.Product.Substring(0, port.Service.Product.IndexOf(" ", StringComparison.Ordinal) + 1); } else { product = port.Service.Product; } } if (port.Service.Version != null) { if (port.Service.Version.Contains(" ")) { version = port.Service.Version.Substring(0, port.Service.Version.IndexOf(" ", StringComparison.Ordinal) + 1); } else { version = port.Service.Version; } int index = -1; for (int i = 0; i < 3; i++) { index = version.IndexOf(".", index + 1, StringComparison.Ordinal); if (index == -1) { break; } } if (index > 3) { version = version.Substring(0, index); } } var searchTerm = $"{product} {version}"; restRequest.AddParameter("query", searchTerm); // Limited to 1 request per second with Shodan, in case a request returns no results we'll wait 1 second before the next. Thread.Sleep(1000); var query = restClient.Execute((IRestRequest)restRequest); JObject parsedQuery; try { parsedQuery = JObject.Parse(query.Content); var array = parsedQuery.GetValue("matches"); foreach (var jObject in array) { var vulnerability = new VulnerabilityModel(); vulnerability.Port = port.PortNumber; vulnerability.Service = $"{port.Service.Name} ({port.Service.Product})"; vulnerability.Version = port.Service.Version; if (jObject["source"] != null) { vulnerability.Source = jObject["source"].ToString(); } if (jObject["type"] != null) { vulnerability.Type = jObject["type"].ToString(); } if (jObject["cve"] != null && jObject["cve"].HasValues) { vulnerability.CVE = jObject["cve"].ToObject <string[]>()[0]; } if (jObject["date"] != null) { vulnerability.Date = DateTime.Parse(jObject["date"].ToString()); } if (jObject["description"] != null) { var description = jObject["description"].ToString(); if (description.Length > 100) { //Truncate long descriptions to display to users. var tempString = description.Substring(0, 100); if (tempString.LastIndexOf(' ') > 0) { description = tempString.Substring(0, tempString.LastIndexOf(' ')) + " ..."; } } vulnerability.Description = description; } vulnerabilities.Add(vulnerability); } } catch (Exception e) { Console.WriteLine($"There was an error during the search."); } } if (vulnerabilities.Any()) { var vulnerableDevice = new DeviceModel(); vulnerableDevice.OpenPorts = openPorts; vulnerableDevice.IP = device.Address.ToString(); vulnerableDevice.PhysicalAddress = device.PhysicalAddress; vulnerableDevice.Vendor = device.Vendor; try { vulnerableDevice.Hostname = Dns.GetHostEntry(device.Address).HostName; } catch (Exception e) { //GetHostEntry throws an exception if host not found. vulnerableDevice.Hostname = "Hostname not found."; } // Remove duplicate instances of vulnerabilities by CVE var vulnerabilitiesTest = DistinctByExtension.DistinctBy(vulnerabilities, x => x.CVE).ToList(); //Only keep top 5 most recent vulnerabilities /*vulnerabilities = vulnerabilities.Where(x => x.Date.HasValue) * .OrderByDescending(x => x.Date.Value) * .Take(5).ToList();*/ vulnerableDevice.Vulnerabilities = vulnerabilities; vulnerableDevices.Add(vulnerableDevice); Console.WriteLine($"Vulnerability scan completed for {device.Address}. {vulnerableDevice.Vulnerabilities.Count} vulnerabilities found."); } } return(vulnerableDevices); }