Use the tree to jump between collections without leaving the reader.

archive Select writeup Open tree
NullconCTF2026/WriteupPasty.en.md READ_ONLY

Pasty

Challenge Data

Field Value
CTF Nullcon CTF 2026
Challenge Pasty
Category Web / Crypto
Flag ENO{cr3at1v3_cr7pt0_c0nstruct5_cr4sh_c4rd5}

Challenge Description

The service was a pastebin that protected each paste with a homemade signature. The interface would only show a paste if id and sig matched, so everything depended on understanding how that signature worked and why it could be forged.


Reconnaissance

When creating a paste, the server returned a URL like this.

view.php?id=<HEX_ID>&sig=<HEX_SIG>

That gave exactly what we needed to study the scheme, because it provided many valid (id, sig) pairs. The logic was in sig.php, and there it became clear that the signature was not using something standard like HMAC. It was a custom construction built around SHA256.

At that point, the next step was to recover enough key material from legitimate signatures.


Analysis

The algorithm derived only 24 effective bytes from the key.

  • m0, m1, and m2
  • three 8 byte blocks taken from SHA256(k)

Then, for each signature block, it selected one of those sub blocks according to h[8*i] % 3 and mixed it with the message hash using XOR and chaining.

The problem was that if we knew the signed message and the resulting signature, we could work out which secret sub block was used in each position.

  • c0 = b0 XOR o0
  • ci = bi XOR oi XOR o(i-1) for i > 0

Since paste creation gave us the signed message id, SHA256(id) was also known. By collecting enough valid signatures, it became possible to recover m0, m1, and m2 completely.

The important realization was that the homemade construction was leaking key material little by little. Every new signature told us a bit more than it should have.


Solution

The strategy was this.

  1. Create many pastes and save each (id, sig) pair.
  2. Compute SHA256(id) for each one.
  3. Split sig into four 8 byte blocks.
  4. Recover which secret block was used at each position.
  5. Fill in m0, m1, and m2 until all of them were complete.
  6. Recompute a valid signature for id = "flag".
  7. Request view.php?id=flag&sig=<forged_signature>.

Once the three key sub blocks were recovered, the rest was just reproducing the server algorithm for a new id. It was a nice challenge because there was no need to break SHA256 itself. The weakness was entirely in the custom construction wrapped around it.


Flag

ENO{cr3at1v3_cr7pt0_c0nstruct5_cr4sh_c4rd5}