Challenge / Reversing

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

MediumPublished 2025-06-15Sanitized local writeup

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.

Coffee Invocation sanitized attack graph

Walkthrough flow

01

Extract the HTB archive with the standard archive...

02

Use strings and byte search to find embedded CAFEBABE...

03

Carve the embedded classes and decompile them with...

04

Treat the Java output as incomplete because the...

05

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.

100% coverage
Evidence verdict

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:

  • Verify1 compares a source string against a target string byte by byte.
  • Verify2 compares sorted two-character chunks against slices of a shuffled constant and calls System.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.value and Short.value caches are rewritten before Verify1, so the first 26 input bytes must be recovered by inverting the native byte/short maps.
  • Boolean.TRUE and Boolean.FALSE behavior is swapped before Verify2, changing which side is sorted.
  • Character.value cache mappings are rotated after each intercepted System.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:

bash
cd <local workspace>
python3 solve/solve.py --output loot/flag-candidate.txt

Then capture through the harness:

bash
cd <local workspace>
python3 scripts/challenge_harness.py capture-flag Reversing/Coffee-Invocation --from loot/flag-candidate.txt

The 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.exit can become a control-flow oracle when the native host replaces Shutdown.halt0.
  • Wrapper caches such as Byte, Short, Character, and Boolean are 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

FileSizeSHA256TypeNotes
files/a12c73a2-1983-42ae-b5b8-8b77e6d4db8d.zip9356<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 2 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-13T05:28:12Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-13T05:28:22Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-13T05:28:49Zhypothesis recordedhypothesis-board.mdReverse the coffee_invocation artifact, identify JVM/native entrypoints and any encoded recipe/flag validation logic, then build a decoder or keygen.MediumRun file/strings/binwalk-style triage, inspect embedded class/JAR/native sections, then execute only after understanding input/output behavior.
2026-06-13T05:29:05Zresearch skipanalysis/research/research-skip.mdResearch intentionally skipped with recorded reasonMediumGate before exploit
2026-06-13T05:29:05Zinstrumentation plananalysis/instrumentation-plan.mdRecover the flag or <password redacted> logic from the coffee_invocation artifact and produce a reproducible decoder.HighAfter two failed reverse paths without a new artifact fact, record the failure and revisit tool choice rather than guessing flags.
2026-06-13T05:36:00Zstatic modelanalysis/static-solve-model.txtModeled JNI cache mutation and staged System.exit handling to recover the validated flag body without executing the Linux ELFHighWrite reproducible solver
2026-06-13T05:36:20Zsolve scriptsolve/solve.pyReproducible solver parses the ELF data tables and writes an HTB-format candidate to lootHighCapture flag through harness
2026-06-13T05:36:40Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-13T05:38:54Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional 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 CAFEBABE offsets and decompiled with JADX.
  • The native JNI wrapper mutates Java Byte, Short, Boolean, and Character runtime 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:

bash
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.

  1. Extract the HTB archive with the standard archive password and identify the single stripped x86-64 Linux ELF.
  2. Use strings and byte search to find embedded CAFEBABE Java class files.
  3. Carve the embedded classes and decompile them with JADX to understand Verify1 and Verify2.
  4. Treat the Java output as incomplete because the native wrapper creates the JVM and mutates runtime state through JNI.
  5. 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.

  1. Reconstruct the two 26-byte flag-body halves from native .data tables and write the HTB-format candidate to loot/.
  2. 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 in loot/.

Dead Ends

  • Running the Linux ELF locally was not available on macOS because no qemu-x86_64 was installed and Docker did not respond promptly.
  • Treating Tinfoil as 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 javap could not be used.
  • JADX was available and successfully decompiled the carved .class files.
  • A faithful Python model was faster and cleaner than setting up Linux runtime execution.

Evidence Paths

  • analysis/strings.txt
  • analysis/objdump-x.txt
  • analysis/objdump-d.txt
  • analysis/classes/Verify1.class
  • analysis/classes/Verify2.class
  • analysis/jadx/sources/defpackage/Verify1.java
  • analysis/jadx/sources/defpackage/Verify2.java
  • analysis/static-solve-model.txt
  • solve/solve.py
  • loot/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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Reverse 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.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit 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.