$ cat writeup.md…
$ cat writeup.md…
alfactf
Task: a Go service verified a YAML gradebook with a raw polynomial hash and unmarshaled it with gopkg.in/yaml.v3. Solution: forge a new all-5s document for the same passport, terminate it with '...', then append a printable correction tail that restores the original signature modulo 2^56-5.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Горпкорный институт
The service accepted a YAML certificate and verified it server-side before granting admission. The goal was to submit a certificate for passport 1337676769 that parsed as all 5s while still matching the one stored signature.
The challenge combined a web submission endpoint with a weak homemade signature scheme.
The important source-level details were:
gopkg.in/yaml.v3 v3.0.1 into:type Attestat struct { Passport string `yaml:"паспорт"` Grades map[string]int `yaml:"оценки"` }
originalAttestat, under passport 1337676769.\r\n to \n, unmarshaled the submitted bytes with yaml.Unmarshal, and then hashed the exact normalized byte string.256 modulop = 2^56 - 5
The original signed document was:
# Подтвержденный аттестат паспорт: "1337676769" оценки: физика: 4 химия: 3 информатика: 5 английский: 4 русский: 5 литература: 4 физкультура: 5 обществознание: 3 алгебра: 4 история: 5 география: 4 биология: 3 геометрия: 4
If
H(b0..bn-1) = Σ bi * 256^(n-1-i) mod p,
then for concatenation we have
H(x || y) = H(x) * 256^|y| + H(y) mod p.
The YAML behavior provided the second half of the exploit. From parser tests against gopkg.in/yaml.v3 v3.0.1:
yaml.Unmarshal parsed only the first YAML document;... were ignored by unmarshaling;...
$ grep --similar