Skip to main content

Command Palette

Search for a command to run...

WebVersePro Labs - Labs: NewsForge Writeup (Command Injection)

Updated
6 min read
S
messy writer

Challenge Link: https://dashboard.webverselabs-pro.com/labs/newsforge


Introduction

Welcome back to another CTF writeup, we are tackling NewsForge, an easy-level web challenge from the WebVersePro Labs platform. At first glance, NewsForge appears to be a standard, simple blog aggregator built on Node.js and SQLite. However, digging a little deeper into its functionality reveals some classic, highly impactful security flaws. While the challenge hints at broken access controls, we'll be taking a more direct route by exploiting a severe injection vulnerability that grants us complete control over the underlying container. Let's dive in and break it down.

  • OBJECTIVE: Leverage the command injection vulnerability to directly enumerate the target system and extract the hidden flag from the environment variables.

  • VULNERABILITY: CWE-78: OS Command Injection.

Challenge Briefing

NewsForge started as a side project by a local developer who wanted to share tech conference updates and open-source project milestones with the community. The platform grew organically — users can register, browse articles, and use the search feature to find content. The developer was proud of the simple, clean interface and basic functionality. But during a recent security audit, a colleague mentioned they noticed some unusual behavior in the search results. The search seems to return more than just article content. Find what the search is really doing and prove you can access sensitive information.

Category: Web

Difficulty: Easy

OS: Linux


Reconnaissance & Enumeration

Upon visiting the application at newsforge.local, we are presented with a typical news aggregation site. The interface is clean, displaying top stories across various categories. A panel on the right explicitly states that users must sign in to access the "powerful site search tool."

  • Key Findings:

    • Authentication Requirement: The search feature is gated behind authentication, meaning anonymous users cannot interact with it.

    • Open Registration: Standard user registration is open and functioning normally. I registered a standard account with the credentials Zor0ark:admin123.

    • Target Endpoint Identified: After logging in, the site search feature becomes available, providing a user input vector that interacts directly with the backend application.

Vulnerability Analysis

The challenge synopsis notes that the vulnerable search feature "executes user input as shell commands." In a Node.js environment, this typically indicates that user input is being concatenated directly into system execution functions like child_process.exec() without adequate sanitization.

Unlike spawn() or execFile(), the exec() function spawns a shell to run the command, which makes it highly susceptible to shell metacharacter manipulation and command injection. To verify its existence without fully exploiting it, I simply navigated to the search bar and I tried searching for 'technology' and the result shows /bin/sh: technology: not found which confirms that the search function is vulnerable to command injection, so to confirm this more, I inputted a benign system command: whoami.

The application returned the output newsforge directly to the web page. This confirmed two things:

  1. The input is being executed as a raw command.

  2. We do not even need to use command chaining operators (like ; or &&) to break out of a preceding command—our input is the command.


Exploitation

With direct Remote Code Execution (RCE) confirmed and the output returning directly to our browser, establishing a reverse shell wasn't strictly necessary to complete this specific objective. Instead, I could enumerate the system directly through the web interface's search bar.

Because this is a containerized Node.js application, sensitive data, API keys, and challenge flags are frequently passed in as environment variables rather than being stored in standard text files.

To check this, I simply inputted the following command directly into the search bar:

printenv

Post-Exploitation

Upon clicking "Search" with the printenv payload, the application executed the command on the server and returned the system's environment variables straight to the web page's results area:

NODE_VERSION=20.20.2
HOSTNAME=dccd16302220
YARN_VERSION=1.22.22
SHLVL=3
PORT=3000
HOME=/home/newsforge
OLDPWD=/app
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/app/db
NODE_ENV=production
FLAG=WEBVERSE{[REDACTED]}

The flag was successfully retrieved directly from the environment, completing the lab with a single payload!


Risk

The Command Injection vulnerability demonstrated here is a critical security flaw (CVSS 10.0 in many contexts). In a real-world scenario, this allows an attacker to achieve unauthenticated Remote Code Execution (RCE). An attacker could:

  • Steal sensitive configuration files, database files, and proprietary source code.

  • Modify application data, serving malicious content to legitimate users.

  • Use the compromised server as a pivot point to scan and attack internal network infrastructure.

  • Install persistent backdoors or deploy ransomware/cryptominers within the container environment.


Remediation

To fix this vulnerability and prevent future occurrences, developers must avoid passing untrusted user input directly to system shells.

Here is an example of what the vulnerable code likely looked like, and how it should be fixed.

Vulnerable Implementation

const { exec } = require('child_process');

app.get('/search', (req, res) => {
    const searchTerm = req.query.q;
    
    // VULNERABLE: Passing raw user input directly to a shell command
    exec(`grep -r "${searchTerm}" ./articles`, (error, stdout, stderr) => {
        if (error) {
            return res.status(500).send("Error performing search.");
        }
        res.send(`<pre>${stdout}</pre>`);
    });
});

Secure Implementation

The fix is to use child_process.execFile(). This function executes a specified file without spawning a shell first. User inputs are passed as a separate array of arguments. The system will treat these inputs strictly as data (literal strings) rather than executable shell code.

const { execFile } = require('child_process');

app.get('/search', (req, res) => {
    const searchTerm = req.query.q;
    
    // REMEDIATED: Using execFile with an arguments array prevents shell injection
    execFile('grep', ['-r', searchTerm, './articles'], (error, stdout, stderr) => {
        if (error && error.code !== 1) { // grep returns exit code 1 if no lines match
            return res.status(500).send("Error performing search.");
        }
        res.send(`<pre>${stdout}</pre>`);
    });
});

Additional Security Best Practices:

  1. Input Validation: Implement strict allowlisting for any user input. Ensure the search term only contains expected alphanumeric characters before it ever touches the system execution logic.

  2. Database Searching over System Commands: If the search is only meant to query articles, the backend should ideally use parameterized SQL queries (e.g., searching the SQLite database via LIKE ?) rather than resorting to system-level grep commands.

  3. Principle of Least Privilege: Ensure the user running the Node.js application (newsforge) has the absolute minimum permissions necessary on the host operating system.


Conclusion

The NewsForge challenge is an excellent reminder of why user input must always be treated as hostile. While the developer may have intended to use an internal shell command to index or search files quickly, passing user input directly into that command pipeline resulted in a catastrophic RCE vulnerability. Finding that the output reflected directly back to the user allowed for a rapid, "smash-and-grab" style extraction of the flag without even needing a reverse shell.


References