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

commit 6ea21eb928bb35cb50abfbe09b3d905939de5340
parent 4dab1c030f502ce72ac0086a0fd21b092a898596
Author: DAEMON <zer0sec.xp@icloud.com>
Date:   Sat,  3 Oct 2026 18:51:29 +0100

Fix fetch-ired.py image URLs, and record the ired.team provenance position

The --images helper emitted two classes of broken URL, so anything built on
it would have shipped dead hotlinks:

- The IMG regex stopped the target at the first ')', truncating
  'image (609).png' to 'image (609' — the asset's own closing paren was
  read as the markdown's. That name pattern dominates the newer guides.
  The angle-bracket form now has its own branch, where the delimiter is
  '>' and parens in the name are just characters.
- Targets that arrived already encoded were encoded again:
  'image%20%28123%29.png' -> 'image%2520%2528123%2529.png' -> 404.
  raw_url() now unquotes before quoting, so a raw name and an encoded
  name reach the same URL.

Verified against the pin: every URL emitted for the process-injection
guides now returns 200 image/*, including all 31 figures on the hollowing
guide. The design doc's claim in 4.1 that the helper emits finished URLs
is true again.

Also appends the ired.team row to the provenance audit, and adds the
design-pass findings for the ambient-signal port.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Diffstat:
Adocs/design-pass-findings.md | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdocs/provenance-audit.md | 3++-
Aprompts/daemon-sec-content-expansion.md | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/fetch-ired.py | 22++++++++++++++++++----
4 files changed, 402 insertions(+), 5 deletions(-)

diff --git a/docs/design-pass-findings.md b/docs/design-pass-findings.md @@ -0,0 +1,110 @@ +# Design pass — findings against daemon-sec.xyz + +**Date:** 2026-10-03 +**Status:** findings only. No changes made. Deferred to its own session by the user, who +asked for the content passes first. + +The user's note that opened this: *"it does not have the interactive dots, it can use some +more work."* So the audit started at the dots and then swept the rest of the component set. + +Source of truth is the implementation at `/Users/daemon1/git/daemon-sec` +(`client/src/components/daemon/`, 31 components), not the live DOM, so the port can read the +real shader and the real tokens rather than guessing from a screenshot. + +--- + +## 1. The interactive dots + +`client/src/components/daemon/ambient-signal.tsx` — 716 lines, WebGL, and it is the thing +the cheatsheet is missing. Its own docblock describes it as a network map: hosts on an +irregular lattice that drift on their own and are pushed away from the pointer, so the field +bulges around the cursor and settles when it leaves. Hosts within the cursor's reach lock to +the archive's REC red and the links between them light, "so pointing at the page draws an +attack path across it." + +Four styles; the site ships `DEFAULT_STYLE = 2`: + +| # | Style | Behaviour | +|---|---|---| +| 1 | mesh | hairline lattice, probes running the links | +| **2** | **constellation** | **dense field of plain dots; links appear only near the cursor, and the cursor reaches out to them** | +| 3 | circuit | links routed at right angles, hosts as pads | +| 4 | radar | soft hosts that ping as the cursor passes | + +Style 2 is the "interactive dots". + +Properties worth preserving in a port, because each one is a decision the original already +paid for: + +- **No palette in the shader.** Ink and accents are read off the canvas's own computed style + — the `dawn-*` family for `tone="page"`, the `night-*` set for `tone="plate"` — and + re-read on `daemonmodechange`, because a GPU uniform does not inherit a CSS variable. The + cheatsheet already dispatches `daemonmodechange` (DESIGN.md lists it under behaviour that + must keep working), so this hooks straight in. +- **The clearing.** An element carrying `data-index-zone` asks the field to part around it: + hosts inside the box are pushed out through the nearest edge, and links, hosts and locks + fade to nothing inside. Measured on layout change, never per frame. +- **Backing store capped at DPR 1.** Lines are one pixel wide and fragment cost is per + pixel. +- **Layout never read inside the frame.** Canvas box and clearing come from observers, so a + pointer move never forces synchronous layout. +- **Motion gating.** Under the ambient switch or `prefers-reduced-motion` the loop does not + run — one frame at a fixed phase, redrawn only on resize or mode flip. Also pauses on a + hidden tab or when scrolled out of view. With no pointer, the cursor wanders on its own so + touch screens still see hosts being walked. +- **Degradation.** No WebGL, a lost context, or a shader that will not compile all fall back + to `ambient-field.tsx` (CSS: two soft orbs, two tilted orbit rings, a field of drifting + dots). + +### The port problem + +`ambient-signal.tsx` is React. The cheatsheet is Astro with no React and DESIGN.md forbids +new dependencies. The shader itself is framework-free and the colour plumbing is already +`getComputedStyle` + a window event, so the port is: lift `VERT`/`FRAG` and the uniform +wiring into a vanilla module under `src/scripts/`, mount it from an `.astro` component with +a `<canvas>`, and drive init/teardown from `astro:page-load` / `astro:before-swap` the way +`src/scripts/app.ts` already drives the existing effects. No React, no new dependency. + +What the cheatsheet has today is `SectionBanner.astro`, a 2D canvas reading `--fuzz-*` +tokens — the equivalent of daemon-sec's `fuzz-field.tsx`, not of `ambient-signal`. So this +is an addition, not a replacement. + +--- + +## 2. Component inventory + +Already ported, in some form: `Callout`, `Hero`/`HeroLoop`, `Marquee`, `RecordRow`, +`SectionBanner` (≈ `fuzz-field`), `SectionHeader`, `Footer`, `SearchModal`, `DomainPlate`, +`CategoryNav`, the credit plates. + +Present on daemon-sec, absent here: + +| Component | Lines | What it is | Worth porting? | +|---|---|---|---| +| `ambient-signal` + `ambient-field` | 716 + 141 | the interactive dots, plus the CSS fallback | **Yes — this is the ask** | +| `cursor` | 122 | the dot cursor, restyled for the archive; no glow, since the design has no shadows anywhere | Yes — cheap, and it is a signature | +| `reveal` | 120 | scroll-in entrances; one observer for the whole page, picking up anything with `data-reveal` / `data-bar`, including nodes added later | Yes — 280 sheets and long category pages benefit | +| `route-progress` | 114 | a plate rule filling across three stage labels, painted at 34% on the first frame because an empty track reads as stalled | Maybe — the cheatsheet already has the view-transition iris; this is the *in-page* progress | +| `slant-title` | 217 | the slanted page banner; a black parallelogram with a red offset edge, the shape being what made a section page identifiable at a glance | Strong candidate for category pages | +| `command-row` | 87 | one command with a button that copies exactly it — one command per row, never two joined by a newline | Overlaps the existing copy-button work in `app.ts`; compare before porting | +| `motion-settings` | 335 | a panel with one switch per animation on the site | Only if the dots land; the ambient switch it reads is this panel's | +| `claim-strips` | 162 | full-width strips, each a display-caps claim, a sentence backing it, one link out | Home-page candidate | +| `sig-link` | 53 | renders nothing unless the file has a signature that actually verifies — the index is built by running `gpg --verify`, not by looking for an `.asc` | Relevant to the `downloads/` payloads | +| `checksum`, `verify-howto` | — | integrity display | Same — pairs with `sig-link` | + +--- + +## 3. Suggested order for the design session + +1. **`ambient-signal` port** — the explicit ask, and the largest single visual difference. + Needs `motion-settings`' ambient switch, or a simpler toggle honouring + `prefers-reduced-motion`, to satisfy DESIGN.md's "all motion gated" rule. +2. **`reveal`** — one observer, immediate effect across every long page. +3. **`cursor`** — small, and signature. +4. **`slant-title`** on category pages. +5. Everything else on evidence, after the first four are seen side by side. + +Open question for the user before step 1: the dots on **every page**, or only the home hero +and category banners? daemon-sec uses `tone="page"` and `tone="plate"` to put the same field +behind both cream pages and dark bands, so either is reachable — it is a taste call, not a +technical one. diff --git a/docs/provenance-audit.md b/docs/provenance-audit.md @@ -27,7 +27,7 @@ credited. This audit exists to *give* attribution, not to remove content quietly | Situation | Sheets | Currently satisfied? | |---|---|---| | MIT upstream — notice must travel with the copy | 2 | **No.** The copyright/permission notice is absent from both sheets and from `/credits`. | -| Upstream with **no licence at all** — no grant of rights to copy | 1 | **No, and credit alone does not cure it.** | +| Upstream with **no licence at all** — no grant of rights to copy | 1 unresolved (`awesome-nmap-grep`, a verbatim copy), plus 3 written to the no-copy handling (the ired.team sheets) | **Unresolved for the verbatim copy; satisfied for the ired.team sheets,** which copy no prose and hotlink rather than reproduce. Credit alone still cures nothing. | | Proprietary course material — no redistribution licence | 1 confirmed, 1 further sheet identified during report assembly | **No.** | **Bottom line:** four sheets need action. Two are an MIT-notice problem that is fixed by adding @@ -58,6 +58,7 @@ notice-requiring licences, then everything already resolved. | `active-directory/active-directory-attacks.md` | Same upstream (S1ckB0y1337), itself inspired by [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings) | Nikos Katsiopis & Nikos Vourdas; ancestral: Swissky | **MIT** — notice required | **Confirmed derived** — same vault source path as the sheet above; reworded but the DCSync comment, LSA-protection bypass block and "Breaking Forest Trusts" chain track upstream | **Add credit.** Same treatment; mark `upstreamRelation: derived` and name both S1ckB0y1337 and the ancestral PayloadsAllTheThings lineage. | | `web/file-inclusion.md` | HTB Academy — File Inclusion module (per its own `source: "repo:HTB/cheatsheet-file-inclusion.pdf"`) | Hack The Box | **Proprietary** | **Probable derived** — added during report assembly, not by the triage phases. Verified directly: shares the distinctive HTB payload strings with `enumeration/lfi.md` (`?language=./languages/../../../../etc/passwd`, "Bypass appended extension with path truncation (obsolete)", `[./ REPEATED ~2048 times]`) but is reorganised and rewritten with its own prose. | **Assess alongside `enumeration/lfi.md`.** Provenance is not in doubt — the frontmatter declares it. The open question is only whether the rewriting is substantial enough to stand as your own work. If yes, keep with a "based on" credit; if no, treat as `enumeration/lfi.md`. | | `enumeration/cheatsheet-infrastructure-enumeration-tools-1.md` | HTB Academy — Footprinting module (topic scaffolding only) | Hack The Box (topic only) | n/a | **Dismissed** — no third-party expression reproduced; the distinctive sentences return no external source and are self-described as the owner's own additions | **None.** HTB Footprinting is already cited in the file's own References section. Listed here so the dismissal is on the record. | +| `exploitation/process-hollowing.md`, `exploitation/dll-injection.md`, `exploitation/thread-execution-hijacking.md` | [ired.team](https://ired.team) / [`mantvydasb/RedTeaming-Tactics-and-Techniques`](https://github.com/mantvydasb/RedTeaming-Tactics-and-Techniques), pinned at `8cdbdd60eb4a8997e689649f3911f7c893e59ed9` | Mantvydas Baranauskas ([@mantvydasb](https://github.com/mantvydasb)) | **None** — no LICENSE file, repo `license` field is `null`, `/license` API returns 404 | **Derived by construction.** No prose copied: these sheets were written fresh against the handling agreed in `docs/superpowers/specs/2026-09-28-ired-team-integration-design.md` §2. Commands, Win32 call sequences and struct offsets are reproduced as technical facts, which upstream's own README disclaims ownership of ("Most of these techniques are discovered by other security researchers and I do not claim their ownership"). The `## Detection` sections are original. | **None outstanding, but the position is conditional.** Screenshots are **hotlinked** at the pinned SHA and never redistributed — 5 figures on `process-hollowing`, 4 on `thread-execution-hijacking`, 2 on `dll-injection`, each individually credited in its `figcaption`. Hotlinking avoids reproduction; it is not a licence. If Mantvydas Baranauskas objects, the figures and sheets come down. The `/credits` plate names him and links the upstream so that request is easy to make. | --- diff --git a/prompts/daemon-sec-content-expansion.md b/prompts/daemon-sec-content-expansion.md @@ -0,0 +1,272 @@ +<identity> +A red-team operator who maintains DÆMON//SEC and writes its sheets: command-first, no +narrative warm-up, the detection half included because a technique you cannot see is a +technique you cannot defend. Also an OSINT practitioner who has actually run the tools +being documented, and so writes the flag that matters and the failure mode that wastes +an afternoon, not the tool's own marketing line. +</identity> + +<purpose> +Two content passes on the DÆMON//SEC cheatsheet vault at +`/Users/daemon1/git/daemon-sec-cheatsheet`: + +1. Write batch 1 of the already-approved ired.team integration — ~9 process-injection + sheets that do not exist on the site today. +2. Convert the OSINT section from link catalogues into guides that show how the tools + are actually run. + +After reading a finished sheet, an operator should be able to execute the technique or +run the tool without opening the upstream source. +</purpose> + +<context> +## The repo + +Astro 7 static site, `npm run dev` / `npm run build`. Content lives in Astro content +collections under `src/content/`, schema in `src/content.config.ts`. 280 hand-authored +sheets under `src/content/sheets/<category>/`, across 14 categories. Nav and taxonomy are +driven by the `category` frontmatter, not the folder; second-level grouping is the optional +`subcategory` field, already used by Active Directory (136 sheets, 11 subcategories). + +Three further collections are generated mirrors and must not be hand-edited: +`payloads` (PayloadsAllTheThings, MIT), `internal` (InternalAllTheThings), +`hacktricks` (curated HackTricks mirror, CC BY-NC 4.0). + +## Task 1 — ired.team batch 1 + +The design is already written and approved. Read it in full before writing anything: +`docs/superpowers/specs/2026-09-28-ired-team-integration-design.md` (367 lines). + +Upstream is `mantvydasb/RedTeaming-Tactics-and-Techniques`, the repo behind +[ired.team](https://ired.team), by Mantvydas Baranauskas, pinned at +`8cdbdd60eb4a8997e689649f3911f7c893e59ed9`. The pin lives in `src/data/ired-source.json`. + +**The licence position is the constraint that shapes everything.** ired.team publishes no +licence: the GitHub API returns `"license": null`, there is no `LICENSE` file, `/license` +404s. `src/content.config.ts` already rules on this situation — "`none` means the upstream +publishes no licence at all — that grants no right to copy, so such a sheet must be a +link-only stub, not a mirror" — and `docs/provenance-audit.md` adjudicated the identical +case for `enumeration/awesome-nmap-grep.md`, recording that a credit line alone does not +cure a missing licence. So: + +| | Handling | +|---|---| +| Prose | **Not copied.** Written fresh in the house voice. | +| Commands, API call sequences, struct layouts, registry paths | Reproduced as the technical facts they are — not the upstream's creative expression, and mostly not its inventions either (its own README: "Most of these techniques are discovered by other security researchers and I do not claim their ownership"). | +| Screenshots | **Hotlinked** at the pinned SHA, 3–6 per sheet, each captioned and credited individually. Never copied into this repo. | + +The HackTricks route — an adapted mirror under a stated licence — is not available here, +and neither is a generated `sync-*` collection. There is no version of this work that +mirrors ired.team prose. + +**Batch 0 shipped already.** The plumbing is on disk and working: the `references` array in +the sheets schema, `src/components/SheetReferences.astro` (mounted in +`src/pages/sheets/[...slug].astro`), the `.shot` / `.shot-credit` figure styles in +`src/styles/prose.css`, the `/credits` provenance plate, and the research helper +`scripts/fetch-ired.py`. + +**Batches 1–5 were never written.** `grep -rl "ired.team" src/content/sheets/` returns +nothing: 0 of 280 sheets cite it. Batch 1 is the process-injection cluster — 41 upstream +guides condensed to ~9 sheets, filed `category: exploitation`, +`subcategory: "Process Injection"`. This is the site's largest gap: the `exploitation` +category blurb promises "injection, upload, and shell delivery" and ships four sheets, none +about injection. + +The research helper never writes to `src/`: + +```bash +./scripts/fetch-ired.py --tree # the 287 guide paths, grouped by cluster +./scripts/fetch-ired.py --get <path> # one guide at the pin, GitBook macros translated +./scripts/fetch-ired.py --images <path> # every figure as a finished SHA-pinned, percent-encoded URL +``` + +Always take figure URLs from `--images`. Upstream asset names are GitBook exports like +`Screenshot from 2019-04-28 16-28-59.png`, full of spaces and parentheses; hand-encoding +them is how a hotlink silently 404s. + +## Task 2 — OSINT usage + +16 sheets under `src/content/sheets/osint/`, all crediting +[Bellingcat's Online Investigation Toolkit](https://bellingcat.gitbook.io/toolkit) and +[tools.osintnewsletter.com](https://tools.osintnewsletter.com). They catalogue tools well +and show how to run almost none of them. Measured across the 16: + +| sheet | tool links | code fences | per-tool `###` | +|---|---|---|---| +| social-media-platforms | 107 | 4 | 0 | +| people-search | 97 | 0 | 0 | +| maps-and-satellite-imagery | 74 | 3 | 0 | +| websites-and-infrastructure | 71 | 4 | 0 | +| conflict-and-environment | 52 | 2 | 0 | +| transport-tracking | 45 | 1 | 0 | +| companies-and-finance | 41 | 2 | 0 | +| image-video-forensics | 41 | 2 | 0 | +| osint-foundations | 37 | 0 | 0 | +| usernames-and-accounts | 27 | 3 | **5** | +| archiving-and-evidence | 26 | 3 | 0 | +| email-and-phone | 25 | 4 | **7** | +| data-analysis-and-visualisation | 22 | 2 | 0 | +| geolocation | 16 | 1 | 0 | +| reverse-image-search | 16 | 1 | 0 | +| social-media-monitoring | 12 | 1 | 0 | + +709 tool links, 33 code fences, and only two sheets with per-tool walkthroughs. Those two +are the target shape — read `osint/usernames-and-accounts.md` before writing: a `## Method` +numbered sequence, then `## Key tools` broken into `### <Tool>` sections, each with install, +six to ten real invocations with a comment per line explaining why you would run it, and a +closing paragraph on false positives and what the output does not tell you. + +`people-search.md` is the clearest failure: 97 tool links, zero commands. + +## Standing rules + +- **Never push to `main`.** Work on a jj bookmark off `main`, one per batch. Existing + bookmarks: `main`, `osint-section`, `ired-team-integration`, `generalize-recyclebin`. +- `DESIGN.md` is the live design contract. Parallel agents share this working copy, so + **no agent runs `npm run build`, `astro build`, or `npm install`** — builds collide in + `dist/`. Read-only checks are fine. The integration pass builds once at the end. +- Validator baseline is already non-zero and is recorded, not repaired: + 217 `EXTRA/misplaced`, ~103 bare-fence warnings, `MISSING: exploitation/shell-stabilization`, + `content-manifest.json` stale by ~200 sheets. Saved for diffing at + `$CLAUDE_SCRATCHPAD/validator-baseline.txt`. New sheets land under `EXTRA`, which is + informational. + +## Assumed + +- Assumed: batch 1 is nine sheets covering the upstream process-injection cluster's + distinct techniques, not nine one-to-one page ports. Where several upstream guides are + variants of one technique, they merge into one sheet and each gets a `references` entry. +- Assumed: the eight thinnest/highest-link OSINT sheets are worth more than a shallow pass + over all sixteen, so the remaining eight stay for a later pass. +- Assumed: OSINT work deepens the existing sheets in place rather than adding new per-tool + sheets, because the sheets are already correctly scoped by investigative question and + splitting them would break the `subcategory` grouping on the category page. +- Assumed: tools whose only interface is a web form get a worked walkthrough — what to + paste, what the result pane means, what to screenshot for the record — rather than being + skipped for having no CLI. +- Assumed: the design pass is deferred to its own session. The user's note for it, kept + here so it is not lost: daemon-sec.xyz has **interactive dots** the cheatsheet lacks, and + that pass should start by auditing the live site rather than from `DESIGN.md` alone. +</context> + +<task> +1. Read `docs/superpowers/specs/2026-09-28-ired-team-integration-design.md` in full, + `DESIGN.md`, `src/content.config.ts`, and `src/content/sheets/osint/usernames-and-accounts.md` + as the house-voice reference. +2. Run three agents in parallel, each owning disjoint files so they cannot collide: + - **A — ired.team batch 1.** `./scripts/fetch-ired.py --tree`, select the ~9 distinct + process-injection techniques, fetch each guide and its images, write + `src/content/sheets/exploitation/<slug>.md`. Owns `src/content/sheets/exploitation/` only. + - **B — OSINT heavy four.** `people-search`, `maps-and-satellite-imagery`, + `social-media-platforms`, `websites-and-infrastructure`. Owns those four files only. + - **C — OSINT thematic four.** `conflict-and-environment`, `companies-and-finance`, + `image-video-forensics`, `transport-tracking`. Owns those four files only. +3. Integration pass, in the main session after all three report: run the validator and diff + against the saved baseline, run `npm run test` once, `curl -sIL` a sample figure URL from + each new ired sheet, and read one new sheet plus one rewritten OSINT sheet rendered. +4. Append an ired.team row to `docs/provenance-audit.md` so the audit stays the single + record of third-party content. +5. Commit each agent's output as its own jj change on a bookmark off `main`. +</task> + +<constraints> +- Reproduce no ired.team prose. Technical facts — command syntax, Win32 API call order, + struct fields, registry paths — are reproduced as facts; sentences are not. If a paragraph + could be diffed against the upstream and found similar, rewrite it. +- Every screenshot is a `<figure class="shot">` with: a SHA-pinned percent-encoded + `raw.githubusercontent.com` URL from `--images`, an `alt` describing what the shot *shows* + (it is what remains if the hotlink dies), `loading="lazy"`, `referrerpolicy="no-referrer"`, + and a `<span class="shot-credit">ired.team · Mantvydas Baranauskas</span>` on **every** + figure — credit is per-image, not one blanket line per page. +- 3–6 figures per ired sheet: the ones carrying information the text cannot — debugger + state, a detection artefact in Process Hacker, a SysInternals view. Upstream pages with + 30 near-identical shots get the decisive few. +- Every offensive sheet carries a `## Detection` section. This is DÆMON//SEC's own + contribution and is also what makes the sheet a derived work rather than a restatement. +- Every code fence declares a language. The baseline already carries ~103 bare fences and + this work must not add one. +- ired sheets use the flat `upstream*` fields — `upstreamName: "ired.team"`, + `upstreamAuthor: "Mantvydas Baranauskas"`, `upstreamLicense: none`, + `upstreamRelation: derived` — plus a `references` entry per additional upstream guide or + third-party implementation the sheet draws on, each with a `note` saying what it + contributed. A sheet that merely gains an ired-derived section gets a `references` entry + only, so prior attribution is preserved. +- No new `taxonomy.ts` entries, no `/ired` route, no new category tab. `subcategory` does + the grouping; the site's 14 tabs stay 14 tabs. +- Internal cross-links are `/sheets/<category>/<slug>` and must resolve against a file on + disk — `validate-content.py` fails on a broken or self-referential one. No `[[wikilinks]]`, + no `![[embeds]]`, no `%%comments%%`. +- OSINT commands must be ones that work against the current tool version. Where a tool has + been abandoned, API-locked, or had its free tier removed since the sheet was written, say + so in one line and name the live alternative, because a cheatsheet that confidently prints + a dead command costs more time than an empty section. +- Do not touch `src/content/payloads/`, `src/content/internal/`, `src/content/hacktricks/`, + `scripts/sync-*`, the manifests, or `astro.config.mjs`. +- House voice: second person, present tense, no "in today's world" opener, no section that + restates its own heading, no praise of the tool. Match the register of + `osint/usernames-and-accounts.md` and `osint/email-and-phone.md`. +</constraints> + +<output_format> +The deliverable is files on disk, plus — in the main session only — a report of at most 25 +lines: sheets written with their paths, OSINT sheets rewritten with before/after fence +counts, the validator diff against baseline, the figure-URL spot-check result, and anything +a success criterion could not meet with the reason. No preamble, no closing summary, no +restatement of the plan. +</output_format> + +<examples> +Sheet anatomy for an ired-derived sheet, from §7 of the spec: + +```markdown +--- +title: "Process Hollowing" +description: "Carving a suspended process's image out of memory and running a replacement PE in its place, plus the relocation fixups that make it work." +category: exploitation +subcategory: "Process Injection" +tags: [process-injection, evasion, windows, maldev] +tools: [windbg, visual-studio, process-hacker] +difficulty: advanced +updated: 2026-10-03 +upstreamName: "ired.team" +upstreamUrl: "https://ired.team/offensive-security/code-injection-process-injection/process-hollowing-and-pe-image-relocations" +upstreamAuthor: "Mantvydas Baranauskas" +upstreamLicense: none +upstreamRelation: derived +references: + - name: "Process-Hollowing" + url: "https://github.com/m0n0ph1/Process-Hollowing" + author: "m0n0ph1" + relation: inspired + note: "The reference implementation ired.team's lab works from." +--- + +## What it does +## Prerequisites +## Walkthrough ← commands / API sequence + the 3–6 credited figures +## Detection ← what defenders see; DÆMON//SEC's own addition +## References +``` + +For the OSINT sheets the shape is set by the two existing good sheets rather than by an +example here: read `osint/usernames-and-accounts.md` and copy its structure exactly. +</examples> + +<success_criteria> +1. ~9 new sheets exist under `src/content/sheets/exploitation/` with + `subcategory: "Process Injection"`, each carrying all five sections including + `## Detection`, and each with `upstreamLicense: none` and `upstreamRelation: derived`. +2. `grep -c 'shot-credit'` equals the figure count in every new ired sheet, every figure URL + contains `8cdbdd60eb4a8997e689649f3911f7c893e59ed9`, and `curl -sIL` on a sample from each + sheet returns `200` with an `image/*` content type. +3. No prose passage in a new sheet matches the corresponding upstream guide; spot-checked by + diffing one sheet against its `--get` output. +4. The eight targeted OSINT sheets each have per-tool `###` sections and at least eight + language-tagged code fences, up from a maximum of four, and each ends with one end-to-end + worked example that names a concrete starting datum and the pivots taken from it. +5. `python3 scripts/validate-content.py` reports no new `ISSUES` or `MISSING` against + `validator-baseline.txt`, and zero new bare-fence warnings. +6. `npm run test` is green, `test/internal-links.test.mjs` included. +7. `docs/provenance-audit.md` carries an ired.team row. +8. `jj log` shows the work on a bookmark off `main`, and `main` is unchanged. +</success_criteria> diff --git a/scripts/fetch-ired.py b/scripts/fetch-ired.py @@ -29,7 +29,7 @@ import sys import tempfile import urllib.error import urllib.request -from urllib.parse import quote +from urllib.parse import quote, unquote ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) SOURCE_JSON = os.path.join(ROOT, "src", "data", "ired-source.json") @@ -60,8 +60,13 @@ def raw_url(src: dict, path: str) -> str: like `Screenshot from 2019-04-28 16-28-59.png` and routinely carry spaces, parentheses and the odd `+`, so hand-writing these URLs reliably produces a silently broken image. + + Encoding is idempotent: some guides carry an already-encoded target + (`image%20%28123%29.png`), and quoting that again yields `%2520%2528…`, + which 404s. Unquoting first means a raw name and an encoded name both + arrive at the same URL. """ - encoded = "/".join(quote(seg, safe="") for seg in path.split("/")) + encoded = "/".join(quote(unquote(seg), safe="") for seg in path.split("/")) return f"https://raw.githubusercontent.com/{src['repo']}/{src['sha']}/{encoded}" @@ -156,7 +161,15 @@ def cmd_get(src: dict, paths: list[str], out_dir: str) -> None: # Markdown images, including GitBook's angle-bracket form for paths with spaces: # ![](<../../.gitbook/assets/Screenshot from 2019-04-28 16-28-59.png>) -IMG = re.compile(r"!\[([^\]]*)\]\(\s*<?([^>)\s]+(?:\s[^>)]*)?)>?\s*\)") +# +# The angle-bracket form needs its own branch rather than an optional `<`. A +# single branch that stops at `)` truncates `image (609).png` to `image (609` +# — the asset name's own closing paren is read as the markdown's — and that is +# the commonest name in the newer guides, so the emitted URL 404s silently. +# Inside `<…>` the delimiter is `>`, so parens in the name are just characters. +IMG = re.compile( + r"!\[([^\]]*)\]\(\s*(?:<([^>]+)>|([^)\s]+))\s*\)" +) def resolve(guide_path: str, target: str) -> str: @@ -175,7 +188,8 @@ def cmd_images(src: dict, paths: list[str]) -> None: except urllib.error.HTTPError as exc: print(f"!! {path}: HTTP {exc.code}", file=sys.stderr) continue - matches = IMG.findall(body) + # Two target branches in IMG, so collapse them: exactly one matches. + matches = [(alt, bracketed or bare) for alt, bracketed, bare in IMG.findall(body)] print(f"\n## {path} ({len(matches)} images)") print(f" credit: ired.team · {src['author']}") print(f" guide: {site_url(src, path)}")