Matrixfun II (Crypto)
Challenge Data
| Field | Value |
|---|---|
| CTF | Nullcon CTF 2026 |
| Challenge | Matrixfun II |
| Category | Crypto |
| Flag | ENO{l1ne4r_alg3br4_i5_ev3rywh3re} |
Challenge Description
The server encrypted blocks over the Base64 alphabet using an affine transformation of the form c = A·x + b (mod 65). It also let us encrypt arbitrary messages with the same key. That oracle was enough to recover A and b.
Reconnaissance
Each block of 16 Base64 symbols was converted into indices 0..64, and over that vector the server applied this transformation.
c = A·x + b (mod 65)
With chosen plaintext encryption under the same matrix and the same vector, recovering the key came down to preparing the right inputs.
Analysis
There were two very simple observations here.
- If the input block was all zeros, the output was directly
b. - If the input block had a single
1at positionj, the output was columnjofAplusb.
Since in Base64 the index 0 corresponds to 'a', the required messages were those whose first block represented this.
aaaaaaaaaaaaaaaato obtainb- one block with a single
1in the desired position to recover each column ofA
Once b and the 16 columns of A were known, the only thing left was to invert the matrix modulo 65 and decrypt the initial ciphertext.
There was no need for brute force. The recovery came from exploiting the linearity of the scheme.
Solution
The full procedure was this.
- Save the flag ciphertext.
- Request the encryption of a block that translates to 16 zeros and extract
b. - Repeat the process 16 times with blocks containing a single
1to recover each column ofA. - Compute
A^-1 mod 65. - Apply
x = A^-1(c - b)to each ciphertext block. - Rebuild the Base64 string and decode it.
In code, this required a function to recover A and b from the service, another to invert matrices modulo 65, and a final one to transform the encrypted blocks back into the original text.
Once that was in place, the encryption could be inverted block by block.
Flag
ENO{l1ne4r_alg3br4_i5_ev3rywh3re}