Trending Tags
Say My Name
Say My Name
Author: KuantumKnight
The challenge provides an ONNX model as the main artifact. The flag is not directly present in the model; instead, it is encoded as an input that produces a specific output through an auxiliary branch of the neural network.
Firstly, we inspect the structure of the ONNX model.
The graph contains a shared Gemm -> Tanh feature extractor and a normal four-class classification head. There is also an additional fifth logit calculated using a distance from a hidden target representation.
The relevant computation can be represented as:
1
2
3
4
5
h(x) = tanh(Ax + b)
d(x) = ||h(x) - target||²
score_4(x) = 18 - 2500*d(x)
The useful tensors are stored in the model initializers:
1
2
3
fc1.weight → 48×32 matrix A
fc1.bias → bias vector b
buffer_0 → hidden target representation
The player input is normalized as:
1
x = raw / 255
Since the matrix A has full column rank, we can invert the Tanh operation using arctanh and solve the resulting linear system using least squares.
Starting from:
1
target = tanh(Ax + b)
we apply arctanh:
1
arctanh(target) = Ax + b
Therefore:
1
x = lstsq(A, arctanh(target) - b)
After recovering the normalized input, we convert it back into the original byte values:
1
raw = rint(x * 255).astype(uint8)
The following Python script performs the complete inversion:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python3
from pathlib import Path
import numpy as np
import onnx
from onnx import numpy_helper
ROOT = Path(__file__).resolve().parents[2]
MODEL = ROOT / "player" / "heisenberg.onnx"
def main() -> None:
model = onnx.load(MODEL)
tensors = {
item.name: numpy_helper.to_array(item)
for item in model.graph.initializer
}
matrix = tensors["fc1.weight"].astype(np.float64)
bias = tensors["fc1.bias"].astype(np.float64)
target = tensors["buffer_0"].astype(np.float64)
normalized = np.linalg.lstsq(
matrix,
np.arctanh(target) - bias,
rcond=None,
)[0]
raw = np.rint(normalized * 255).astype(np.uint8)
print(bytes(raw).decode("ascii"))
if __name__ == "__main__":
main()
Running the script recovers the flag:
1
CYS{Y0UR3_G0DD4MN_R1GHT_ABQKQF!}
An alternative approach is to perform gradient ascent on the fifth logit, since maximizing the fifth score minimizes the distance between the generated representation and the hidden target.
The intended solve is therefore:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ONNX model
↓
Inspect computation graph
↓
Find auxiliary distance branch
↓
Extract fc1.weight, fc1.bias and buffer_0
↓
Invert Tanh using arctanh
↓
Solve Ax + b = arctanh(target)
↓
Convert normalized values back to bytes
↓
Recover flag
Flag
CYS{Y0UR3_G0DD4MN_R1GHT_ABQKQF!}