Back to skill

Security audit

OpenScan

Security checks for vulnerabilities and agentic risk

Overview

OpenScan is a coherent malware-scanning skill, but a real command-injection bug can let a specially named file run commands on macOS, so it needs review before use.

Treat this as a Review item rather than confirmed malware. Do not run the current scanner with sudo or against untrusted macOS files until the codesign check is changed to an argument-array API such as execFileSync. The YARA-style strings are expected for a malware scanner, but the command-injection and malformed-binary crash issues should be fixed before relying on it for security decisions.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
lib/scanner.js:212
Finding

Shell Command Injection Through Code-Signature Verification

Content
View full analysis
&1`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }); return { valid: true, signed: true }; } catch (err) { const output = err.stdout || err.stderr || ''; if (output.includes('not signed')) { return { valid: false, signed: false, error: 'Not signed' }; } return { valid: false, signed: true, error: output.trim() }; } } ``` The attacker-controlled path originates from the CLI and reaches the vulnerable function through the following logic: ```javascript const targetPath = args.find(a => !a.startsWith('--')); const fullPath = path.resolve(targetPath); results = [await scanFile(fullPath, options)]; ``` ### Technical Analysis `checkCodeSignature()` inserts `filePath` directly into a command string passed to `child_process.execSync()`. Unlike argument-based process execution, `execSync()` runs the string through a shell. Surrounding the path with double quotes does not make this safe. Shell command substitution such as `$(command)` remains active inside double quotes. A filename containing a double quote can also terminate the quoted argument and introduce shell control operators. The vulnerable branch is reached on macOS when the scanned file is a Mach-O binary or has its owner-executable permission bit set: ```javascript if (process.platform === 'darwin' && (result.type === 'macho' || isExecutable(filePath))) { result.details.codesign = checkCodeSignature(filePath); } ``` The scanner is explicitly intended to process untrusted files, so filenames and paths must be treated as hostile input. ### Attack Path 1. An attacker creates or distributes a Mach-O or executable file whose ...[truncated 1360 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/elf.js:51
Finding

Malformed 64-bit ELF File Can Abort a Scan

Content
View full analysis
buffer.readUInt16LE(off) : (off) => buffer.readUInt16BE(off); const readU32 = isLE ? (off) => buffer.readUInt32LE(off) : (off) => buffer.readUInt32BE(off); const readU64 = isLE ? (off) => buffer.readBigUInt64LE(off) : (off) => buffer.readBigUInt64BE(off); // ... const shOff = is64 ? Number(readU64(40)) : readU32(32); const shEntSize = is64 ? readU16(58) : readU16(46); const shNum = is64 ? readU16(60) : readU16(48); const shStrIndex = is64 ? readU16(62) : readU16(50); ``` ### Technical Analysis The parser applies a single 52-byte minimum-length check to both 32-bit and 64-bit ELF files. A valid 64-bit ELF header is 64 bytes long, and the 64-bit parsing branch reads fields through byte offset 63. An attacker can provide a buffer that: - Begins with the ELF magic bytes; - Sets `EI_CLASS` to `ELFCLASS64`; - Has a total size from 52 through 63 bytes. Such a file passes the initial validation but causes `Buffer.readUInt16*()` or another header read to exceed the buffer boundary. Node.js then throws a `RangeError`. The exception is not converted into a per-file finding inside `scanFile()` or `scanDirectory()`. Consequently, one malformed file can reject the scan operation and prevent remaining files from being inspected. ### Attack Path 1. An attacker places a truncated file with valid ELF magic and a 64-bit class marker in a directory to be audited. 2. The us ...[truncated 935 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/macho.js:68
Finding

Truncated FAT Mach-O Header Can Abort a Scan

