Skip to main content
Glama
REMnux

REMnux MCP Server

Official
by REMnux

run_tool

Execute malware analysis commands on REMnux to extract strings, analyze files, and investigate suspicious samples using tools like pestr and strings.

Instructions

Execute a command in REMnux. Supports piped commands (e.g., 'oledump.py sample.doc | grep VBA'). String extraction: For PE files use 'pestr'; for non-PE use 'strings' (ASCII) and 'strings -el' (Unicode).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYesCommand to execute (can include pipes, e.g., 'strings sample.exe | grep -i password')
input_fileNoInput file path (relative to samples dir, or absolute path in local mode) - appended to command
timeoutNoTimeout in seconds (default: 300)

Implementation Reference

  • Main handler function handleRunTool that executes commands in REMnux. Implements path validation, command security checks, discouraged pattern warnings, command execution, output truncation, auto-parsing, and advisory generation.
    export async function handleRunTool( deps: HandlerDeps, args: RunToolArgs ) { const startTime = Date.now(); const { connector, config } = deps; // Build full command let fullCommand = args.command; if (args.input_file) { // Validate input file path (skip unless --sandbox) if (!config.noSandbox) { const pathValidation = validateFilePath(args.input_file, config.samplesDir); if (!pathValidation.safe) { return formatError("run_tool", new REMnuxError( pathValidation.error || "Invalid input file path", "INVALID_PATH", "validation", "Use a relative path within the samples directory", ), startTime); } } // Append quoted file path to command (single-quotes prevent shell expansion) // Escape any single quotes in the path as defense-in-depth (isPathSafe also rejects them) const escapedFile = args.input_file.replace(/'/g, "'\\''"); const resolvedInputFile = (config.mode === "local" && args.input_file.startsWith("/")) ? escapedFile : `${config.samplesDir}/${escapedFile}`; fullCommand = `${args.command} '${resolvedInputFile}'`; } // Security: Validate command against blocklist const validation = isCommandSafe(fullCommand); if (!validation.safe) { return formatError("run_tool", new REMnuxError( validation.error || "Command blocked", "COMMAND_BLOCKED", "security", "This command is blocked for security reasons. Use an allowed tool instead.", ), startTime); } // Check for discouraged patterns BEFORE execution (unless acknowledged) // Use regex to ensure --acknowledge-raw is a flag, not part of a filename const hasAcknowledge = /(?:^|\s)--acknowledge-raw(?:\s|$)/.test(args.command); if (!hasAcknowledge) { const discouraged = checkDiscouragedPattern(fullCommand); if (discouraged) { return formatResponse( "run_tool", { warning: discouraged.warning, suggestion: discouraged.suggestion, command_blocked: true, note: "Command was NOT executed. Use suggested alternatives or add --acknowledge-raw to proceed.", }, startTime ); } } const MAX_STDOUT_RESPONSE = 100 * 1024; // 100KB — fits in LLM context const MAX_STDERR_RESPONSE = 50 * 1024; try { const execOptions: { timeout: number; cwd?: string } = { timeout: (args.timeout || config.timeout) * 1000, }; // Only set cwd when input_file is provided (file-based analysis) if (args.input_file) { execOptions.cwd = config.samplesDir; } const result = await connector.executeShell(fullCommand, execOptions); let stdout = result.stdout || ""; let stderr = result.stderr || ""; stderr = filterStderrNoise(stderr); let truncated = false; const fullStdoutLength = stdout.length; if (stdout.length > MAX_STDOUT_RESPONSE) { stdout = stdout.slice(0, MAX_STDOUT_RESPONSE); truncated = true; } if (stderr.length > MAX_STDERR_RESPONSE) { stderr = stderr.slice(0, MAX_STDERR_RESPONSE); truncated = true; } // Auto-parse output if command matches a known tool with a parser let findings; let parsedMetadata; const toolName = detectToolName(fullCommand); if (toolName && hasParser(toolName) && stdout) { const parsed = parseToolOutput(toolName, stdout); if (parsed.parsed) { findings = parsed.findings; parsedMetadata = parsed.metadata; } } // Check for advisory (non-blocking guidance) const advisory = getCommandAdvisory(fullCommand); return formatResponse("run_tool", { command: fullCommand, stdout, stderr, exit_code: result.exitCode, truncated, ...(truncated && { truncation_notice: "Output exceeded response size limit. Use 'run_tool' with '| head -N' or redirect to a file for full output.", full_stdout_length: fullStdoutLength, }), ...(findings && { findings, parsed_metadata: parsedMetadata }), ...(advisory && { advisory }), }, startTime); } catch (error) { return formatError("run_tool", toREMnuxError(error, config.mode), startTime); } }
  • Zod schema definition for run_tool input validation. Defines three parameters: command (required string), input_file (optional string), and timeout (optional number with default 300).
    export const runToolSchema = z.object({ command: z.string().describe("Command to execute (can include pipes, e.g., 'strings sample.exe | grep -i password')"), input_file: z.string().optional().describe("Input file path (relative to samples dir, or absolute path in local mode) - appended to command"), timeout: z.number().optional().describe("Timeout in seconds (default: 300)"), }); export type RunToolArgs = z.infer<typeof runToolSchema>;
  • src/index.ts:94-101 (registration)
    MCP tool registration for run_tool. Registers the tool with name 'run_tool', description, schema validation (runToolSchema.shape), and maps to handleRunTool handler function.
    // Tool: run_tool - Execute a command in REMnux server.tool( "run_tool", "Execute a command in REMnux. Supports piped commands (e.g., 'oledump.py sample.doc | grep VBA'). " + "String extraction: For PE files use 'pestr'; for non-PE use 'strings' (ASCII) and 'strings -el' (Unicode).", runToolSchema.shape, (args) => handleRunTool(deps, args) );
  • Security validation function isCommandSafe that checks commands against blocked patterns (null byte injection, catastrophic commands). Called by handleRunTool to prevent dangerous command execution.
    export function isCommandSafe(command: string): { safe: boolean; error?: string } { // Reject empty or whitespace-only commands if (!command || command.trim() === "") { return { safe: false, error: "Empty command" }; } // Check against blocked patterns for (const { pattern, category } of BLOCKED_PATTERNS) { if (pattern.test(command)) { return { safe: false, error: `Command blocked: ${category}` }; } } // Check against dangerous pipe patterns for (const { pattern, category } of DANGEROUS_PIPE_PATTERNS) { if (pattern.test(command)) { return { safe: false, error: `Command blocked: ${category}` }; } } return { safe: true }; }
  • Utility function filterStderrNoise that removes common non-actionable warnings from tool stderr output (Volatility progress bars, Python warnings, compatibility notices). Called by handleRunTool to clean stderr before response.
    export function filterStderrNoise(stderr: string): string { return stderr // Volatility 3 progress bars .replace(/^Progress:\s+[\d.]+\s+.*$/gm, "") // Python SyntaxWarning / DeprecationWarning lines .replace(/^.*(?:SyntaxWarning|DeprecationWarning):.*$/gm, "") // Python version compatibility notices .replace(/^.*(?:This version of|requires Python).*$/gm, "") // Python source context lines (indented code following warnings) .replace(/^\s+\^+\s*$/gm, "") .trim(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/REMnux/remnux-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server