目录
在不同的环境之下,相同的代码可能带来不一样的实现效果,对于开发人员来说,不同的环境应该写出兼容性更好的代码,对于安全人员来说,利用环境,也可以让一行普通的语句变成漏洞。
一、PHP的特性
1.弱类型
(1)简介
在PHP中有一些相等的值:
在PHP中,比较两个值是否相等,可以用“==”或者“===”两种符号,前者会在比较的时候自动进行类型转换,而不是改变原来的值,所以存在漏洞的位置往往是“==”,其中的一个常见错误用法就是:
这个时候,如果input变量的值为labc,则比较的时候labc会被转换为1,if语句的条件满足,进而造成其他的漏洞,另一个常见的场景是在运用函数的时候,参数和返回值经过了类型转换造成漏洞。
(2)实例1
如何才能满足下面这样一个if判断语句?
需要使两个变量不相等而MD5值相等。这样的思路可以通过MD5碰撞的方法来解决。对于PHP来讲,MD5的返回值是一个32位的字符串,若这个字符串以“0e”开头,类型转换机制将会把它识别为一个科学计数法表示的数字“0”,下面给出两个MD5以0e开头的字符串
aabg7XSs=0e087386482136013740957780965295
aabC9Rqs=0e041022518165728065344349536299
提交这两个字符串即可绕过
(3)实例2
如果上面的“==”变成了“===”就不能成功了,但是仍然可以利用PHP的特性来进行,可以在URL栏中提交a[]=1&b=[]=2成功绕过,因为当我们令MD5函数的参数为一个数组的时候,函数会报错,并返回NULL值,虽然函数的参数时两个不同的数组,但是返回的值都是NULL。
同样是在程序返回值中容易判断错误的函数还有很多,如:strpos,见PHP手册
这也是一种经常能见到的写法,当str1在str2开头时,函数的返回值是0,而0 == false是成立的,这就会造成开发者逻辑之蛙的结果。
2.PHP反序列化漏洞
PHP提供的serialize和unserialize函数将任意类型的数据转换成string类型或者从string类型还原成任意类型。当unserialize函数的参数被用户控制的时候就会形成反序列化漏洞。
与之相关的是PHP语法中的类,PHP的类中可能会包含一些特殊的函数,名为magic函数,magic函数的命名方式是以符号“——”开头的,比如:_construct、_destruct、_toString、_sleep、_wakeup等。这些函数在某些情况下会被自动调用。
为了更好的理解magic函数时如何工作的,我们可以自创一个PHP文件,并在其中增加magic函数:_construct、_destruct、_toString。_construct在对象创建时被调用,_destruct在PHP脚本结束时被调用。若我们在反序列化的时候加入一个类,并控制类中的变量值,那么结合具体的代码就能够执行magic函数里的危险逻辑。
实例1:
页面的功能是从cookie中反序列化过后的对象打印出来的,这样,_toString()函数就会在打印的时候被调用,在本地生成filelist对象的时候,可以将scouce变量的值设置为想要读取的文件名,序列化后再提交即可,生成的序列化字符串的代码如下:
将打印出来的字符串作为参数提交,即可读取/ect/psswd文件。
如果代码量复杂,使用了大量的类,需要构造ROP链来来进行利用。
3.截断漏洞
NULL字符截断是最有名的截断漏洞之一,原理是:PHP内核是有C语言实现,因此使用了C语言中的一些字符串来处理函数,在遇到NULL(\x00)字符时,处理函数就会将它当做结束标记,这个漏洞能帮助我们去掉变量结尾不想要的字符,代码如下:
按照正常的程序逻辑来说,这段代不能直接包含任意文件,但是在NULL字符的帮助下,我们只需要提交:
即可读取到psswd文件。与之类似的是利用路径的长度绕过,比如:
系统在处过长的路径时会主动选择截断他,不过这两个漏洞已经随着PHP版本的更新而消失,遇到这种情况会越来越少,另外造成截断的方法就是不正确地使用icov函数:
在遇到file变量中包含非法字符UTF-8字符的时候,iconv函数就会截断这个字符串。
在这个场景之中,我们只需要交“?file=shell.jpg%ff”即可,因为在UTF-8字符集中单个“\x80-\-xff”都是非法的,这个漏洞只在windows系统中存在,在新版本的PHP中也已经得到修复。
4.伪协议
截断漏洞在新版本的PHP中难以实现,但是我们能通过伪协议去绕过,但是这种情况只适用于我们能控制include指令参数的前半部分的时候,若在php.ini的设置中让allow_url_include=1,即允许远程包含的时候,我们可以令参数为:
这样,PHP服务会从攻击者的服务器上取得shell.jpg并包含,如果我们能上传自定义图片的话,那么我们可以将webshell改名为shell.php并压缩成zip上传,然后再利用zip协议包含:
这样即可包含到shell,与zip协议效果相同的还有phar协议。
除此之外,我们还能通过伪协议读取到部分文件,在上面的例子中,如果服务器上有一个index.php,那我们可以令参数为:
然后,就能在页面中得到index.php文件源码base64编码后的字符串了。
5.变量覆盖
变量覆盖漏洞通常是使用外来参数替换或初始化程序中原有变量的值。
(1)函数使用不当
A.extract函数
考虑代码如下:
此处的extract函数GET传入的数据转换为变量名和变量的值,所以这里构造如下payload,即可将$auth的值为true并获得flag:
B.parse_str函数
考虑如下代码:
此处的parse_str函数同样也是将GET转入的字符串解析为变量,所以payload与上方的extract函数的payload一样。
C.import_request_variables函数
此处,import_request_variables函数的值由G、P、C三个字母组合而成,G代表GET,P代表POST,C代表Cookies。排在前面的字符会覆盖排在后面的字符传入参数的值,如,参数为“GP”。且GET和POST同时传入了auth参数,则POST传入的auth会被忽略。需要注意的是,这个函数自PHP4.5起就被移除了。
(2)配置不当
在PHP版本号小于5.4的时候,还存在配置问题导致的全局变量覆盖漏洞。当PHP配置register_globals=ON时,便可能出现该漏洞,考虑如下代码:
利用register_globals的特性,用户传入参数auth=1即可进入if语句块。需要注意的是,如果在if语句前初始化$auth变量,则不会触发变量。
(3)代码逻辑漏洞
在讲述代码逻辑漏洞导致的变量覆盖之前,需要大家先来了解在PHP中的$$(可变变量)。可变变量可以让一个普通变量的值作为这个可变变量的变量名,可以参考一下代码:
在新版本PHP移除了前面提到的import_request_variables函数和register_globals选项之后,开发者会选择使用foreach遍历数组(如,$_GET、$_POST)来注册变量,这样也会存在变量覆盖漏洞的情况,考虑如下代码:
此处的foreach循环就将GET传入的参数注册为变量,所以与前面一样,传入“?auth=1”,即可绕过判断flag。
6.防护绕过
主要的用到的防护手段,分别是:open_basedir和disable_function
open_basedir是PHP设置中为了防御PHP跨目录进行文件(目录)读写的方法,所有PHP中有关文件读、写的函数都会经过open_basedir的检查。
常见的绕过方法有DirectoryIterator+Glob,目前最新版本的PHP中,官方还没有修复这个问题。测试代码如下:
二、windows系统特性
1.短文件名
windows以8.3格式生成了与MS-DOS兼容的“短”文件名,以允许基于MS-DOS或16位windows的程序访问这些文件,在cmd下输入“dir/x”即可看到段文件名的效果。
在windows下的apache环境里,我们除了能爆破服务器文件,还能通过短文件直接下载长文件。
2.文件上传
另一个与文件系统相关的功能就是,上传的时候若以黑名单的形式限制后缀,那么我们可以利用文件系统的特性去绕过,比如一下代码:
在这段代码中,我们不能上传后缀名为php的文件,但是如果我们在上传的时候在PHP的后面追加高位字符[\x80-\xff],这样就可以绕过黑名单的判断而上传成功,上传的文件后缀会去掉[\x80-\xff],与高位字符具有相同效果的还有::$data,后者是利用:$DATA Alternate Data Stream。
在windows下面一个特殊符号是冒号,如果我们上传的时候将后缀改为.php:.png形式,那么在系统中最后得到的讲师0字节的php后缀文件,也就是说起到了截断的效果,但是没能成功写入内容。