TexSAW CTF 2026 Excellent Neurons

Find the flag by reverse engineering this neural network. Oh, and its in Excel.

Flag format: texsaw{flag} (e.g., texsaw{orthogonal})

前置知识

本题涉及神经网络逆向工程,且模型实现在 Excel 电子表格中。以下概念有助于理解:

概念 说明
前馈神经网络 输入层→隐藏层→输出层,数据逐层前向传递
ReLU f(x)=max(0,x),负输入→零输出(关键逻辑门控!)
Sigmoid σ(x)=1/(1+e^{-x}),输出映射到 (0,1),>0.5 表示正输入
权重与偏置 网络参数,本例中 W1 是极端稀疏的 +1/-1 矩阵
Excel 逆向 .xlsx 本质是 ZIP,内部 XML 含公式与数据
前向传播公式 ASCII/127 × weight + bias = 0 ⇒ ASCII = round(-bias × 127 / weight)

参考:Excel NN 教程hxp EXCELlent

Solution

1. Understanding the Success Condition

Examining the spreadsheet, specifically Row 112, we find the conditions for a valid flag: - F > 0.5, L < 0.5, A > 0.5, G < 0.5

Since the output layer uses a Sigmoid activation function: - To get output > 0.5, the input to the sigmoid (z3) must be positive (> 0). - To get output < 0.5, the input to the sigmoid (z3) must be negative (< 0).

Analyzing the final weights (W3) and biases (b3), we find that all weights in W3 are large (300 or -300) and the biases are small (between 0.49 and 0.59). For these specific constraints to be met, the output of Layer 2 (a2) must be an extremely small positive number, very close to zero.

2. Reverse-Engineering Layer 2

Layer 2 uses the ReLU activation function: a2 = ReLU(z2), where z2 = sum(a1 * W2) + b2.

Key observations: - All weights in W2 are negative (e.g., -0.954667). - The bias b2 is a very small positive number (approx. 1/254). - a1 is the output of Layer 1's ReLU, so a1 >= 0.

If any value in a1 is a positive number, its product with the negative weights in W2 will likely make z2 negative, resulting in a2 = 0 (via ReLU). To maintain that tiny positive value for a2, we conclude that all activation values in a1 must be forced to zero.

3. Analyzing Layer 1

To force a1 = 0, we must ensure that z1 = x * W1 + b1 <= 0 for all neurons in the first layer.

Looking closely at the biases (b1), they appear to be "random" decimals: - h1[0] = -0.795276 - h1[3] = -0.90551 - h1[12] = -0.968504 - h1[23] = 0.91339

If we multiply these values by 127 (the maximum standard ASCII value), they resolve into integers. This reveals that W1 and b1 are essentially implementing boundary checks for each character of the input string.

Converting these non-zero biases back to ASCII: - -0.795276 * 127 \(\approx\) -101 \(\rightarrow\) e - -0.90551 * 127 \(\approx\) -115 \(\rightarrow\) s - -0.968504 * 127 \(\approx\) -123 \(\rightarrow\) { - 0.91339 * 127 \(\approx\) 116 \(\rightarrow\) t

4. Reconstructing the Flag

Extracting all valid b1 values and converting them yields a multiset of 22 characters: t, e, x, s, a, w, {, }, s, v, r, r, r, 3, 3, 3, 3, _, n, l, 4, u

We know the flag format is texsaw{flag}. Removing the wrapper characters (t, e, x, s, a, w, {, }), we are left with: s, v, r, r, r, 3, 3, 3, 3, _, n, l, 4, u

Rearranging these characters (Leetspeak for "neural reverse") gives us the inner flag content.

Flag

texsaw{n3ur4l_r3v3rs3}