Content
View full analysis
10) return null; // Sanity check ``` ### Technical Analysis `parseMachO()` only requires four bytes before recognizing a FAT Mach-O magic value and calling `parseFatBinary()`. The FAT parser immediately reads a 32-bit architecture count at offset 4, which requires a buffer of at least eight bytes. A crafted file containing only a four- to seven-byte FAT Mach-O prefix therefore passes the format check but causes `readUInt32BE(4)` to throw a `RangeError`. As with the ELF parser issue, parsing errors are not isolated on a per-file basis. A malformed FAT Mach-O can therefore terminate a single-file scan or interrupt recursive directory scanning. ### Attack Path 1. An attacker creates a four- to seven-byte file beginning with `FAT_MAGIC` or `FAT_CIGAM`. 2. The attacker places the file in a directory that a victim or automated service will scan. 3. `isMachO()` and `parseMachO()` recognize the FAT magic value. 4. `parseMachO()` calls `parseFatBinary()` without verifying that eight header bytes are available. 5. `parseFatBinary()` reads `nArch` at offset 4. 6. Node.js throws an out-of-ra ...[truncated 494 chars]
Remediation
View remediation
10) { return null; } if (FAT_HEADER_SIZE + nArch * FAT_ARCH_SIZE > buffer.length) { return null; } // Continue parsing... } ``` Additional hardening should include: 1. Verify all architecture offsets and sizes using overflow-safe range checks. 2. Validate minimum regular Mach-O header lengths before calling `parseSingleMachO()`. 3. Catch parser exceptions per file and report malformed input without aborting the whole directory scan. 4. Add regression tests for truncated FAT headers and architecture tables. 5. Fuzz both FAT and single-architecture Mach-O parsers with malformed offsets, sizes, command counts, and command lengths. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (6)

YARA rule 'keylogger_indicators': Keylogger functionality in scripts or source code [malware]

High
Category
YARA Match
Confidence
70% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · README.md (reported line 34)May include surrounding context.

ecutable stack/heap, missing NX

  • Packing/encryption: High entropy detection, encrypted segments
  • Segment anomalies: Suspicious names like __INJECT, UPX, __MALWARE

Pattern Detection

Scans binary content for:

  • Shellcode patterns: x86/x64 prologue sequences, NOP sleds, infinite loops
  • Suspicious APIs: Process injection (CreateRemoteThread, VirtualAllocEx), keylogging (GetAsyncKeyState), anti-debugging (IsDebuggerPresent)
  • Network indicators: Embedded URLs, IP addresses
  • Encoded payloads: Large base64 blobs

Script Analysis

For shell scripts, Python, JavaScript, etc.:

  • Dangerous patterns: curl | bash, eval(), base64 decode + exec
  • Persistence mechanisms: crontab, launchctl, LaunchAgents
  • Injection vectors: LD_PRELOAD, DYLD_INSERT_LIBRARIES
  • Obfuscation: Heavy hex escaping, very long lines

Installation

bash
# Clone the repo
git clone https://github.com/marqbritt/openscan.git
cd openscan

# No dependencies - p

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · README.md (reported line 41)May include surrounding context.

md
### Script Analysis

For shell scripts, Python, JavaScript, etc.:
- **Dangerous patterns**: `curl | bash`, `eval()`, base64 decode + exec
- **Persistence mechanisms**: crontab, launchctl, LaunchAgents
- **Injection vectors**: `LD_PRELOAD`, `DYLD_INSERT_LIBRARIES`
- **Obfuscation**: Heavy hex escaping, very long lines

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · lib/scanner.js (reported line 154)May include surrounding context.

js
{ pattern: /nc\s+-[el]/gi, name: 'netcat listener/exec' },
    { pattern: /\/dev\/tcp\//gi, name: 'bash /dev/tcp network' },
    { pattern: /rm\s+-rf\s+[\/~]/gi, name: 'dangerous rm -rf' },
    { pattern: /chmod\s+[0-7]*777/gi, name: 'chmod 777' },
    { pattern: />\s*\/etc\//gi, name: 'writing to /etc' },
    { pattern: /crontab/gi, name: 'crontab modification' },
    { pattern: /launchctl/gi, name: 'launchctl usage' },

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · README.md (reported line 7)May include surrounding context.

md
## Why This Exists

OpenClaw skills can declare binary dependencies via `requires.bins`. Users install these binaries from various sources (Homebrew, npm, apt, random GitHub releases). There's currently no verification that these binaries are safe.

This scanner provides:
- **Pre-trust scanning** of binaries before execution

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · lib/scanner.js (reported line 154)May include surrounding context.

js
{ pattern: /nc\s+-[el]/gi, name: 'netcat listener/exec' },
    { pattern: /\/dev\/tcp\//gi, name: 'bash /dev/tcp network' },
    { pattern: /rm\s+-rf\s+[\/~]/gi, name: 'dangerous rm -rf' },
    { pattern: /chmod\s+[0-7]*777/gi, name: 'chmod 777' },
    { pattern: />\s*\/etc\//gi, name: 'writing to /etc' },
    { pattern: /crontab/gi, name: 'crontab modification' },
    { pattern: /launchctl/gi, name: 'launchctl usage' },

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
lib/scanner.js:205