$ cat writeup.md…
$ cat writeup.md…
fortid2026
Task: Express app renders a user-supplied Handlebars 4.7.8 template in a worker whose prototype-access guard is on; a recursive theme merge only filters the literal __proto__ key. Solution: pollute Object.prototype via constructor.prototype to inject allowedProtoMethods/allowedProtoProperties (absent as own props on the runtime options, so read off the prototype), bypass the proto-access guard, and drive the String.prototype.sub->Function-constructor RCE to fs.readFileSync the flag path allowed by the Node permission model.
$ 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 Lika Tourist Board has launched a postcard designer for visitors who think they know the region. Pick a theme, fill in the postcard data, and let a fresh print worker process prepare a preview. Can you print the ranger's secret?
English summary: A Node.js/Express app exposes POST /api/preview accepting JSON {template, theme, model}. Each request spawns a fresh worker that compiles and renders the user-supplied Handlebars template. The worker runs under the Node --permission sandbox, which blocks child_process but allows fs read of the flag file. The goal is template-injection RCE that performs require('fs').readFileSync(process.env.FLAG_PATH) and returns the content in the preview output.
server.js spawns worker.js per request with flags:
--permission --allow-fs-read=/challenge --allow-fs-read=/tmp/lika/flag.txt --max-old-space-size=48/tmp/lika/flag.txt at startup; the FLAG env var is then deleted; the worker only sees FLAG_PATH=/tmp/lika/flag.txt.worker.js compiles job.template with Handlebars.compile(..., { preventIndent: true }) and renders with:return template({ ...job.model, theme }, { data: rootData, partials: safePartials, allowProtoMethodsByDefault: false, allowProtoPropertiesByDefault: false, });
.constructor, .sub, etc. from prototype chains is blocked by default.containsBlockedKey recursively rejects only the literal key __proto__ in theme/model. It does not block constructor or prototype.allowProtoMethodsByDefault:false / allowProtoPropertiesByDefault:false block prototype method/property access.{{> (expr)}}, inline partials {{#*inline}}, and restricts partial names via regex to an allowlist {@partial-block, myPartial}. The partials object is null-prototype; myPartial is non-writable/non-configurable; rootData['partial-block'] is an own undefined to shadow any pollution.setLayoutOption(target, key=, value=) with key restricted to {title,accent,caption} — a decoy, not needed for the solve....
$ grep --similar