I also wanted to write a article about the similar thing. here is the brain dump:
All
- Avoid challenge names that hint at the solution.
- ✕: [pwn] Orange Juice: use House of Orange
- This is unnecessary, and it makes people who did not notice the hint feel bad or think the challenge is unfair.
- If you want to give a hint, do it explicitly.
- ✕: Our heart is bleeding...
- ◯: This challenge is related to [Heartbleed](https[:]//www[.]heartbleed[.]com/)
- Challenges should be self-contained.
- ✕: `from FLAG import secret`
- ◯: `FLAG = os.environ.get("FLAG", "flag{fake}")`
- What if `secret` does something suspicious?
- Avoid multi-stage challenges, such as putting two completely different challenges together
- Ideally, one challenge should contain one core idea
- Put the source code in the encrypted ZIP if you make revenge challenges
- NEVER use ZipCrypt
- Diffing the source can reveal the patch and invalidate the effort of solvers who solved it in the unintended way
- If your challenge is based on a previous challenge, link to it
- Otherwise, you need to add the OSINT tag (just kidding)
- Avoid fake flags and intentional rabbit holes.
- They are not fun :(
Pwn
- Pin the Docker image hash. Never run `apt upgrade` inside the container, or libc may be updated
- Document the kernel version and VM configuration. `mmap` works differently across environments
- If your challenge can be cheesed with a 24-bit bruteforce, add rate limiting or a strong PoW
- Stop reading huge inputs with `read`
- It can make the challenge unsolvable from distant locations
- Instead, use `scanf` or a hand-written string input function
- Include the source code if possible
Crypto
- Assert the flag format if the solution relies on it
- This can be too obvious, so add the assertion to all challenges: hide the tree in the forest
- Try to avoid magic numbers, such as embedded suspicious primes
- If you need determinism, use SHA-256 of the flag as a seed
- If the magic number has some useful property, such as smoothness, state it explicitly
- Ideally, solvers should know all information except the secret
- Some common cheese:
- A 512-bit `N` can be factored on a workstation
- A 256-bit DLP is solvable
Rev
- Byte-by-byte comparison or early returns can be cheesed with instruction counting
While we’re here, let me propose an option that would cut your server load and make Japanese users ditch Claude on the spot: please add a “Ctrl+Enter to send” option 🙏🙏🙏
Japanese/Chinese users use input method so-called IMEs. We hit Enter a million times just to confirm characters, not to send.
So accidental sends happen constantly. Every time, we get annoyed, and your GPUs get one more totally pointless request.
Imagine if double-tapping Space was “send”. That’s basically what this feels like.
Ctrl+Enter to send would cut server load and make our QoL a gazillion times better.
Please consider it 🙏🙏🙏🙏