Daily AlpacaHack 2026/2/11 の write-up です。
最初は下記のようなコードで解きました。
solve.py
import re from pwn import * def nc(nc_comm): nc_argv0, host, port = nc_comm.split() return remote(host, int(port)) p = nc("nc 34.170.146.252 25646") def prettify(j): j = j.replace("[,", "[0,") j = j.replace(", ,", ", 0,") j = j.replace(", ,", ", 0,") j = j.replace(", ]", ", 0]") j = j.replace("_", "") return re.sub(r"([a-z])", r'"\1"', j).strip() for _ in range(5): p.recvuntil(b"Stage: ") _ = p.recvline() line = p.recvline().decode().strip() j = re.fullmatch(r"const (.+) = json;", line).group(1) j = prettify(j) p.sendlineafter(b"json> ", j.encode())
.replace(", ,", ", 0,") を単に 1 回だけ使うと [1, , , , 2] のような入力を [1, 0, , 0, 2] にしてしまい、うまくいきません(置換の範囲が overlap してしまうため)。
2 回行えばうまくいくのがちょっと面白いなと思いました。正規表現でがちゃがちゃやるのも考えたのですが、あんまり頭を使いたくなかったので。
入力において空白がぐちゃぐちゃになったりしておらず、優しいなと思いました。
一度解いてしばらくしてから、次の解法に思い至りました。そもそも JavaScript では {a: [, , { b: _0, c: [{ d: _1 }] }] } のようなパターンを書けるという前提が教えられているわけで、餅は餅屋、JavaScript は JavaScript 屋です。
// Welcome to Node.js v24.13.0. // Type ".help" for more information. > {a: [, , { b: 0, c: [{ d: 1 }] }] } { a: [ <2 empty items>, { b: 0, c: [Array] } ] } > JSON.stringify({a: [, , { b: 0, c: [{ d: 1 }] }] }) '{"a":[null,null,{"b":0,"c":[{"d":1}]}]}'
ということで、node に食わせればそのまま整形までやってくれそうです。
solve.py
import re from subprocess import PIPE, Popen from pwn import * def nc(nc_comm): nc_argv0, host, port = nc_comm.split() return remote(host, int(port)) p = nc("nc 34.170.146.252 25646") def prettify(j): p = Popen(["node"], stdin=PIPE, stdout=PIPE) j = j.replace("_", "").strip() stdout, _ = p.communicate(f"console.log(JSON.stringify({j}));".encode()) return stdout.decode() for _ in range(5): p.recvuntil(b"Stage: ") _ = p.recvline() line = p.recvline().decode().strip() j = re.fullmatch(r"const (.+) = json;", line).group(1) j = prettify(j) p.sendlineafter(b"json> ", j.encode()) p.recvuntil(b"Here's your flag: ") print(p.recvline().decode())
以上です。Alpaca{the_notation_is_pretty_intuitive_IMO} 👏