Jambavan: The Forgotten Strength
By Team Cryptonite · 1 min read
About the CTF: Pinaka CTF
jambavanseal is an oracle that reads 0/1 characters from stdin. The goal is a 128-bit "chant" that drives its hidden state to a target sigil.
**Pinaka CTF 2026** · Reverse engineering · Team Cryptonite · All 44 Pinaka write-ups
Category: reverse engineering
Flag: PINAKA{jambavanforgottenstrengthlearnedfromtheoracle}
Overview
jambavan_seal is an oracle that reads 0/1 characters from stdin. The goal is a 128-bit "chant" that drives its hidden state to a target sigil.
Files:
K06player/jambavanseal: stripped x86-64 PIE oracleK06player/sealtarget.json: the target 128-bitawakened_sigil
Analysis
Every 0 or 1 character advances a hidden 128-bit linear-feedback automaton and emits one resonance bit. The state lives in two 64-bit registers (r12 low, rbp high), and the constants are in the binary:
tap1 = 0xca504c4a84c21f09 (AND r12)
tap2 = 0x7f78bc91aa80f7ef (AND rbp)
tap3 = 0x49fbc9fedff08584 (AND new r12)
tap4 = 0x53b71036898edaf9 (AND new rbp)
feedback = parity(r12 & tap1) ^ parity(rbp & tap2) ^ input_bit
new_r12 = (r12 >> 1) | (rbp << 63)
new_rbp = (rbp >> 1) | (feedback << 63)
output = parity(new_r12 & tap3) ^ parity(new_rbp & tap4)
The resting state is also in the binary:
r12 = 0xb43f4db6aefb5271
rbp = 0x2d54ac7aec3fd6fd

Solution
Shifts, ANDs, parities and XORs are all affine over GF(2), so the whole machine is:
S_{i+1} = A*S_i XOR b*u_i
y_i = c*S_{i+1}
S is the 128-bit state and u_i is the input bit. A and b come from stepping the basis vectors with input 0. After 128 input bits:
S_128 = A^128*S_0 XOR sum_{i=0..127} A^(127-i)*b*u_i
The 128 columns A^(127-i)*b form a square 128x128 system over GF(2). Solving it for the target sigil 0x5e0d6ee1d2c86176c52d690679648156 with Gaussian elimination gives the chant.
t1=0xca504c4a84c21f09; t2=0x7f78bc91aa80f7ef
t3=0x49fbc9fedff08584; t4=0x53b71036898edaf9
S0=0xb43f4db6aefb5271 | (0x2d54ac7aec3fd6fd<<64)
def par(x): return bin(x).count('1') & 1
def step(S,bit):
r12=S&((1<<64)-1); rbp=S>>64
fb=par(r12&t1)^par(rbp&t2)^bit
nr12=((r12>>1)|(rbp<<63))&((1<<64)-1)
nrbp=((rbp>>1)|(fb<<63))&((1<<64)-1)
out=par(nr12&t3)^par(nrbp&t4)
return nr12|(nrbp<<64),out
A=[step(1<<i,0)[0] for i in range(128)]
b=step(0,1)[0]
def applyA(S):
r=0
for i in range(128):
if (S>>i)&1: r^=A[i]
return r
target=int("5e0d6ee1d2c86176c52d690679648156",16)
cols=[b]
for _ in range(127): cols.append(applyA(cols[-1]))
cols=cols[::-1] # column for u_i = A^(127-i) b
v=S0
for _ in range(128): v=applyA(v)
rhs=target^v
rows=[]
for eq in range(128):
row=0
for i in range(128):
if (cols[i]>>eq)&1: row|=1<<i
if (rhs>>eq)&1: row|=1<<128
rows.append(row)
piv=[]; r=0
for col in range(128):
p=next((rr for rr in range(r,128) if (rows[rr]>>col)&1), None)
if p is None: continue
rows[r],rows[p]=rows[p],rows[r]
for rr in range(128):
if rr!=r and ((rows[rr]>>col)&1): rows[rr]^=rows[r]
piv.append(col); r+=1
sol=0
for k,col in enumerate(piv):
if (rows[k]>>128)&1: sol|=1<<col
chant=''.join('1' if (sol>>i)&1 else '0' for i in range(128))
The solved chant:
1000101110000110110010010100110101010011111101001001001100010111
0011110010101001011101101110101000100001111111011100011111000010
Feeding it back through the simulated state lands exactly on 0x5e0d6ee1d2c86176c52d690679648156.

Submission
curl -X POST https://pinaka-k06-cyber-xo-x.vercel.app/seal/awaken \
-H 'Content-Type: application/json' \
-d '{"chant":"10001011100001101100100101001101010100111111010010010011000101110011110010101001011101101110101000100001111111011100011111000010"}'
{"flag":"PINAKA{jambavan_forgotten_strength_learned_from_the_oracle}",
"proclamation":"Jambavan remembers his strength; the seal yields",
"status":"awakened"}
Notes
- The 128x128 system has full rank 128, so the chant is unique and there is nothing to search.
- The step function is easiest to get right by stepping basis vectors with input
0to buildA, rather than deriving the matrix by hand.