NH-RED-TEAM / RustHound

Active Directory data ingestor for BloodHound Legacy written in Rust. 🦀
MIT License
943 stars 90 forks source link

[FeatureRequest] Option to dumps all ldap attributes from root ldap #22

Closed 1mm0rt41PC closed 10 months ago

1mm0rt41PC commented 11 months ago

From a pentest I have found that a owned user was able to read an LAPS entry (new one msLAPS-Password) but not LAPS Legacy. But Rusthound like Bloodhound doesn't dump all attributes and associated ACE, so this path was not visible, I was blind...

The targeted user also had unusual ACLs in non-standard LDAP paths linked to SCCM in CN=Services,CN=Configuration.

Feature:

g0h4n commented 11 months ago

Hi @1mm0rt41PC,

I'm going to add the LAPS attributes relating to the encrypted version. This will warn the auditor whether or not if the user used can read the LAPS passwords stored for a computer object. https://learn.microsoft.com/fr-fr/windows-server/identity/laps/laps-technical-reference

As far as the non-standard "Configuration" part is concerned, version 2 of RustHound will automatically retrieve the namingContext in order to retrieve the objects associed (Container,Group,User,etc).

For example, if the domain has the following naming context CN=Configuration,DC=DOMAIN,DC=LOCAL then RustHound will automatically fetch the stored objects (useful for ADCS and SCCM for example) and the associated ACE.

For information, version 2 of RustHound is almost complete. It will feature a complete restructuring of the Rust code and compatibility with BloodHound-CE.

Version 2 of RustHound will also automatically generate the following ADCS-related files:

Thanks for your suggestion, I'll take it into account and incorporate it into version 2. :smiley:

1mm0rt41PC commented 11 months ago

Hi there!

Great news! I can't wait to see version 2.0, is there a channel/tag/branch alpha 2.0? To avoid multiple modifications of the code what do you think about managing the dump of attributes via a yaml conf file with why not an embedded version in the code.

With the format:

dump:
    objectClass:
        ? attribute_A // Dump attribute_A and ACE write only 
        secretAttribute: read // Dump only ACE ref to read

We can imagine:

version: 2

defaults: &commonAttrib
    ? name
    ? canonicalName
    ? objectSid
    ? objectGUID
    ? objectCategory
    ? displayName

dump:
    user:
        <<: *commonAttrib
        ? accountExpires
        ? adminCount
        ? badPasswordTime
        ? badPwdCount
        ? cn
        ? description
        ? distinguishedName
        ? isCriticalSystemObject
        ? lastLogoff
        ? lastLogon
        ? lastLogonTimestamp
        ? logonCount
        ? logonHours
        ? memberOf
        ? msDS-parentdistname
        ? msDS-PrincipalName
        ? primaryGroupID
        ? pwdLastSet
        ? sAMAccountName
        userAccountControl: {"parser": func_pimpMyParser}
        ? userPassword
        ? whenChanged
        ? whenCreated
        ? serviceprincipalname

    computers:
        <<: *commonAttrib
        msLAPS-Password: {"parser": "func_pimpMyPassword", "ACE":"read"}
        msLAPS-EncryptedPassword: read
        msLAPS-EncryptedDSRMPassword: read
        ? serviceprincipalname

    group:
        <<: *commonAttrib
        ? groupType

    dns:
        <<: *commonAttrib

    pKICertificateTemplate:
        <<: *commonAttrib
        ? msPKI-Cert-Template-OID
        ? msPKI-Enrollment-Flag
        ? msPKI-Minimal-Key-Size
        ? msPKI-Private-Key-Flag
        ? msPKI-RA-Application-Policies
        ? msPKI-RA-Signature
        ? msPKI-Template-Minor-Revision
        ? msPKI-Template-Schema-Version
        ? pKICriticalExtensions
        ? pKIDefaultCSPs
        ? pKIDefaultKeySpec
        ? pKIExpirationPeriod
        ? pKIKeyUsage
        ? pKIMaxIssuingDepth
        ? pKIOverlapPeriod

    certificationAuthority:
        <<: *commonAttrib

    dnsNode:
        <<: *commonAttrib

    domainDns:
        <<: *commonAttrib
        ? gPLink
        ? fSMORoleOwner
        ? maxPwdAge
        ? minPwdAge
        ? minPwdLength
        ? ms-DS-MachineAccountQuota
        ? pwdHistoryLength
        ? pwdProperties

    groupPolicyContainer:
        <<: *commonAttrib
        ? gPCFileSysPath
        ? gPCFunctionalityVersion
        ? gPCMachineExtensionNames

    organizationalUnit:
        <<: *commonAttrib
        ? gPLink
g0h4n commented 10 months ago

It's a good idea, but it would make version 2, which is almost finished, unusable.

I've changed everything to use structures dedicated to each object in the directory in order to optimise the execution of RustHound.

image

Each object is now structured as follows:

/// User structure
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct User {
   #[serde(rename = "ObjectIdentifier")]
   object_identifier: String,
   #[serde(rename = "IsDeleted")]
   is_deleted: bool,
   #[serde(rename = "IsACLProtected")]
   is_acl_protected: bool,
   #[serde(rename = "Properties")]
   properties: UserProperties,
   #[serde(rename = "PrimaryGroupSID")]
   primary_group_sid: String,
   #[serde(rename = "SPNTargets")]
   spn_targets: Vec<SPNTarget>,
   #[serde(rename = "Aces")]
   aces: Vec<AceTemplate>,
   #[serde(rename = "AllowedToDelegate")]
   allowed_to_delegate: Vec<Member>,
   #[serde(rename = "HasSIDHistory")]
   has_sid_history: Vec<String>,
   #[serde(rename = "ContainedBy")]
   contained_by: Option<Member>,
}

impl User {
   /// New User
   pub fn new() -> Self { 
      Self { ..Default::default() } 
   }

   /// Function to parse and replace value for user object.
   pub fn parse(
      &mut self,
      result: SearchEntry,
      domain: &String,
   ) {
      // parse all ldap attribut and change value in User struct
   }
  }
g0h4n commented 10 months ago

RustHound version 2.0 is out and the output is now compatible with BloodHound-CE

Changes can be find in v2 branch. :rocket:

https://github.com/NH-RED-TEAM/RustHound/commit/b445723c0330c5b3e5c237d5632625379bf48fbf

LAPS arguments: https://github.com/NH-RED-TEAM/RustHound/blob/v2/src/objects/computer.rs#L266

1mm0rt41PC commented 10 months ago

OMG ! Nice job ! Super-fast development speed 🤯

g0h4n commented 10 months ago

I'm closing the issue. In version 2, all the attributes are retrieved and can be analysed in debug mode rusthound ....... -vv to trace all the LDAP attributes of the retrieved object.