rusthound_ce/modules/
mod.rs1pub mod adcs;
3pub mod gpo;
4pub mod resolver;
5pub mod sessions;
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(common_args: &Options, ad: &mut ADResults) -> Result<(), Box<dyn Error>> {
17 if common_args.fqdn_resolver {
19 resolver::resolv::resolving_all_fqdn(
20 common_args.dns_tcp,
21 &common_args.name_server,
22 &mut ad.mappings.fqdn_ip,
23 &ad.computers,
24 )
25 .await;
26 }
27
28 if common_args.collection_method.does_sessions() {
35 sessions::run(common_args, &ad.users, &mut ad.computers).await?;
36 }
37
38 if !matches!(common_args.collection_method, CollectionMethod::DCOnly)
42 && !matches!(common_args.collection_method, CollectionMethod::LdapOnly)
43 && !ad.enterprisecas.is_empty()
44 {
45 log::info!(
46 "Starting ESC8 web enrollment probe on {} CA(s)...",
47 ad.enterprisecas.len()
48 );
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 computer_scope = gpo::sysvol::ComputerGpoScope::from_gpos(&ad.gpos);
59 let sysvol = match collect_sysvol_targets(common_args, &computer_scope).await {
60 Ok(v) => v,
61 Err(e) => {
62 log::warn!("[gpo] SYSVOL collection failed: {e}");
63 Vec::new()
64 }
65 };
66 if !sysvol.is_empty() {
67 log::info!(
68 "[gpo] mapping {} GPO(s) to GPOChanges / UserRights",
69 sysvol.len()
70 );
71 gpo::apply_gpo(
72 &mut ad.ous,
73 &mut ad.domains,
74 &ad.users,
75 &ad.groups,
76 &mut ad.computers,
77 &sysvol,
78 &ad.mappings.dn_sid,
79 );
80 }
81 }
82
83 Ok(())
85}
86
87async fn collect_sysvol_targets(
89 common_args: &Options,
90 computer_scope: &gpo::sysvol::ComputerGpoScope,
91) -> anyhow::Result<Vec<gpo::SysvolGpo>> {
92 use crate::transport::smb::{nt_hash_from_str, SmbAuth};
93
94 let user = common_args.username.clone().unwrap_or_default();
95 let password = common_args.password.clone().unwrap_or_default();
96 let nt = common_args.hashes.as_deref().and_then(nt_hash_from_str);
97 let auth = match &nt {
98 Some(h) => SmbAuth::Hash(h),
99 None => SmbAuth::Password(&password),
100 };
101
102 let dc_host = common_args
104 .ip
105 .as_deref()
106 .filter(|s| !s.is_empty())
107 .or(common_args.ldapfqdn.as_deref())
108 .map(str::to_string)
109 .unwrap_or_else(|| common_args.domain.clone());
110
111 if dc_host.is_empty() {
112 log::warn!(
113 "[gpo] no SMB target (ip/ldapfqdn/domain) available, skipping SYSVOL collection"
114 );
115 return Ok(Vec::new());
116 }
117
118 gpo::collect_sysvol(
120 &dc_host,
121 &common_args.domain,
122 &common_args.domain,
123 &user,
124 auth,
125 computer_scope,
126 )
127 .await
128}