πΊWolvCTF 2024
Writeups for challenges I solved in team Project Sekai
Beginner
Web: The Gauntlet (319 solves)
Problem:
Can you survive the gauntlet?
10 mini web challenges are all that stand between you and the flag.
Note: Automated tools like sqlmap and dirbuster are not allowed (and will not be helpful anyway).
https://gauntlet-okntin33tq-ul.a.run.app
Solution:











OSINT: Redditor (416 solves)
Problem:
Someone told me WolvSec has a Reddit account. I wonder if they left a flag there...
Solution:

Forensics: Hidden Data (418 solves)
Problem:
WOLPHV sent me this file. Not sure what to comment about it
Solution:

Rev: babyre (348 solves)
Problem:
Just a wee-little baby re challenge.
Solution:

π©Έ Rev: Shredded (66 solves)
Problem:
We encoded a flag, and to make sure that pesky interlopers couldn't reverse it, we shredded the encoding code.
Note: The encoder was written in C. The code is written with good style, but all indents have been removed.
Solution:
The shredded files are vertical columns of shredder.c, we can manually piece them together since we know the file starts with #include<stdio.h>, so we grep for each desired character and find which shredded file has that letter on line 1, then do the same for the longer lines afterwards.
Misc
Made Sense (250 solves)
Problem:
i couldn't log in to my server so my friend kindly spun up a server to let me test makefiles. at least, they thought i couldn't log in :P
https://madesense-okntin33tq-ul.a.run.app
Solution:
Almost no filtering, we can run commands if we put them in the makefile directly.


Made Functional (128 solves)
Problem:
the second makejail
https://madefunctional-okntin33tq-ul.a.run.app
Solution:

Don't have path, can't use cat. Still have source.


Made Harder (68 solves)
Problem:
the third makejail
https://madeharder-okntin33tq-ul.a.run.app
Solution:
Payload needs to be made of specific characters now.

We have access to cat again though since this is diff'd from sense. We get cat from the target and the filename from the dependency.


Made With Love (57 solves)
Problem:
the final makejail
https://madewithlove-okntin33tq-ul.a.run.app
Solution:
Payload needs to be special characters, no cat.




UnholyFile (10 solves)
Problem:
It's an unholy file cast out from a holy land.
Maybe try out UnholyEXE first.
Solution:
We're given a binary file which seems like all FF in a hex viewer, although after searching through it, there are block of zeros in the middle. The file size is 3145745 which factors to 2^20 * 3. Based on these two things, my initial thought already is this is a 1024x1024 RGB image with the flag written in black on a white background. The simplest image header for raw image data is PPM, P6 to specify color and then width, height, and component depth.


We can see the flag so we know this is the right direction, but it's hard to read and it seems like there's some repetition. After playing around with the width and changing it to grayscale PGM with P5 instead of P6, we get the flag.


Rev
π©Έdoubledelete's revenge (105 solves)
Problem:
The notorious WOLPHV group has re-emerged and doubledelete is now ransoming us for our flags! Can you help us so we don't have to pay them?
Solution:
The decompiled code shows the encryption works by first opening a file, reads 0x30 (48) bytes, and then for every 4 bytes (32 bits), it concatenates two copies of it, one shifted to the left 13 and one shifted to the right 19 (which basically equals the value rotated 13 to the left), and then writes it to a file. Simple enough to decrypt.
Palworld (2 solves)
Problem:
We have Palworld at home. Palworld at home:
Solution:
We're given a PLD file defining digital logic. Displaying below for convenience, trimmed out repetitive parts.
There's 8 bits of flag input (flag_in), many intermediate signals (JTCN294-369), and a bunch of flip flops. AGEB are basically a check to know when 32 characters are red, SDFK store 32 characters of output, and SWTE scramble the input for added obfuscation.
Some background info: during the competition, I didn't notice that some of the reset lines for SWTE go to AP (async preset) instead of AR (async reset), and since SWTE feeds into itself, I thought it was always zero and didn't affect the logic. It is actually initialized with some ones which make it XOR the flag inputs for JTCN307-314. After adding that, my simulation code was functional.
Since JTCN isn't registered (clocked), all of the interconnecting logic happens at once every clock cycle whereas the other signals (AGEB/SDFK/SWTE) update once per clock via the flip flop. Therefore, there needs to be a way to simulate the logic propagation until the JTCN signals reach steady state. During debugging, I built two different ways to do this, the first is just doing the same update procedure as clocked signals, but keep clocking until the output doesn't change, and the second is to recursively expand all the pin logic so each JTCN signal is related directly to flag_in.
Method 1:
I added a configurable limit to the number of update loops because I thought maybe the reason it wasn't working initially was that the challenge was simulated to not leave enough time for the signals to stabilize per clock cycle, although this wouldn't really work anyway since different signals have different numbers of gates between them and the flag_in.
The way this works is the loop breaks once JTCN_current and JTCN_new are equal which represents all changes being propagated throughout the circuit. This often takes 10+ cycles for the logic.
The code itself was created via find and replace with the original PAL, replacing & with and, $ with ^, and not with !.
Method 2:
This second method recursively expands each JTCN signal so the logic can be calculated on one loop. Not really ideal but it is easier to understand and I confirmed it results in the same output as method 1.
Next is figuring out what our goal is. The flag_ok signal conveniently has a ! in front of the SDFK signals that should be 0 and not in front of the ones that should be 1, so with some more find and replace and some sorting, we can get the desired bits from 0-255:
The first input goes to SDFK0-7 which eventually becomes SDFK248-255. The typical convention is flag_in 7 downto 0 represents most to least significant bits, so SDFK255 is the most significant bit of the first input at the end. Looking at the end of the binary string, the first input should therefore result in SDFK255-248 being 10000000, which is 128 in decimal. The rest in decimal:
At this point, we can just run the simulation for all the possible flag_in combinations from 32-127, and note what values give us the desired values one character at a time by reading SDFK0-7. Full solution script below.

Last updated
Was this helpful?