rusthound_ce/modules/
mod.rs1pub mod gpo;
3pub mod resolver;
4pub mod sessions;
5pub mod adcs;
6
7use std::error::Error;
8
9use rayon::prelude::*;
10
11use crate::api::ADResults;
12use crate::args::{CollectionMethod, Options};
13use crate::modules::adcs::probe_enterpriseca_esc8;
14
15pub async fn run_modules(
17 common_args: &Options,
18 ad: &mut ADResults,
19) -> Result<(), Box<dyn Error>> {
20
21 if common_args.fqdn_resolver {
23 resolver::resolv::resolving_all_fqdn(
24 common_args.dns_tcp,
25 &common_args.name_server,
26 &mut ad.mappings.fqdn_ip,
27 &ad.computers,
28 ).await;
29 }
30
31 if common_args.collection_method.does_sessions() {
38 sessions::run(common_args, &ad.users, &mut ad.computers).await?;
39 }
40
41 if !matches!(common_args.collection_method, CollectionMethod::DCOnly)
45 && !matches!(common_args.collection_method, CollectionMethod::LdapOnly)
46 && !ad.enterprisecas.is_empty()
47 {
48 log::info!("Starting ESC8 web enrollment probe on {} CA(s)...", ad.enterprisecas.len());
49 ad.enterprisecas.par_iter_mut().for_each(|ca| {
50 let esc8 = probe_enterpriseca_esc8(ca.dns_host());
51 ca.apply_esc8(esc8.http_enrollment_endpoints);
52 });
53 }
54
55 if common_args.collection_method.does_gpo() {
58 let sysvol = match collect_sysvol_targets(common_args).await {
59 Ok(v) => v,
60 Err(e) => {
61 log::warn!("[gpo] SYSVOL collection failed: {e}");
62 Vec::new()
63 }
64 };
65 if !sysvol.is_empty() {
66 log::info!("[gpo] mapping {} GPO(s) to GPOChanges / UserRights", sysvol.len());
67 gpo::apply_gpo(
68 &mut ad.ous,
69 &mut ad.domains,
70 &ad.users,
71 &ad.groups,
72 &mut ad.computers,
73 &sysvol,
74 &ad.mappings.dn_sid,
75 );
76 }
77 }
78
79 Ok(())
81}
82
83async fn collect_sysvol_targets(common_args: &Options) -> anyhow::Result<Vec<gpo::SysvolGpo>> {
85 use crate::transport::smb::{nt_hash_from_str, SmbAuth};
86
87 let user = common_args.username.clone().unwrap_or_default();
88 let password = common_args.password.clone().unwrap_or_default();
89 let nt = common_args.hashes.as_deref().and_then(nt_hash_from_str);
90 let auth = match &nt {
91 Some(h) => SmbAuth::Hash(h),
92 None => SmbAuth::Password(&password),
93 };
94
95 let dc_host = common_args
97 .ip
98 .as_deref()
99 .filter(|s| !s.is_empty())
100 .or(common_args.ldapfqdn.as_deref())
101 .map(str::to_string)
102 .unwrap_or_else(|| common_args.domain.clone());
103
104 if dc_host.is_empty() {
105 log::warn!("[gpo] no SMB target (ip/ldapfqdn/domain) available, skipping SYSVOL collection");
106 return Ok(Vec::new());
107 }
108
109 gpo::collect_sysvol(&dc_host, &common_args.domain, &common_args.domain, &user, auth).await
111}