My solution is probably unintended.
The idea is returning back to fgets with leak from printf.
The approach is to utilize a stack pointer chain in stack.
In this challenge `15$` points to `41$`,
so we can use something like `xxx%15$hnxxx%41$hhn` to firstly
write `41$` and let it points to return address of printf,
then use `41$` to change return address of printf.
However, if we use format string in this way,
original `41$` value is fetched instead of the rewritten one.
By debugging and reading source code of printf,
we found that when first `$` is encounted,
all arguments will be recorded into a `args_value` array.
Thus old value will be recorded before it is updated by first `%n`.
When `41$` is encounterd later, value will be fetched from `args_value`
instead of stack.
Thus, the approach is not using positional argument for first `%n`,
which is achievable in 0x30 characters!
We can also leak the data before reaching `15$`,
which have all pointers we need.
With leak, everything is easy.
Note that, this approach need 4096 bruteforces,
so we rent a server in Germany to bruteforce.