rusthound_ce/modules/
mod.rs1pub mod adcs;
3pub mod gpo;
4pub mod resolver;
5pub mod session;
6pub mod localgroup;
7pub mod webclient;
8
9use std::error::Error;
10
11use futures::future;
12
13use crate::api::ADResults;
14use crate::args::{CollectionMethod, Options};
15use crate::modules::adcs::probe_enterpriseca_esc8;
16use crate::modules::gpo::sysvol::collect_sysvol_targets;
17
18pub async fn run_modules(
20 common_args: &Options,
21 ad: &mut ADResults
22) -> Result<(), Box<dyn Error>> {
23
24 let cert_auth = common_args.uses_cert();
25 if cert_auth {
26 log::warn!("Certificate authentication in use: skipping SMB-based modules \
27 (sessions and GPO/SYSVOL) no SMB credentials available.");
28 }
29
30 if common_args.fqdn_resolver {
32 resolver::resolv::resolving_all_fqdn(
33 common_args.dns_tcp,
34 &common_args.name_server,
35 &mut ad.mappings.fqdn_ip,
36 &ad.computers,
37 )
38 .await;
39 }
40
41 if common_args.collection_method.does_session() && !cert_auth {
48 session::run(common_args, &ad.users, &mut ad.computers).await?;
49 }
50
51 if !matches!(common_args.collection_method, CollectionMethod::DCOnly)
60 && !matches!(common_args.collection_method, CollectionMethod::LdapOnly)
61 && !ad.enterprisecas.is_empty()
62 {
63 log::info!(
64 "Starting ESC8 web enrollment probe on {} CA(s)...",
65 ad.enterprisecas.len()
66 );
67 let targets: Vec<(String, String)> = ad
68 .enterprisecas
69 .iter()
70 .map(|ca| (ca.dns_host().to_string(), ca.caname().to_string()))
71 .collect();
72 let probes = future::join_all(targets.into_iter().map(|(host, ca_name)| {
73 tokio::task::spawn_blocking(move || probe_enterpriseca_esc8(&host, &ca_name))
74 }))
75 .await;
76 for (ca, probe) in ad.enterprisecas.iter_mut().zip(probes) {
77 match probe {
78 Ok(esc8) => ca.apply_esc8(esc8.http_enrollment_endpoints),
79 Err(join_err) => log::warn!(
80 "[adcs] ESC8 probe task for {} did not complete ({}), skipping",
81 ca.dns_host(),
82 join_err
83 ),
84 }
85 }
86 }
87
88 if common_args.collection_method.does_gpo() && !cert_auth {
91 let computer_scope = gpo::sysvol::ComputerGpoScope::from_gpos(&ad.gpos);
92 let sysvol = match collect_sysvol_targets(common_args, &computer_scope).await {
93 Ok(v) => v,
94 Err(e) => {
95 log::warn!("[gpo] SYSVOL collection failed: {e}");
96 Vec::new()
97 }
98 };
99 if !sysvol.is_empty() {
100 log::info!(
101 "[gpo] mapping {} GPO(s) to GPOChanges / UserRights",
102 sysvol.len()
103 );
104 gpo::apply_gpo(
105 &mut ad.ous,
106 &mut ad.domains,
107 &ad.users,
108 &ad.groups,
109 &mut ad.computers,
110 &sysvol,
111 &ad.mappings.dn_sid,
112 &common_args.domain
113 );
114 }
115 }
116
117 if common_args.collection_method.does_local_group() && !cert_auth {
125 localgroup::run(common_args, &ad.users, &mut ad.computers, &ad.mappings.sid_type).await?;
126 }
127
128 if common_args.collection_method.does_web_client() && !cert_auth {
131 webclient::run(common_args, &mut ad.computers).await?;
132 }
133
134 Ok(())
136}