Pocket CTF Reference · Device home

Final Authority

The vault exposes a low-privilege command authenticated with SHA-256(secret || body). Extend it to the required action, then discover how the hidden ledger accepts commands.

Objective

Before you begin

Complete Quiet Oracle and retain its original body, digest, and secret-length range. Read the required suffix on the Final Authority page. Use the local continuation tool introduced below.

Concepts

Merkle–Damgård hashes expose a final state that can continue hashing after the original padding. A plain secret-prefix hash is therefore not a safe MAC.

Original bytes and their length-dependent padding fill SHA-256 blocks. The digest encodes the resulting chaining state. More bytes can continue that state only with the correct processed length; HMAC uses a different keyed construction.

SHA-256 consumes 64-byte blocks. Its final padding includes the original bit length, and its digest encodes the resulting chaining state. A continuation tool needs that state and the number of bytes already processed, including padding. Hashing the printed digest starts a different computation. A raw secret-prefix hash exposes a reusable state; HMAC uses keyed inner and outer hashing instead.

Suggested tools

Tool practice

Rehearse the worked tool exercise before using challenge data.

Keep padding as bytes throughout computation and transport. Ordinary hashlib.sha256 does not import a digest as internal state: hashing the digest text starts a new hash. Use the downloadable continuation tool below and verify it against a known local example before working with challenge evidence.

Investigation

Rehearse the continuation tool locally

Save the offline SHA-256 teaching tool as sha256_state.py in your practice folder. It performs local calculations only and contains no challenge data. Run python3 sha256_state.py --self-test; expected output is 33 continuation checks passed against hashlib.

Save the following alongside it as practice_extension.py, then run it:

import hashlib
from sha256_state import padding, extend

original = b"practice" + b"label=lamp"
suffix = b";color=blue"
digest = hashlib.sha256(original).hexdigest()
glue = padding(len(original))
continued = extend(digest, len(original), suffix)
expected = hashlib.sha256(original + glue + suffix).hexdigest()
print(len(original), len(glue))       # 18 46
print(continued == expected)          # True

This known-input check separates understanding the tool from discovering challenge values. padding(n) returns SHA-256 padding for n original bytes; extend(digest, n, suffix) resumes from that digest after the original padding. Its length argument is the total original input length, including any hidden prefix, not the digest's 64-character display length. The tool accounts for already-processed padding when continuing. SHA-256 uses 64-byte blocks, one 0x80 byte, enough zero bytes, and an eight-byte big-endian bit count.

For transport practice, percent-encode unrelated binary bytes locally:

from urllib.parse import quote_from_bytes, unquote_to_bytes

raw = b"A\x80\x00B"
encoded = quote_from_bytes(raw, safe="")
print(encoded)                       # A%80%00B
print(unquote_to_bytes(encoded) == raw)  # True

Keep padding as bytes. Encode each form value exactly once; --data-urlencode expects raw input, whereas a body already containing percent escapes must be sent without encoding those percent signs again. Check your local round trip before submitting the module's bounded candidates, one at a time.

Observe

Record the original body, MAC, suffix, and length range. Use the discovery file and OPTIONS to identify the ledger method and exact field names.

Form a hypothesis

Does the bounded secret-length range permit a continued SHA-256 state that the server accepts for a command with the required meaning?

Test carefully

Generate candidates locally. Submit each length at most once, preserving all padding bytes and the required beginning and ending of the command.

Progressive hints

Hint 1

Generate candidates for the supplied secret-length range locally.

Hint 2

Account for SHA-256 padding and its encoded bit length.

Hint 3

Discover the route and request grammar before submitting candidates.

Troubleshooting and reset

Do not UTF-8-decode glue padding. Percent-encode raw bytes exactly and use the field names reported by OPTIONS. The stage is otherwise stateless.