This challenge was really fun and taught me a lot about ROP.
It was a ret-2-win with parameters. The binary provided was x86-64 with the following protections:
Ghidra showed a function fill_ammo() which wasn't called in the main and the lack of any stack canaries or PIE indicated a ret-2-win.
So, fill_ammo took 3 parameters which needed to be 0xdeadbeef, 0xdeadbabe, and 0xdead1337 for the flag to be printed.
Since the binary was 64 bit the calling conventions stated that function arguments are passed in order in the registers RDI, RSI, RDX, RCX, R8, and R9. Therefore I needed RDI=0xdeadbeef, RSI=deadbabe, and RDX=0xdead1337 before the function was called.
And now for the ROP stage. In order to get these values into the registers prior to the return to fill_ammo I needed 3 ROP gadgets: POP RDI/RSI/RDX; RET. Thankfully all of them were already in the binary.
The buffer needed to be 40 bytes so my initial payload was: A*40 + POP RDI; RET + 0xdeadbeef + POP RSI; RET + 0xdeadbabe + POP RDX; RET + 0xdead1337 + fill_ammo. I delivered the payload and during last printf this happened...
After messing around with my payload for way too long thinking I missed something, I came across this page on stack allignment. This was a godsend and I would highly recommend ir0nstones page for info on binary exploitation.
I added the RET instruction to the ROP chain and got the flag!
Unfortunately, I am writing this after the event ended and no longer have access to the challenge server hence the fake flag.