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 ) -> Result<(), Box<dyn Error>> {
105 let result_dn: String = result.dn.to_uppercase();
106 let result_attrs: HashMap<String, Vec<String>> = result.attrs;
107 let result_bin: HashMap<String, Vec<Vec<u8>>> = result.bin_attrs;
108
109 debug!("Parse computer: {result_dn}");
111
112 for (key, value) in &result_attrs {
114 trace!(" {key:?}:{value:?}");
115 }
116 for (key, value) in &result_bin {
118 trace!(" {key:?}:{value:?}");
119 }
120
121 let mut computer = Computer::new();
123
124 self.properties.domain = domain.to_uppercase();
126 self.properties.distinguishedname = result_dn;
127 self.properties.enabled = true;
128 self.domain_sid = domain_sid.to_string();
129
130 let mut sid: String = "".to_owned();
131 let mut group_id: String = "".to_owned();
132 for (key, value) in &result_attrs {
134 match key.as_str() {
135 "name" => {
136 let name = &value[0];
137 let email = format!("{}.{}",name.to_owned(),domain);
138 self.properties.name = email.to_uppercase();
139 }
140 "sAMAccountName" => {
141 self.properties.samaccountname = value[0].to_owned();
142 }
143 "dNSHostName" => {
144 self.properties.name = value[0].to_uppercase();
145 }
146 "description" => {
147 self.properties.description = Some(value[0].to_owned());
148 }
149 "operatingSystem" => {
150 self.properties.operatingsystem = value[0].to_owned();
151 }
152 "lastLogon" => {
170 let lastlogon = &value[0].parse::<i64>().unwrap_or(0);
171 if lastlogon.is_positive() {
172 let epoch = convert_timestamp(*lastlogon);
173 self.properties.lastlogon = epoch;
174 }
175 }
176 "lastLogonTimestamp" => {
177 let lastlogontimestamp = &value[0].parse::<i64>().unwrap_or(0);
178 if lastlogontimestamp.is_positive() {
179 let epoch = convert_timestamp(*lastlogontimestamp);
180 self.properties.lastlogontimestamp = epoch;
181 }
182 }
183 "pwdLastSet" => {
184 let pwdlastset = &value[0].parse::<i64>().unwrap_or(0);
185 if pwdlastset.is_positive() {
186 let epoch = convert_timestamp(*pwdlastset);
187 self.properties.pwdlastset = epoch;
188 }
189 }
190 "whenCreated" => {
191 let epoch = string_to_epoch(&value[0])?;
192 if epoch.is_positive() {
193 self.properties.whencreated = epoch;
194 }
195 }
196 "servicePrincipalName" => {
197 let mut result: Vec<String> = Vec::new();
199 for value in &result_attrs["servicePrincipalName"] {
200 result.push(value.to_owned());
201 }
202 self.properties.serviceprincipalnames = result;
203 }
204 "userAccountControl" => {
205 let uac = &value[0].parse::<u32>().unwrap();
207 let uac_flags = get_flag(*uac);
208 for flag in uac_flags {
210 if flag.contains("AccountDisable") {
211 self.properties.enabled = false;
212 };
213 if flag.contains("TrustedForDelegation") {
216 self.properties.unconstraineddelegation = true;
217 self.unconstrained_delegation = true;
218 };
219 if flag.contains("TrustedToAuthForDelegation") {
220 self.properties.trustedtoauth = true;
221 };
222 if flag.contains("PasswordNotRequired") {
223 self.properties.passwordnotreqd = true;
224 };
225 if flag.contains("DontExpirePassword") {
226 self.properties.pwdneverexpires = true;
227 };
228 if flag.contains("ServerTrustAccount") {
229 self.properties.is_dc = true;
230 self.is_dc = true;
231 }
232 }
233 }
234 "msDS-AllowedToDelegateTo" => {
235 let mut vec_members2: Vec<Member> = Vec::new();
239 for objet in value {
240 let mut member_allowed_to_delegate = Member::new();
241 let split = objet.split("/");
242 let fqdn = split.collect::<Vec<&str>>()[1];
243 let mut checker = false;
244 for member in &vec_members2 {
245 if member.object_identifier().contains(fqdn.to_uppercase().as_str()) {
246 checker = true;
247 }
248 }
249 if !checker {
250 *member_allowed_to_delegate.object_identifier_mut() = fqdn.to_uppercase().to_owned().to_uppercase();
251 *member_allowed_to_delegate.object_type_mut() = "Computer".to_owned();
252 vec_members2.push(member_allowed_to_delegate.to_owned());
253 }
254 }
255 self.allowed_to_delegate = vec_members2;
257 }
258 "ms-Mcs-AdmPwd" => {
260 info!(
263 "Your user can read LAPS password on {}: {}",
264 &result_attrs["name"][0].yellow().bold(),
265 &result_attrs["ms-Mcs-AdmPwd"][0].yellow().bold()
266 );
267 self.properties.haslaps = true;
268 }
269 "ms-Mcs-AdmPwdExpirationTime" => {
270 self.properties.haslaps = true;
272 }
273 "msLAPS-Password" => {
275 info!(
276 "Your user can read LAPS password on {}: {:?}",
277 &result_attrs["name"][0].yellow().bold(),
278 &value[0].yellow().bold()
279 );
280 self.properties.haslaps = true;
281 }
282 "msLAPS-EncryptedPassword" => {
283 info!(
284 "Your user can read uncrypted LAPS password on {} please check manually to decrypt it!",
285 &result_attrs["name"][0].yellow().bold()
286 );
287 self.properties.haslaps = true;
288 }
289 "msLAPS-PasswordExpirationTime" => {
290 self.properties.haslaps = true;
292 }
293 "primaryGroupID" => {
294 group_id = value[0].to_owned();
295 }
296 "IsDeleted" => {
297 self.is_deleted = true;
298 }
299 "msDS-SupportedEncryptionTypes" => {
300 self.properties.supportedencryptiontypes = convert_encryption_types(value[0].parse::<i32>().unwrap_or(0));
301 }
302 _ => {}
303 }
304 }
305
306 for (key, value) in &result_bin {
308 match key.as_str() {
309 "objectSid" => {
310 sid = sid_maker(LdapSid::parse(&value[0]).unwrap().1, domain);
312 self.object_identifier = sid.to_owned();
313
314 for domain_sid in OBJECT_SID_RE1.captures_iter(&sid) {
315 self.properties.domainsid = domain_sid[0].to_owned().to_string();
316 }
317 }
318 "nTSecurityDescriptor" => {
319 let relations_ace = parse_ntsecuritydescriptor(
321 &mut computer,
322 &value[0],
323 "Computer",
324 &result_attrs,
325 &result_bin,
326 domain,
327 );
328 self.aces = relations_ace;
329 }
330 "msDS-AllowedToActOnBehalfOfOtherIdentity" => {
331 let relations_ace = parse_ntsecuritydescriptor(
334 &mut computer,
335 &value[0],
336 "Computer",
337 &result_attrs,
338 &result_bin,
339 domain,
340 );
341 let mut vec_members_allowtoact: Vec<Member> = Vec::new();
342 let mut allowed_to_act = Member::new();
343 for delegated in relations_ace {
344 if *delegated.right_name() == "GenericAll" {
347 *allowed_to_act.object_identifier_mut() = delegated.principal_sid().to_string();
348 vec_members_allowtoact.push(allowed_to_act.to_owned());
349 continue
350 }
351 }
352 self.allowed_to_act = vec_members_allowtoact;
353 }
354 _ => {}
355 }
356 }
357
358 #[allow(irrefutable_let_patterns)]
360 if let id = group_id {
361 if let Some(part1) = SID_PART1_RE1.find(&sid) {
362 self.primary_group_sid = format!("{}{}", part1.as_str(), id);
363 } else {
364 eprintln!("[!] Regex did not match any part of the SID");
365 }
366 }
367
368 dn_sid.insert(
370 self.properties.distinguishedname.to_string(),
371 self.object_identifier.to_string(),
372
373 );
374 sid_type.insert(
376 self.object_identifier.to_string(),
377 "Computer".to_string(),
378 );
379
380 fqdn_sid.insert(
381 self.properties.name.to_string(),
382 self.object_identifier.to_string(),
383 );
384
385 fqdn_ip.insert(
386 self.properties.name.to_string(),
387 String::from(""),
388 );
389
390 Ok(())
393 }
394}
395
396impl LdapObject for Computer {
397 fn to_json(&self) -> Value {
399 serde_json::to_value(self).unwrap()
400 }
401
402 fn get_object_identifier(&self) -> &String {
404 &self.object_identifier
405 }
406 fn get_is_acl_protected(&self) -> &bool {
407 &self.is_acl_protected
408 }
409 fn get_aces(&self) -> &Vec<AceTemplate> {
410 &self.aces
411 }
412 fn get_spntargets(&self) -> &Vec<SPNTarget> {
413 panic!("Not used by current object.");
414 }
415 fn get_allowed_to_delegate(&self) -> &Vec<Member> {
416 &self.allowed_to_delegate
417 }
418 fn get_links(&self) -> &Vec<Link> {
419 panic!("Not used by current object.");
420 }
421 fn get_contained_by(&self) -> &Option<Member> {
422 &self.contained_by
423 }
424 fn get_child_objects(&self) -> &Vec<Member> {
425 panic!("Not used by current object.");
426 }
427 fn get_haslaps(&self) -> &bool {
428 &self.properties.haslaps
429 }
430
431 fn get_aces_mut(&mut self) -> &mut Vec<AceTemplate> {
433 &mut self.aces
434 }
435 fn get_spntargets_mut(&mut self) -> &mut Vec<SPNTarget> {
436 panic!("Not used by current object.");
437 }
438 fn get_allowed_to_delegate_mut(&mut self) -> &mut Vec<Member> {
439 &mut self.allowed_to_delegate
440 }
441
442 fn set_is_acl_protected(&mut self, is_acl_protected: bool) {
444 self.is_acl_protected = is_acl_protected;
445 self.properties.isaclprotected = is_acl_protected;
446 }
447 fn set_aces(&mut self, aces: Vec<AceTemplate>) {
448 self.aces = aces;
449 }
450 fn set_spntargets(&mut self, _spn_targets: Vec<SPNTarget>) {
451 }
453 fn set_allowed_to_delegate(&mut self, allowed_to_delegate: Vec<Member>) {
454 self.allowed_to_delegate = allowed_to_delegate;
455 }
456 fn set_links(&mut self, _links: Vec<Link>) {
457 }
459 fn set_contained_by(&mut self, contained_by: Option<Member>) {
460 self.contained_by = contained_by;
461 }
462 fn set_child_objects(&mut self, _child_objects: Vec<Member>) {
463 }
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize, Default)]
469pub struct ComputerProperties {
470 domain: String,
471 name: String,
472 distinguishedname: String,
473 domainsid: String,
474 isaclprotected: bool,
475 highvalue: bool,
476 samaccountname: String,
477 haslaps: bool,
478 description: Option<String>,
479 whencreated: i64,
480 enabled: bool,
481 unconstraineddelegation: bool,
482 trustedtoauth: bool,
483 lastlogon: i64,
484 lastlogontimestamp: i64,
485 pwdlastset: i64,
486 passwordnotreqd: bool,
487 pwdneverexpires: bool,
488 serviceprincipalnames: Vec<String>,
489 operatingsystem: String,
490 sidhistory: Vec<String>,
491 supportedencryptiontypes: Vec<String>,
492 #[serde(skip_serializing)]
493 is_dc: bool
494}
495
496impl ComputerProperties {
497 pub fn name(&self) -> &String {
499 &self.name
500 }
501 pub fn unconstraineddelegation(&self) -> &bool {
502 &self.unconstraineddelegation
503 }
504 pub fn enabled(&self) -> &bool {
505 &self.enabled
506 }
507 pub fn get_is_dc(&self) -> &bool {
508 &self.is_dc
509 }
510}