Coffee Invocation
Coffee Invocation is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Coffee Invocation attack path
Coffee Invocation is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Objective
Challenge walkthrough focused on Reversing evidence, validation, and reusable operator lessons.
Walkthrough flow
Extract the HTB archive with the standard archive...
Use strings and byte search to find embedded CAFEBABE...
Carve the embedded classes and decompile them with...
Treat the Java output as incomplete because the...
Model the native JNI side effects:
Source coverage
High source coverage
Status: complete. This article is generated from 4 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- Reversing/Coffee-Invocation/writeup.md
- htb-challenge/Reversing/Coffee-Invocation/notes.md
- htb-challenge/Reversing/Coffee-Invocation/memory-summary.md
- htb-challenge/Reversing/Coffee-Invocation/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Coffee-Invocation
- Category: Reversing
- Difficulty: Medium
- Mode: file
Summary
Coffee Invocation is a stripped Linux ELF that embeds and launches a JVM through JNI. The binary contains two carved Java classes, Verify1 and Verify2, but the Java source alone is misleading because the native wrapper mutates Java wrapper-object caches and intercepts System.exit. Modeling those JNI side effects recovers the two validated flag-body halves, which the native binary prints inside the standard HTB flag wrapper.
Artifact Inventory
The original archive is stored unmodified at files/a12c73a2-1983-42ae-b5b8-8b77e6d4db8d.zip. It extracts to one stripped x86-64 Linux PIE executable:
files/extracted/rev_coffee_invocation/coffee_invocation
The executable dynamically links libjvm.so, embeds two Java class files at CAFEBABE offsets, and stores the native transformation tables in .data. Extraction and triage evidence is in analysis/unzip.txt, analysis/strings.txt, analysis/objdump-x.txt, and analysis/objdump-d.txt.
Analysis
Static strings showed JVM symbols, Verify1, Verify2, Tinfoil, and the flag-printing prefix. Carving the two CAFEBABE blobs produced valid Java class files in analysis/classes/.
JADX recovered the Java checks:
Verify1compares a source string against a target string byte by byte.Verify2compares sorted two-character chunks against slices of a shuffled constant and callsSystem.exit(i + 3)on each matched pair.
The native wrapper is the real logic. It creates the JVM, registers a native replacement for java/lang/Shutdown.halt0, mutates cached wrapper objects, and then runs the Java classes. The important native effects are:
Byte.valueandShort.valuecaches are rewritten beforeVerify1, so the first 26 input bytes must be recovered by inverting the native byte/short maps.Boolean.TRUEandBoolean.FALSEbehavior is swapped beforeVerify2, changing which side is sorted.Character.valuecache mappings are rotated after each interceptedSystem.exit(i + 3), so the second 26 input bytes must be recovered one pair at a time from the staged native tables.- The native success path prints the standard HTB flag prefix, then the first 52 bytes of the provided argument, then the closing brace.
The solver models those native side effects directly from the ELF data segment instead of executing the Linux binary.
Solve
Run:
cd <local workspace>
python3 solve/solve.py --output loot/flag-candidate.txtThen capture through the harness:
cd <local workspace>
python3 scripts/challenge_harness.py capture-flag Reversing/Coffee-Invocation --from loot/flag-candidate.txtThe solver parses the ELF, reads the native transform tables, reconstructs both 26-byte validated substrings, and writes the final HTB-format candidate to loot/flag-candidate.txt.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- Embedded Java bytecode can be a decoy if the native JNI host mutates JVM runtime state.
System.exitcan become a control-flow oracle when the native host replacesShutdown.halt0.- Wrapper caches such as
Byte,Short,Character, andBooleanare valid attack surfaces in JNI-heavy reversing challenges. - When local execution is unavailable, model the native data tables and side effects directly rather than guessing from decompiled Java alone.
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Scope
- Challenge: Coffee-Invocation
- Category: Reversing
- Difficulty: Medium
- Mode: file
- Remote instance: none
- Start time: 2026-06-13T05:28:12Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c73a2-1983-42ae-b5b8-8b77e6d4db8d.zip | 9356 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-13T05:28:12Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-13T05:28:22Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-13T05:28:49Z | hypothesis recorded | hypothesis-board.md | Reverse the coffee_invocation artifact, identify JVM/native entrypoints and any encoded recipe/flag validation logic, then build a decoder or keygen. | Medium | Run file/strings/binwalk-style triage, inspect embedded class/JAR/native sections, then execute only after understanding input/output behavior. |
| 2026-06-13T05:29:05Z | research skip | analysis/research/research-skip.md | Research intentionally skipped with recorded reason | Medium | Gate before exploit |
| 2026-06-13T05:29:05Z | instrumentation plan | analysis/instrumentation-plan.md | Recover the flag or <password redacted> logic from the coffee_invocation artifact and produce a reproducible decoder. | High | After two failed reverse paths without a new artifact fact, record the failure and revisit tool choice rather than guessing flags. |
| 2026-06-13T05:36:00Z | static model | analysis/static-solve-model.txt | Modeled JNI cache mutation and staged System.exit handling to recover the validated flag body without executing the Linux ELF | High | Write reproducible solver |
| 2026-06-13T05:36:20Z | solve script | solve/solve.py | Reproducible solver parses the ELF data tables and writes an HTB-format candidate to loot | High | Capture flag through harness |
| 2026-06-13T05:36:40Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-13T05:38:54Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- The executable is a stripped x86-64 Linux ELF that embeds a JVM and two Java class files.
- The embedded Java classes were carved from
CAFEBABEoffsets and decompiled with JADX. - The native JNI wrapper mutates Java
Byte,Short,Boolean, andCharacterruntime state, so the Java decompile alone is misleading. - The final flag body is the concatenation of two 26-byte validated substrings recovered from native data tables.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: Reversing
- Challenge: Coffee-Invocation
- Difficulty: Medium
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Extract the HTB archive with the standard archive password and identify the single stripped x86-64 Linux ELF.
- Use strings and byte search to find embedded
CAFEBABEJava class files. - Carve the embedded classes and decompile them with JADX to understand
Verify1andVerify2. - Treat the Java output as incomplete because the native wrapper creates the JVM and mutates runtime state through JNI.
- Model the native JNI side effects:
- Byte and Short cache value mutation for the first verification stage.
- Boolean behavior swapping for the second stage.
- staged Character cache mutation driven by intercepted System.exit codes.
- Reconstruct the two 26-byte flag-body halves from native
.datatables and write the HTB-format candidate toloot/. - Capture the flag through the challenge harness.
Reusable Lessons
- For JNI-hosted Java reversing challenges, inspect the native host before trusting decompiled Java conditions.
- Search for embedded class magic bytes in native wrappers that link against
libjvm.so. - If Java code calls
System.exit, inspect whether native code registers or replaces shutdown/halt handlers. - Java wrapper caches can be mutated from JNI and can change apparently simple Java comparisons.
- Keep recovered flag material out of
analysis/; store raw values only inloot/.
Dead Ends
- Running the Linux ELF locally was not available on macOS because no
qemu-x86_64was installed and Docker did not respond promptly. - Treating
Tinfoilas the real password is misleading; the native wrapper changes Boolean behavior and uses staged exit-code side effects.
Tool Quirks
- Local Java runtime was unavailable, so
javapcould not be used. - JADX was available and successfully decompiled the carved
.classfiles. - A faithful Python model was faster and cleaner than setting up Linux runtime execution.
Evidence Paths
analysis/strings.txtanalysis/objdump-x.txtanalysis/objdump-d.txtanalysis/classes/Verify1.classanalysis/classes/Verify2.classanalysis/jadx/sources/defpackage/Verify1.javaanalysis/jadx/sources/defpackage/Verify2.javaanalysis/static-solve-model.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes, after user approval
- Requires user approval before ingestion: yes
Hypothesis Board
Keep no more than 3 active hypotheses on Easy/Medium and 5 on Hard unless the user explicitly asks for breadth.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | Reverse the coffee_invocation artifact, identify JVM/native entrypoints and any encoded recipe/flag validation logic, then build a decoder or keygen. | Medium Reversing challenge with a small single-file artifact named coffee_invocation and Java-themed scenario text. | Run file/strings/binwalk-style triage, inspect embedded class/JAR/native sections, then execute only after understanding input/output behavior. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Technical analogy
How to remember this solve
Think of it like taking apart a small appliance on a workbench. You do not need every screw at once; you trace the control path and rebuild just enough logic to make it reveal the answer.
For Coffee Invocation, keep the mental model simple: identify the trusted assumption, prove it with the smallest safe test, then automate or repeat only the part that directly leads to the flag.