My first CTF (part 1) (english version)

Yesterday, I had the opportunity to participate in a CTF for the first time.

A CTF (Capture The Flag) is a limited-time event during which hundreds of teams from around the world compete to solve a series of challenges to obtain the famous “flags”. The difference with traditional challenge sites is precisely this limited duration (a few hours to a few days), which adds a certain pressure to the challengers…

For my first experience, I therefore chose a “beginner” CTF organized by an Italian university: m0leCon Beginner CTF. A 5-hour CTF, combining crypto, web hacking, reversing, etc. tests.

I now follow the tradition of “write-up”, that is to say sharing some of my solutions after the end of the competition, to allow everyone to progress!

Unguessable

Unguessable, really?

The first challenge invites us to guess a number. Unless I’m extraordinary lucky, the answer is always “Wrong, try again”

The first reflex in this type of challenge is to understand how the application works. And for that, you have to dive into the code…

The two functions that interest us in the javascript code are:

    function update(res) {
       if (res === "wrong") {
         card.style.backgroundColor = "red";
         text.innerText = "Wrong, try again";
       } else {
         card.style.backgroundColor = "green";
         fetch("/vjfYkHzyZGJ4A7cPNutFeM/flag")
           .then((response) => response.text())
           .then((str) => {
             text.innerText = str
           });
       }

       card.removeAttribute("hidden");
     }

     document.getElementById("guessBtn").onclick = function () {
       card.setAttribute("hidden", "");
       load().then(() =>
         fetch("/guess", {
           body: document.getElementById("guess").value,
           method: "POST",
         }))
         .then((response) => response.json())
         .then((json) => update(json["result"]))
         .then(() => loader.parentElement.setAttribute("hidden", ""));

     };

The second function is the one that is triggered when you click on the “Guess” button. This function sends the number entered in the form to a “/guess” page, retrieves the result returned by this page and passes it to the “update” function

The “update” function checks if the result is “wrong”. In this case, it displays the message “Wrong, try again!” in red. Otherwise, it will get another page (/vjfYkHzyZGJ4A7cPNutFeM/flag) and display its contents. And this page contains the flag…

AND Cipher

This challenge contains a page allowing you to generate an encrypted string.

Each press of the “Encrypt!!!” button generates a new string. According to the clues in the title, this string is the result of the Boolean operator AND between a secret message (probably the flag) and a random key

as a reminder, here is how the AND operator works:

Input 1 (flag bit) Input 2 (key bit)Output (encrypted string bit)
000
010
100
111
AND operator

If we have the key, how could we find the flag? Let’s try reversing the AND operator:

encrypted string bitkey bitflag bit
000 or 1???
010
10Impossible!!
111
reversing the AND operator

We can see two problems in this AND “encryption”:

  • If the bits of the encrypted string and the key are both 0, it is impossible to know which bit was the flag
  • If the bit of the encrypted string is 1, this means that we know that the flag bit is 1 even if we do not know the key

It is this second property that I will use. Indeed, each time a bit of the encrypted string is 1, we can deduce that the corresponding bit of the flag is 1. And as we can generate as much encryption as we want, we can accumulate these bits to reconstruct the flag. I automated the process in a small python program:

from Crypto.Util.number import long_to_bytes
url="https://andcipher.challs.m0lecon.it/api/encrypt"
import requests

s=0
while True:
	# get the response from the url
	d=requests.get(url).text.split('"')[3]
	
	# transform it into int
	d=int(d,16)
	
	# accumulate the "1" bits with OR operator
	s|=d
	
	# print the result
	print(long_to_bytes(s))
	input()

Here’s the result: