CVE-2025-46816: Goshs! Your Simple HTTP Server Just Became an Open Backdoor

TL;DR / Executive Summary

CVE-2025-46816 exposes a critical Remote Code Execution (RCE) vulnerability in goshs, a Go-based simple HTTP server, affecting versions 0.3.4 through 1.0.4. When goshs is run without specific command-line arguments (like the -c flag to explicitly enable command execution), it inadvertently leaves a WebSocket endpoint open for unauthenticated command execution. Attackers can send a crafted WebSocket message to run arbitrary commands on the server with the server process's privileges. The vulnerability carries a high severity. Users should immediately upgrade to goshs version 1.0.5 or later, which correctly checks if command execution via WebSockets is permitted based on server startup flags. If an upgrade isn't possible, ensure goshs is not exposed to untrusted networks or run with restrictive configurations.

Introduction

Imagine setting up a quick, 'harmless' HTTP server for some local file sharing or a rapid development task. You type goshs in your terminal and hit enter. Job done, right? You've got a server running. Wrong! If you were using goshs versions 0.3.4 to 1.0.4, you might have just unknowingly rolled out the red carpet for attackers.

Today, we're dissecting CVE-2025-46816, a sneaky vulnerability that turns this handy Go Simple HTTP Server (goshs) into an unwilling accomplice for remote command execution. This isn't just about one tool; it's a stark reminder that even the simplest utilities, especially network-facing ones, need robust security considerations from the ground up. For developers, sysadmins, and security professionals, this CVE is a compelling case study in how default configurations and unchecked features can lead to significant, and often surprising, compromise. Why should you care? Because a tool designed for convenience accidentally became a gateway for control.

Technical Deep Dive

Vulnerability Details: The Open Door

At its heart, CVE-2025-46816 is an unauthenticated Remote Code Execution (RCE) vulnerability. goshs includes a feature allowing command execution via WebSockets. This functionality is intended to be explicitly enabled by the user through the -c (or --cli) command-line flag when starting the server. However, in the vulnerable versions (0.3.4 to 1.0.4), the server doesn't actually verify if this flag was provided when it receives a command execution request over a WebSocket connection. If goshs is started without any arguments, it defaults to a state where this WebSocket command channel is dangerously receptive.

Root Cause Analysis: The Missing Checkpoint

The core of the issue lies within the dispatchReadPump function, located in ws/client.go. This function is responsible for handling incoming messages from connected WebSocket clients. When a specially crafted JSON message like {"type": "command", "content": "your_malicious_command_here"} arrives, the vulnerable code proceeds to unmarshal the content and execute it as a system command. Crucially, it does this without first checking if the server was actually started with the -c flag that's supposed to authorize this very behavior.

Think of it like a high-tech club with a special VIP room (command execution). The bouncer (the dispatchReadPump function) is supposed to check for a specific VIP pass (the -c flag) before letting anyone into that room. However, in this case, the bouncer just lets anyone in who walks up to the WebSocket entrance and says, "I'd like to execute a command," completely forgetting to check if the club owner (the user who started goshs) ever authorized VIP access in the first place.

Here's a simplified representation of the vulnerable logic:

// ws/client.go (Simplified vulnerable logic before patch)
func (c *Client) dispatchReadPump(packet Packet) {
    switch packet.Type {
    // ... other cases for clipboard, etc.
    case "command":
        var command string
        // Assumes packet.Content is a JSON string for the command
        if err := json.Unmarshal(packet.Content, &command); err != nil {
            logger.Errorf("Error reading json packet: %+v", err)
            // Potentially continues or breaks, but the check is missing
        }
        logger.Debugf("Command was: %+v", command)
        
        // PROBLEM: No check here to see if command execution is allowed by server config!
        // It just goes ahead and runs the command.
        output, err := cli.RunCMD(command) // Executes the command
        if err != nil {
            logger.Errorf("Error running command: %+v", err)
        }
        logger.Debugf("Output: %+v", output)
        c.updateCLI(output) // Sends output back over WebSocket

    default:
        logger.Warnf("The event sent via websocket cannot be handeled: %+v", packet.Type)
    }
}

Attack Vectors: How They Get In

An attacker who can reach the goshs instance over the network can connect to its WebSocket endpoint (typically ws://<server_ip>:8000/?ws). By sending a simple JSON payload, they can execute arbitrary commands with the same privileges as the goshs process. This is particularly dangerous if goshs is inadvertently run as root (a terrible practice, but it happens!) or by a user with significant privileges. The attack doesn't require any authentication – just network access to the goshs port.

Business Impact: Why It's Bad News

The impact of CVE-2025-46816 is severe, potentially leading to full server compromise:

  • Data Theft: Attackers can access, modify, or exfiltrate any files readable by the goshs process.
  • Server Hijacking: The compromised server can be used for nefarious activities like participating in DDoS attacks, mining cryptocurrency, or hosting malicious content.
  • Lateral Movement: The server can become a pivot point for attackers to move deeper into the internal network.
  • Full System Compromise: If goshs runs with high privileges (e.g., root), the entire system is at risk.
  • Reputational Damage: For any organization unknowingly exposing this vulnerability, the damage to trust and reputation can be significant.

Proof of Concept (PoC)

Seeing is believing, and exploiting this vulnerability is alarmingly simple. The discoverer provided a straightforward PoC using websocat, a versatile command-line WebSocket client.

Scenario: Your vulnerable goshs server (version 0.3.4-1.0.4) is running on 192.168.1.11:8000. Importantly, it was started simply by typing goshs without any arguments, making it vulnerable by default.

Attack Command:

# PoC using websocat to execute the 'id' command on the server
echo -e '{"type": "command", "content": "id"}' | websocat 'ws://192.168.1.11:8000/?ws' -t

Expected Output (if vulnerable):
The server will execute the id command, and its output will be sent back through the WebSocket. You'll see something like:

{"type":"cli","content":"uid=1000(youruser) gid=1000(youruser) groups=1000(youruser),4(adm),24(cdrom)...\n"}

This confirms successful command execution. An attacker could replace "id" with any other command, such as ls -la to list files, or far more destructive commands. (Please, only test this on systems you own and have permission to test on!)

Mitigation and Remediation

Don't panic, but do act! Here's how to protect yourself from CVE-2025-46816:

Immediate Fixes:

  1. Upgrade goshs (Recommended): The primary and most effective solution is to upgrade to goshs version 1.0.5 or later. This version contains the patch that fixes the vulnerability.
    You can typically upgrade using Go's toolchain:
    go install github.com/patrickhener/goshs@latest 
    # or go get -u github.com/patrickhener/goshs if using older Go versions in GOPATH mode
    
    Alternatively, download the latest pre-compiled binary from the goshs releases page.
  2. Restrict Access: If upgrading immediately isn't feasible (though it really should be a priority):
    • Ensure goshs is not exposed to untrusted networks.
    • Use a firewall to limit access strictly to trusted IP addresses.
    • If only local access is needed, bind goshs to localhost: goshs -i 127.0.0.1.
  3. Explicitly Disable CLI (if not upgrading): If you must use a vulnerable version and do not need command execution, ensure you are not running it with default arguments if those defaults are insecure. However, the vulnerability lies in it being enabled even when not explicitly requested. The safest bet is to upgrade.

Long-Term Solutions:

  • Principle of Least Privilege: Always run services like goshs with the minimum necessary permissions. Avoid running it as root.
  • Secure Defaults: For developers creating tools: ensure your applications have secure defaults. Features with significant security implications (like RCE) should be opt-in and rigorously checked, not opt-out or accidentally enabled.

Verification Steps:

After upgrading to goshs 1.0.5 or later:

  1. Start goshs without any arguments (the previously vulnerable scenario): goshs
  2. Attempt the PoC command again:
    echo -e '{"type": "command", "content": "id"}' | websocat 'ws://<your_server_ip>:8000/?ws' -t
    
  3. You should not see the output of the id command. The WebSocket connection might still establish, but the server should ignore the "command" request because command execution wasn't enabled via the -c flag. This confirms the fix is working.

Patch Analysis: Behind the Scenes of the Fix

The fix implemented in goshs version 1.0.5 is straightforward and directly addresses the root cause by ensuring the server's startup configuration is respected. Let's peek at the key changes (diffs simplified for clarity):

  1. Making the Hub Aware (httpserver/server.go & ws/hub.go):

    • The Hub struct (which manages WebSocket connections) in ws/hub.go now includes a cliEnabled bool field. This field will store whether command-line execution was permitted at startup.
      // File: ws/hub.go
      type Hub struct {
          // ... other fields (clients, broadcast, register, unregister, cb)
      +   // CLI Enabled
      +   cliEnabled bool
      }
      
      // NewHub will create a new hub
      -func NewHub(cb *clipboard.Clipboard) *Hub {
      +func NewHub(cb *clipboard.Clipboard, cliEnabled bool) *Hub { // cliEnabled is passed in
          return &Hub{
              // ... other initializations
              cb:         cb,
      +       cliEnabled: cliEnabled, // Store the flag
          }
      }
      
    • In httpserver/server.go, when the FileServer initializes the WebSocket Hub, it now passes its CLI configuration (which knows if the -c flag was used).
      // File: httpserver/server.go
      func (fs *FileServer) Start(what string) {
          // ...
          // init websocket hub
      -   fs.Hub = ws.NewHub(fs.Clipboard)
      +   fs.Hub = ws.NewHub(fs.Clipboard, fs.CLI) // fs.CLI now passes its state
      }
      
      (Note: The actual fs.CLI being passed might be a boolean derived from the CLI options, e.g., fs.CLIOptions.Command, rather than the whole struct, but the principle is that the hub now knows the intended state.)
  2. The Decisive Check (ws/client.go):

    • The dispatchReadPump function in ws/client.go, which processes incoming WebSocket messages, now crucially checks the c.hub.cliEnabled flag before attempting to execute any command.
      // File: ws/client.go
      func (c *Client) dispatchReadPump(packet Packet) {
          // ...
          case "command":
      +       if c.hub.cliEnabled { // <-- THE CRITICAL FIX!
                  var command string
                  if err := json.Unmarshal(packet.Content, &command); err != nil {
                      logger.Errorf("Error reading json packet: %+v", err)
                      // Potentially return or break
                  }
                  logger.Debugf("Command was: %+v", command)
                  output, err := cli.RunCMD(command)
                  if err != nil {
                      logger.Errorf("Error running command: %+v", err)
                  }
                  logger.Debugf("Output: %+v", output)
                  c.updateCLI(output)
      +       } // else, if cliEnabled is false, this "command" packet is effectively ignored.
          // ...
      }
      

Why the fix works: The patch ensures that the WebSocket command processing logic respects the server's initial configuration. If goshs wasn't started with the -c flag (or any other mechanism that would set cliEnabled to true), then c.hub.cliEnabled will be false. Consequently, the entire block of code responsible for unmarshalling and executing the command is skipped. Our bouncer is now diligently checking for that VIP pass!

Timeline

  • Vulnerable Code Introduced: With goshs version 0.3.4 (when the WebSocket command function was added).
  • Discovery Date: Not publicly specified in the advisory beyond its presence since v0.3.4.
  • Vendor Notification: Not publicly specified. The fix commit 160220974576afe5111485b8d12fd36058984cfa was made by the project maintainer, patrickhener.
  • Patch Commit Date: The primary fix was included in commit 160220974576afe5111485b8d12fd36058984cfa. (GitHub shows this commit was made on Oct 29, 2023).
  • Patched Version Release (1.0.5): Tagged and released, incorporating the fix. The advisory indicates 1.0.5 as the first patched version.
  • Public Disclosure (GHSA-rwj2-w85g-5cmm & CVE-2025-46816): May 6, 2025.

Lessons Learned

This vulnerability, CVE-2025-46816, in goshs offers several valuable lessons:

  • Prevention Practices:

    • Secure by Default: This is paramount. Features with significant security implications, like remote command execution, should always be opt-in and disabled by default. Never assume users will run software only in perfectly secure, isolated environments.
    • Explicit Authorization Checks: Every privileged operation must explicitly verify that the requestor is authorized AND that the server is configured to allow such an operation. It's not enough to receive a "command" request; the system must check if it should process it.
    • Configuration Awareness: Ensure that runtime behavior (like handling WebSocket messages) is directly and correctly influenced by initial configuration settings, especially security-critical ones.
  • Detection Techniques:

    • Network Monitoring: Keep an eye on network traffic. Unexpected WebSocket connections or unusual traffic patterns to goshs instances (or any simple server) should raise suspicion, especially if you aren't intentionally using WebSocket features.
    • Endpoint Detection and Response (EDR): Monitor for anomalous process creation. If goshs (or a similar lightweight server) suddenly starts spawning shell processes (bash, sh, powershell, cmd.exe), it's a massive red flag indicating potential compromise.
    • Log Analysis: Regularly review application and server logs for errors, warnings, or debug messages that might indicate attempted or successful exploitation (e.g., "Error running command" if an attacker tries a malformed command).
  • One Key Takeaway: Defaults dictate destiny (for security). A simple oversight in how default behavior is handled can transform a useful utility into a gaping security hole. Always scrutinize default configurations, especially for network-facing services, and champion the principle of "secure by default" in your own development practices.

References and Further Reading

So, the next time you spin up a "simple" server or use a utility that offers powerful features, take a moment to ask: what are its defaults, and could they be an open invitation rather than a locked door? Stay vigilant, patch promptly, and question defaults!

Read more