一、什么是OS命令注入?
1. 基本概念
OS(Operating system)命令注入(也称为 Shell 注入)是一个 Web 安全漏洞,允许攻击者在运行应用程序的服务器上执行任意操作系统 (OS) 命令,这会破坏应用程序及其所有数据。
2. Shell的概念:
Shell翻译过来就是” 壳”, 操作系统的外壳。Shell接收用户指令, 调用相应的应用程序, 是一种用C语言编写的程序设计语言, 同时也是“为使用者提供操作界面”的命令解析器。
3. Shell的执行过程:
当用户输入Shell命令时,Shell负责将命令解释给内核,内核去运行,最后将处理完的结果反馈给用户;所以说Shell既是一种程序设计语言,又是命令解释程序。
而我们常用的CMD和Windows Power Shell是两种不同的Windows命令行工具, 其中PowerShell要更强大, 是微软发布的目前最新的一代命令行和脚本语言一体化工具。
二、利用OS命令注入漏洞能做什么?
1. 攻击示意图
Curl是一种支持各种协议的传输工具, 经常在Linux系统中使用;
通过curl命令调用受害网站的的backdoor.sh脚本文件;这样就可以控制受害者主机, 访问我不该访问的服务, 获取我不该获取的数据,连接我不该连接的网络,篡改我不该篡改的权限。
2. DDOS攻击:
比如说DDOS攻击: 我可以通过Shell注入控制你的电脑, 这台电脑就是我们常说的肉鸡/傀儡机, 我控制了你的电脑, 就可以用你的电脑, 你的IP对目标网站发起请求, 进行DDOS攻击;我控制几百台几千台不断的发起请求, 这就会影响目标服务器的正常访问。(Powershell -Command Invoke-WebRequest -Uri https://wwww.baidu.com -Method POST -Body @{username=‘admin’; password='abc’})
3. 上传非法脚本执行Shell命令
结合文件上传漏洞:继承Web服务器程序权限,执行系统命令,控制整个服务器。
结合路径遍历漏洞:访问文件存储路径, 清空所有数据文件。
4. 反弹shell
假设现在我们有两台主机一台主机A、一台主机B,我们想通过A主机控制B主机。那么如果用正向Shell,其实就是在A主机上输入B主机的连接地址用户名和密码,连接到B,连接成功之后,我们就可以在A主机通过命令控制B主机了, 通过Shell注入可以绕过输入用户密码的步骤。
但如果现在主机A是公网, 主机B是内网, 而由于防火墙等限制,主机A无法访问到主机B,这时候怎么办?这时候我们就可以利用反弹Shell攻击技术,在公网主机上先开启一个监听端口,然后再通过Shell注入让内网主机去连接公网主机的这个端口,连接成功之后,公网主机这边就能通过命令控制内网主机了。
问:既然已经可以Shell注入进内网主机了,为什么还要让内网主机去连接控制端主机开启的端口,再由控制端主机发命令去控制内网主机?
答:便捷;扩大权限;持久化(利用注入点可能会产生系统日志,可以防止后续注入点被修复无法再控制的情况;目标端IP动态变化)
三、如何检测OS命令注入漏洞?
1. 检查代码
public void shellInjectionSimple(String command) {
try {
// 用于调用外部可执行程序或系统命令,这允许攻击者执行任意的外部命令。
Process process = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2. Shell盲注
与SQL盲注相同,SQL盲注都有哪些方式:
· SQL语句执行结果True/False,触发不同的响应;
· SQL语句注入服务器是否报错;
· 延迟执行,判断执行时间的长短;
· OAST带外技术盲注;
四、如何防止OS命令注入?
1. 永远不要从应用层代码中调用操作系统命令 。
2. 参数化执行
public void shellInjectionParametrize(String[] args) {
try {
String command = ""; // Hard code
// ProcessBuilder类是Java中用于执行外部命令的工具类,它提供了一种安全的方式来执行命令
ProcessBuilder builder = new ProcessBuilder(command);
List parameters = Arrays.asList(args);
builder.command().addAll(parameters); // 使用command()方法将其添加到ProcessBuilder对象中
Process process = builder.start(); // 调用start()方法来启动进程
BufferdReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); // 使用BufferedReader读取输出流中的数据
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitCode = process.waitFor();
System.out.println("Exit code: " + exitCode);
} catch (IOException e) {
e.printStackTrace();
}
}
3. 如果不可避免地使用用户提供的输入调用操作系统命令,则必须执行强输入验证:
a. 添加白名单验证;
b. 控制输入字符(仅包含字母数字字符,不包含其他语法/空格/特殊字符等)
4. 不建议仅通过转义来控制输入,容易出错,并且很容易被熟练的攻击者绕过。
对参数进行Base64编码,Base64编码可以作为一种简单的安全措施来保护不受信任的参数,只能保证攻击者无法在传输过程中直接执行。