T09 · Insecure Skill Coding Practices
- Location
scripts/common/invoke_hidden.ps1:52- Finding
Arbitrary command execution through unescaped StatTransfer batch filenames
- Content
View full analysis
Vulnerability Details
File Location:
scripts/common/invoke_hidden.ps1:52-73, reachable fromscripts/windows-only/StatTransfer/statsoft-stattransfer.ps1:253-270
Vulnerability Type: Windows command injection
Risk Level: HighComplete Code Snippet
Caller processing automatically enumerated filenames:
powershell # scripts/windows-only/StatTransfer/statsoft-stattransfer.ps1:253-270 foreach ($file in $files) { $baseName = [System.IO.Path]::GetFileNameWithoutExtension($file.Name) $outExt = switch ($file.Extension) { ".sav" { ".csv" } ".dta" { ".csv" } ".sas7bdat" { ".csv" } ".csv" { ".dta" } ".xlsx" { ".csv" } default { ".csv" } } $outFile = Join-Path $outputDir "$baseName$outExt" Write-Host " -> $($file.Name) -> $outFile" -ForegroundColor White -NoNewline try { $result = Invoke-StatExe -FilePath $stExePath -ArgumentList $file.FullName,$outFileVulnerable command construction and shell execution:
powershell # scripts/common/invoke_hidden.ps1:52-73 $sb = New-Object System.Text.StringBuilder [void]$sb.Append('"').Append($FilePath).Append('"') foreach ($a in $ArgumentList) { [void]$sb.Append(' ') if ($a -match '\s') { [void]$sb.Append('"').Append($a).Append('"') } else { [void]$sb.Append($a) } } [void]$sb.Append(' > "').Append($outFile).Append('" 2> "').Append($errFile).Append('"') $inner = $sb.ToString() try { $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = 'cmd.exe' $psi.Arguments = ('/c "' + $inner + '"') $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden $psi.UseShellExecute = $falseTechnical Analysis
Invoke-StatExedoes not preserve arguments as a structured argument vector. Instead, it concatenates the executable path and all arguments into a single command string a ...[truncated 2896 chars]- Remediation
View remediation
Remediation Suggestions
- Remove
cmd.exe /cfrom the launch path. Start the configured executable directly withSystem.Diagnostics.ProcessStartInfo. - Preserve argument boundaries. On supported PowerShell/.NET versions, populate
ProcessStartInfo.ArgumentListonce per argument rather than building a command string. - Capture output without shell redirection. Set:
UseShellExecute = $falseRedirectStandardOutput = $trueRedirectStandardError = $trueCreateNoWindow = $truewhere compatible with the target application
- Retain arguments as data. Do not perform manual joining, quoting, or redirection through CMD.
- Add defense-in-depth filename validation. Until the shared launcher is replaced, reject filenames containing CMD metacharacters. This should be treated only as a temporary mitigation, not the primary fix.
- Review every
Invoke-StatExecaller. Determine whether input filenames, output paths, configuration values, or additional arguments can originate from imported files, shared directories, or other lower-trust sources. - Add regression tests using filenames containing spaces and Windows shell-significant characters, including
&,^,%,(, and). Verify that the target executable receives each filename literally and that no secondary process is launched. - Keep the existing authorization gate. It remains useful for controlling expected file writes, but it must not be relied upon as protection against argument injection.
- Remove
