12年爆出的一个洞 前几天比赛的一个cms 于是跟出题人表哥要过来审计了看看
漏洞文件再根目录thumb.php中
1 <?php 2 /* 3 * 自动缩略图 参数 url|w|h|type="cut/full"|mark="text/image|r" 4 * thumb.php?url=/thinksns/data/userface/000/00/00/41_middle_face.jpg?1247718988&w=20&h=20 5 */ 6 error_reporting(0); 7 set_time_limit(30); 8 $biggest_memory_limit = 256; //单位M,后缀不要加M 9 //全局定义文件 10 //require 'define.inc.php'; 11 12 //临时目录 13 $tempDir = "./data/thumb_temp/"; 14 checkDir($tempDir); 15 16 //分析URL 17 $url = urldecode($_GET['url']); 18 19 //XSS脚本攻击探测 20 //include THINK_PATH.'/Vendor/xss.php'; 21 //DetectXSS($url); 22 23 //2009-10-7 修改 将本地图片修改成相对地址,避免file_get_contents不能读取远程文件时出错(可修改php.ini 设置 allow_fopen_url 为 true) 24 //$url = str_ireplace(SITE_URL,'.',$url); 25 if(file_exists($url)){ 26 $url = $url; 27 28 }elseif($result = GrabImage($url,$tempDir)){ 29 $url = $result; 30 $grab_temp_file = $result; 31 }else{ 32 $url = "./public/images/nopic.jpg"; 33 } 34 35 //解析参数 36 $w = $_GET['w']?$_GET['w']:'100'; //宽度 37 $h = $_GET['h']?$_GET['h']:'100'; //高度 38 $t = $_GET['t']?$_GET['t']:'c'; //是否切割 39 $r = $_GET['r']?'1':'0'; //是否覆盖 40 41 42 43 //目录名hash 44 $fileHash = md5($url.$w.$h); 45 $hashPath = substr($fileHash,0,2).'/'.substr($fileHash,2,2).'/'; 46 47 //缩图目录 48 $thumbDir = "./data/thumb/".$hashPath; 49 checkDir($thumbDir); 50 51 $tempFile = $tempDir.$fileHash.'.'.$sourceInfo['type']; 52 53 $thumbFile = $thumbDir.$fileHash."_".$w."_".$h."_".$t.'.'.$sourceInfo['type']; 54 55 $img = new Image(); 56 //判断是否替换,存在则跳转,不存在继续进行 57 if(!$r && file_exists($thumbFile)){ 58 //这里有2种方法,第一种多一次跳转,多一个http连接数,第二种,要进行一次php处理,多占用一部分内存。 59 //header('location:'.$thumbFile); 60 $img->showImg($thumbFile); 61 } 62 63 //不存在输出 64 if(copy($url,$tempFile)){ 65 //判断图片大小 如果图片宽和高都小于要缩放的比例 直接输出 66 $info = getimagesize($tempFile); 67 68 //判断处理图片大约需要的内存 69 $need_memory = (($info[0]*$info[1])/100000); 70 $memory_limit = ini_get('memory_limit'); 71 if( ($need_memory > $memory_limit) && ($need_memory <= $biggest_memory_limit) ){ 72 ini_set('memory_limit',$need_memory.'M'); 73 } 74 75 if($info[0]<=$w && $info[1]<=$h){ 76 77 copy($tempFile,$thumbFile); 78 $img->showImg($thumbFile,'',$info[0],$info[1]); 79 unlink($tempFile); 80 unlink($grab_temp_file); 81 exit; 82 } 83 else{ 84 85 //生成缩图 86 if($t=='c'){ 87 $thumb = $img->cutThumb($tempFile,$thumbFile,$w,$h); 88 }elseif($t=='f'){ 89 $thumb = $img->thumb($tempFile,'',$thumbFile,$w,$h); 90 } 91 //输出缩图 92 $img->showImg($thumb,'',$w,$h); 93 unlink($tempFile); 94 unlink($grab_temp_file); 95 exit; 96 } 97 }
第17行获取了url参数
第28行进入了GrabImage函数
//获取远程图片 function GrabImage($url,$thumbDir) { if($url=="") return false; $filename = md5($url).strrchr($url,"."); $img = file_get_contents($url); if(!$img) return false; $filepath = $thumbDir.$filename; $result = file_put_contents($filepath,$img); if($result){ return $filepath; }else{ return false; } }
获取url参数的内容 并写入了filepath中截取了一下
./data/thumb_temp/7588534a6998ec36787cf997e688a8d6
第53行 重新构造文件名
$thumbFile = $thumbDir.$fileHash."_".$w."_".$h."_".$t.'.'.$sourceInfo['type'];
这里$t传入.php
第77行 完成了复制的操作
copy($tempFile,$thumbFile);
导致了getshell
在后台数据库导入的地方
1 function import(){ 2 $filename = $_GET['filename']; 3 $sqldump = ''; 4 $file = './data/database/'.$filename; 5 //die($file); 6 if(file_exists($file)){ 7 8 $fp = @fopen($file,'rb'); 9 $sqldump = fread($fp,filesize($file)); 10 11 fclose($fp); 12 } 13 14 $ret = D('Database')->import($sqldump); 15 if($ret) { 16 $this->success('导入成功'); 17 }else{ 18 $this->error('导入失败'); 19 } 20 }
首先文件名没有进行过滤 可以遍历读取到东西之后进入了另外一个import函数
跟进一下
public function import($sqldump){ $sqlquery = $this->splitsql($sqldump); var_dump($sqlquery); $ret = false; $linkid = $this->db->connect(); foreach($sqlquery as $sql) { $sql = trim($sql); if(!empty($sql)) { $ret = mysql_query($sql); } } return $ret; }
没有过滤 逐行执行
但是没有上传功能呀 怎么办呢
在前台头像上传的地方
function upload(){ @header("Expires: 0"); @header("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE); @header("Pragma: no-cache"); $pic_id = time();//使用时间来模拟图片的ID. $pic_path = $this->getSavePath().'/original.jpg'; $pic_abs_path = __UPLOAD__.'/avatar'.convertUidToPath($this->uid).'/original.jpg'; //保存上传图片. if(empty($_FILES['Filedata'])) { $return['message'] = L('photo_upload_failed'); $return['code'] = '0'; }else{ $file = @$_FILES['Filedata']['tmp_name']; //die($pic_path); file_exists($pic_path) && @unlink($pic_path); if(@copy($_FILES['Filedata']['tmp_name'], $pic_path) || @move_uploaded_file($_FILES['Filedata']['tmp_name'], $pic_path)) { @unlink($_FILES['Filedata']['tmp_name']); /*list($width, $height, $type, $attr) = getimagesize($pic_path); if($width < 10 || $height < 10 || $width > 3000 || $height > 3000 || $type == 4) { @unlink($pic_path); return -2; }*/ include( SITE_PATH.'/addons/libs/Image.class.php' ); Image::thumb( $pic_path, $pic_path , '' , 300 , 300 ); list($sr_w, $sr_h, $sr_type, $sr_attr) = @getimagesize($pic_path); $return['data']['picurl'] = 'data/uploads/avatar'.convertUidToPath($this->uid).'/original.jpg'; $return['data']['picwidth'] = $sr_w; $return['data']['picheight'] = $sr_h; $return['code'] = '1'; } else { @unlink($_FILES['Filedata']['tmp_name']); $return['message'] = L('photo_upload_failed'); $return['code'] = '0'; } } return json_encode( $return ); }
大体意思就是没检测文件内容 也不管后缀是什么 读取出文件内容 统一写入一个固定的文件
我们在这里写入要执行的sql语句这里我想到的利用方法
1.留xss后门
执行一个update或者insert语句 在用户管理的表中 后台查看用户就会触发xss 这里绕过了前台的过滤持续控制后台
2.因为并没有回显
可以执行update 或者insert语句 在插入文章的
INSERT INTO `ts_ad` (`title`,`place`,`is_active`,`content`,`is_closable`,`mtime`,`ctime`) VALUES ('31',0,'0',user(),0,1502120589,1502120589)
进一步获取服务器敏感信息
3 如果有root权限可以写shell
4有时间可以尝试下时间盲注
同时删除的地方可以进行任意文件删除