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