1use serde_json::value::Value;
2use serde::{Deserialize, Serialize};
3use colored::Colorize;
4use ldap3::SearchEntry;
5use log::{info, debug, trace};
6use std::collections::HashMap;
7use std::error::Error;
8
9use crate::enums::{OBJECT_SID_RE1, SID_PART1_RE1};
10use crate::objects::common::{LdapObject, Session, AceTemplate, Member, SPNTarget, LocalGroup, Link, DCRegistryData};
11use crate::utils::date::{convert_timestamp,string_to_epoch};
12use crate::utils::crypto::convert_encryption_types;
13use crate::enums::acl::parse_ntsecuritydescriptor;
14use crate::enums::secdesc::LdapSid;
15use crate::enums::sid::sid_maker;
16use crate::enums::uacflags::get_flag;
17
18use super::common::UserRight;
19
20#[derive(Debug, Clone, Deserialize, Serialize, Default)]
22pub struct Computer {
23 #[serde(rename = "Properties")]
24 properties: ComputerProperties,
25 #[serde(rename = "Aces")]
26 aces: Vec<AceTemplate>,
27 #[serde(rename = "ObjectIdentifier")]
28 object_identifier: String,
29 #[serde(rename = "IsDeleted")]
30 is_deleted: bool,
31 #[serde(rename = "IsACLProtected")]
32 is_acl_protected: bool,
33 #[serde(rename = "ContainedBy")]
34 contained_by: Option<Member>,
35
36 #[serde(rename = "PrimaryGroupSID")]
37 primary_group_sid: String,
38 #[serde(rename = "AllowedToDelegate")]
39 allowed_to_delegate: Vec<Member>,
40 #[serde(rename = "AllowedToAct")]
41 allowed_to_act: Vec<Member>,
42 #[serde(rename = "HasSIDHistory")]
43 has_sid_history: Vec<String>,
44 #[serde(rename = "DumpSMSAPassword")]
45 dump_smsa_password: Vec<Member>,
46
47 #[serde(rename = "Sessions")]
48 sessions: Session,
49 #[serde(rename = "PrivilegedSessions")]
50 privileged_sessions: Session,
51 #[serde(rename = "RegistrySessions")]
52 registry_sessions: Session,
53 #[serde(rename = "LocalGroups")]
54 local_groups: Vec<LocalGroup>,
55 #[serde(rename = "UserRights")]
56 users_rights: Vec<UserRight>,
57 #[serde(rename = "DCRegistryData")]
58 dcregistry_data: DCRegistryData,
59
60 #[serde(rename = "IsDC")]
61 is_dc: bool,
62 #[serde(rename = "UnconstrainedDelegation")]
63 unconstrained_delegation: bool,
64 #[serde(rename = "DomainSID")]
65 domain_sid: String,
66
67 #[serde(rename = "Status")]
68 status: Option<String>,
69}
70
71impl Computer {
72 pub fn new() -> Self {
74 Self { ..Default::default() }
75 }
76
77 pub fn properties(&self) -> &ComputerProperties {
79 &self.properties
80 }
81 pub fn object_identifier(&self) -> &String {
82 &self.object_identifier
83 }
84 pub fn allowed_to_act(&self) -> &Vec<Member> {
85 &self.allowed_to_act
86 }
87
88 pub fn allowed_to_act_mut(&mut self) -> &mut Vec<Member> {
90 &mut self.allowed_to_act
91 }
92
93 pub fn parse(
96 &mut self,
97 result: SearchEntry,
98 domain: &str,
99 dn_sid: &mut HashMap<String, String>,
100 sid_type: &mut HashMap<String, String>,
101 fqdn_sid: &mut HashMap<String, String>,
102 fqdn_ip: &mut HashMap<String, String>,
103 domain_sid: &str,
104 schema_guid_map: &HashMap<String, String>,
105 ) -> Result<(), Box<dyn Error>> {
106 let result_dn: String = result.dn.to_uppercase();
107 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
108 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
109
110 debug!("Parse computer: {result_dn}");
112
113 for (key, value) in &result_attrs {
115 trace!(" {key:?}:{value:?}");
116 }
117 for (key, value) in &result_bin {
119 trace!(" {key:?}:{value:?}");
120 }
121
122 self.properties.domain = domain.to_uppercase();
124 self.properties.distinguishedname = result_dn;
125 self.properties.enabled = true;
126 self.domain_sid = domain_sid.to_string();
127
128 let mut sid: String = "".to_owned();
129 let mut group_id: String = "".to_owned();
130 for (key, value) in &result_attrs {
132 match key.as_str() {
133 "name" => {
134 let name = &value[0];
135 let email = format!("{}.{}",name.to_owned(),domain);
136 self.properties.name = email.to_uppercase();
137 }
138 "sAMAccountName" => {
139 self.properties.samaccountname = value[0].to_owned();
140 }
141 "dNSHostName" => {
142 self.properties.name = value[0].to_uppercase();
143 }
144 "description" => {
145 self.properties.description = Some(value[0].to_owned());
146 }
147 "operatingSystem" => {
148 self.properties.operatingsystem = value[0].to_owned();
149 }
150 "lastLogon" => {
168 let lastlogon = &value[0].parse::<i64>().unwrap_or(0);
169 if lastlogon.is_positive() {
170 let epoch = convert_timestamp(*lastlogon);
171 self.properties.lastlogon = epoch;
172 }
173 }
174 "lastLogonTimestamp" => {
175 let lastlogontimestamp = &value[0].parse::<i64>().unwrap_or(0);
176 if lastlogontimestamp.is_positive() {
177 let epoch = convert_timestamp(*lastlogontimestamp);
178 self.properties.lastlogontimestamp = epoch;
179 }
180 }
181 "pwdLastSet" => {
182 let pwdlastset = &value[0].parse::<i64>().unwrap_or(0);
183 if pwdlastset.is_positive() {
184 let epoch = convert_timestamp(*pwdlastset);
185 self.properties.pwdlastset = epoch;
186 }
187 }
188 "whenCreated" => {
189 let epoch = string_to_epoch(&value[0])?;
190 if epoch.is_positive() {
191 self.properties.whencreated = epoch;
192 }
193 }
194 "servicePrincipalName" => {
195 let mut result: Vec<String> = Vec::new();
197 for value in &result_attrs["servicePrincipalName"] {
198 result.push(value.to_owned());
199 }
200 self.properties.serviceprincipalnames = result;
201 }
202 "userAccountControl" => {
203 let uac = &value[0].parse::<u32>().unwrap();
205 let uac_flags = get_flag(*uac);
206 for flag in uac_flags {
208 if flag.contains("AccountDisable") {
209 self.properties.enabled = false;
210 };
211 if flag.contains("TrustedForDelegation") {
214 self.properties.unconstraineddelegation = true;
215 self.unconstrained_delegation = true;
216 };
217 if flag.contains("TrustedToAuthForDelegation") {
218 self.properties.trustedtoauth = true;
219 };
220 if flag.contains("PasswordNotRequired") {
221 self.properties.passwordnotreqd = true;
222 };
223 if flag.contains("DontExpirePassword") {
224 self.properties.pwdneverexpires = true;
225 };
226 if flag.contains("ServerTrustAccount") {
227 self.properties.is_dc = true;
228 self.is_dc = true;
229 }
230 }
231 }
232 "msDS-AllowedToDelegateTo" => {
233 let mut vec_members2: Vec<Member> = Vec::new();
237 for objet in value {
238 let mut member_allowed_to_delegate = Member::new();
239 let split = objet.split("/");
240 let fqdn = split.collect::<Vec<&str>>()[1];
241 let mut checker = false;
242 for member in &vec_members2 {
243 if member.object_identifier().contains(fqdn.to_uppercase().as_str()) {
244 checker = true;
245 }
246 }
247 if !checker {
248 *member_allowed_to_delegate.object_identifier_mut() = fqdn.to_uppercase().to_owned().to_uppercase();
249 *member_allowed_to_delegate.object_type_mut() = "Computer".to_owned();
250 vec_members2.push(member_allowed_to_delegate.to_owned());
251 }
252 }
253 self.allowed_to_delegate = vec_members2;
255 }
256 "ms-Mcs-AdmPwd" => {
258 info!(
261 "Your user can read LAPS password on {}: {}",
262 &result_attrs["name"][0].yellow().bold(),
263 &result_attrs["ms-Mcs-AdmPwd"][0].yellow().bold()
264 );
265 self.properties.haslaps = true;
266 }
267 "ms-Mcs-AdmPwdExpirationTime" => {
268 self.properties.haslaps = true;
271 }
272 "msLAPS-Password" => {
274 info!(
275 "Your user can read LAPS password on {}: {:?}",
276 &result_attrs["name"][0].yellow().bold(),
277 &value[0].yellow().bold()
278 );
279 self.properties.haslaps = true;
280 }
281 "msLAPS-EncryptedPassword" => {
282 info!(
283 "Your user can read uncrypted LAPS password on {} please check manually to decrypt it!",
284 &result_attrs["name"][0].yellow().bold()
285 );
286 self.properties.haslaps = true;
287 }
288 "msLAPS-PasswordExpirationTime" => {
289 self.properties.haslaps = true;
291 }
292 "primaryGroupID" => {
293 group_id = value[0].to_owned();
294 }
295 "IsDeleted" => {
296 self.is_deleted = true;
297 }
298 "msDS-SupportedEncryptionTypes" => {
299 self.properties.supportedencryptiontypes = convert_encryption_types(value[0].parse::<i32>().unwrap_or(0));
300 }
301 _ => {}
302 }
303 }
304
305 for (key, value) in &result_bin {
307 match key.as_str() {
308 "objectSid" => {
309 sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
311 self.object_identifier = sid.to_owned();
312
313 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
314 self.properties.domainsid = domain_sid[0].to_owned().to_string();
315 }
316 }
317 "nTSecurityDescriptor" => {
318 let relations_ace = parse_ntsecuritydescriptor(
321 self,
322 &value[0],
323 "Computer",
324 &result_attrs,
325 &result_bin,
326 domain,
327 schema_guid_map,
328 );
329 self.aces = relations_ace;
330 }
331 "msDS-AllowedToActOnBehalfOfOtherIdentity" => {
332 let relations_ace = parse_ntsecuritydescriptor(
335 self,
336 &value[0],
337 "Computer",
338 &result_attrs,
339 &result_bin,
340 domain,
341 schema_guid_map,
342 );
343 let mut vec_members_allowtoact: Vec<Member> = Vec::new();
344 let mut allowed_to_act = Member::new();
345 for delegated in relations_ace {
346 if *delegated.right_name() == "GenericAll" {
349 *allowed_to_act.object_identifier_mut() = delegated.principal_sid().to_string();
350 vec_members_allowtoact.push(allowed_to_act.to_owned());
351 continue
352 }
353 }
354 self.allowed_to_act = vec_members_allowtoact;
355 }
356 _ => {}
357 }
358 }
359
360 #[allow(irrefutable_let_patterns)]
362 if let id = group_id {
363 if let Some(part1) = SID_PART1_RE1.find(&sid) {
364 self.primary_group_sid = format!("{}{}", part1.as_str(), id);
365 } else {
366 eprintln!("[!] Regex did not match any part of the SID");
367 }
368 }
369
370 dn_sid.insert(
372 self.properties.distinguishedname.to_string(),
373 self.object_identifier.to_string(),
374
375 );
376 sid_type.insert(
378 self.object_identifier.to_string(),
379 "Computer".to_string(),
380 );
381
382 fqdn_sid.insert(
383 self.properties.name.to_string(),
384 self.object_identifier.to_string(),
385 );
386
387 fqdn_ip.insert(
388 self.properties.name.to_string(),
389 String::from(""),
390 );
391
392 Ok(())
395 }
396}
397
398impl LdapObject for Computer {
399 fn to_json(&self) -> Value {
401 serde_json::to_value(self).unwrap()
402 }
403
404 fn get_object_identifier(&self) -> &String {
406 &self.object_identifier
407 }
408 fn get_is_acl_protected(&self) -> &bool {
409 &self.is_acl_protected
410 }
411 fn get_aces(&self) -> &Vec<AceTemplate> {
412 &self.aces
413 }
414 fn get_spntargets(&self) -> &Vec<SPNTarget> {
415 panic!("Not used by current object.");
416 }
417 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
418 &self.allowed_to_delegate
419 }
420 fn get_links(&self) -> &Vec<Link> {
421 panic!("Not used by current object.");
422 }
423 fn get_contained_by(&self) -> &Option<Member> {
424 &self.contained_by
425 }
426 fn get_child_objects(&self) -> &Vec<Member> {
427 panic!("Not used by current object.");
428 }
429 fn get_haslaps(&self) -> &bool {
430 &self.properties.haslaps
431 }
432
433 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
435 &mut self.aces
436 }
437 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
438 panic!("Not used by current object.");
439 }
440 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
441 &mut self.allowed_to_delegate
442 }
443
444 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
446 self.is_acl_protected = is_acl_protected;
447 self.properties.isaclprotected = is_acl_protected;
448 }
449 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
450 self.aces = aces;
451 }
452 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
453 }
455 fn set_allowed_to_delegate(&mut self, allowed_to_delegate: Vec<Member>) {
456 self.allowed_to_delegate = allowed_to_delegate;
457 }
458 fn set_links(&mut self, _links: Vec<Link>) {
459 }
461 fn set_contained_by(&mut self, contained_by: Option<Member>) {
462 self.contained_by = contained_by;
463 }
464 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
465 }
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize, Default)]
471pub struct ComputerProperties {
472 domain: String,
473 name: String,
474 distinguishedname: String,
475 domainsid: String,
476 isaclprotected: bool,
477 highvalue: bool,
478 samaccountname: String,
479 haslaps: bool,
480 description: Option<String>,
481 whencreated: i64,
482 enabled: bool,
483 unconstraineddelegation: bool,
484 trustedtoauth: bool,
485 lastlogon: i64,
486 lastlogontimestamp: i64,
487 pwdlastset: i64,
488 passwordnotreqd: bool,
489 pwdneverexpires: bool,
490 serviceprincipalnames: Vec<String>,
491 operatingsystem: String,
492 sidhistory: Vec<String>,
493 supportedencryptiontypes: Vec<String>,
494 #[serde(skip_serializing)]
495 is_dc: bool
496}
497
498impl ComputerProperties {
499 pub fn name(&self) -> &String {
501 &self.name
502 }
503 pub fn unconstraineddelegation(&self) -> &bool {
504 &self.unconstraineddelegation
505 }
506 pub fn enabled(&self) -> &bool {
507 &self.enabled
508 }
509 pub fn get_is_dc(&self) -> &bool {
510 &self.is_dc
511 }
512}