$ cat writeup.md…
$ cat writeup.md…
avitoctf
Task: A Spring Boot hive panel exposes masked request logs and an engineer maintenance bot to a low-privilege operator. Solution: Splice concurrent append writes, bypass first-only redaction, steal the bot credential, and open the gate.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
A smart beehive has cameras, sensors, and camera-controlled landing gates. Low-privileged bee credentials are provided, but this account cannot control the gate. Source code is supplied.
The goal is to open the engineer-only landing gate. The first challenge endpoint is only a CAPTCHA-protected instance launcher; after completing it, the organizer provides a temporary -srv- service origin. The exploit targets that launched Java service, not the launcher.
DatabaseSeeder.java:33-36 gives the low user only AUDIO_OPERATOR, while DatabaseSeeder.java:46-58 makes landing-gate writable only by ENGINEER and hive-speakers writable by both roles. DeviceRegistry.ensureWritable() performs the real role check at lines 269-273, so there is no direct authorization bypass.
This is not JWT or session forgery either. SessionService.java:32-45 creates a random 32-byte token with SecureRandom, stores its username server-side in a ConcurrentHashMap, and sends only the opaque ID to the client. The intended escalation is therefore credential theft: obtain the real BeeEng password and authenticate normally.
An audio operator can disable the speakers. In DeviceController.java:50-57, every successful disable operation calls MaintenanceBot.restoreSpeakers(). The bot schedules a restoration job and then submits this form to the loopback /login endpoint (MaintenanceBot.java:47-76):
username=BeeEng&password=<ENGINEER_PASSWORD>&remember=true&client=maintenance-console
RequestLoggingFilter.java:30-35 logs each request before controller dispatch. Its canonicalQuery() method at lines 52-71 obtains the complete parameter map, sorts parameter names, preserves every value of a repeated parameter, URL-encodes the values, and joins them with &. Thus the bot's request produces a recognizable segment in the audit log:
password=<ENGINEER_PASSWORD>&remember=true&username=BeeEng
Any authenticated account can read or clear the debug log (LogController.java:22-32), so the low account can observe the result.
The vulnerable primitive is in DebugAuditLog.append() (DebugAuditLog.java:59-68):
...
$ grep --similar