Command Injection(命令注入)是一种广泛存在且危害极大的漏洞。该漏洞源于程序对用户输入的内容没有校验或校验不严谨,而将其直接拼接到终端命令中,黑客可以通过构造特殊的命令字符串,使得被攻击的系统可以执行黑客自定义的命令。
不像SQL注入随着ORM框架的流行和开发人员安全意识的提升逐渐减少,命令注入漏洞仍在嵌入设备(如家用光猫、家用路由器)的Web管理界面和一些服务器系统管理页面中广泛存在。
假如我们的网站提供了这样一种功能:用户输入一个IP地址,服务端通过ping
命令检测服务端到IP地址之间的网络是否连通。例如用户输入192.168.1.1
,网站服务端执行ping 192.168.1.1
,以此实现检测网络的功能。
如果我们的网站服务端没有对用户输入的内容进行任何校验,那么攻击者可能构造192.168.1.1 && uname -a
,其中&&
是Linux中的命令连接符,此时服务端执行的命令就是ping 192.168.1.1 && uname -a
,攻击者自定义的命令uname -a
被执行了。
这里我们使用PHP搭建一个漏洞演示页面。下面代码逻辑非常简单,我们页面上有一个表单,输入IP地址提交给服务端后,服务端会拼接为ping
命令,实现网络检测。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Ping检测</title>
</head>
<body>
<h1>网络检测</h1>
<form action="index.php" method="post">
<input type="text" name="ipaddr">
<input type="submit" value="提交">
</form>
<pre id="text">
<?php
if (isset($_POST["ipaddr"])) {
$ipaddr = $_POST["ipaddr"];
$result = shell_exec("ping -c 4 " . $ipaddr);
echo $result;
}
?>
</pre>
</body>
</html>
我们输入192.168.1.1
,页面上会回显ping
命令的结果。
然而由于服务端没有对输入进行任何校验,我们其实可以使用&&
连接符执行自定义命令。
我们构造输入192.168.1.1 && cat /flag
,此时拼接后在服务端执行的命令即为ping 192.168.1.1 && cat /flag
,我们的自定义命令cat /flag
也会在服务端执行!
通过该命令注入漏洞,我们很轻松的就可以拿到服务器的控制权限。
在实际开发中,避免命令注入漏洞的方式就是对请求内容进行校验。上面代码中我们要求用户输入的是IP地址,如果我们对其输入的合法性进行校验就能避免命令注入漏洞了。下面例子中我们使用正则表达式对用户输入进行了检查(例子中仅支持IPv4):
if (isset($_POST["ipaddr"])) {
$ipaddr = $_POST["ipaddr"];
$regex = "/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/";
if (preg_match($regex, $ipaddr)) {
$result = shell_exec("ping -c 4 " . $ipaddr);
echo $result;
} else {
echo '请输入合法的IP地址!!!';
}
}
此时用户输入一些不合法的字符串尝试命令注入是不会通过校验的。