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.