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, andm2- 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 o0ci = bi XOR oi XOR o(i-1)fori > 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.
- Create many pastes and save each
(id, sig)pair. - Compute
SHA256(id)for each one. - Split
siginto four 8 byte blocks. - Recover which secret block was used at each position.
- Fill in
m0,m1, andm2until all of them were complete. - Recompute a valid signature for
id = "flag". - 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}