编外:请参考本文: http://www.2cto.com/Article/201108/98870.html
来源:http://xuser.org/read.php?18 作者:xuser@fsafe 今天在微博上看见的关于wordpress出现了漏洞,随即赶紧打开相关页面分析具体原因,发现是timthumb.php远程存储文件时候的验证上不足而产生的漏洞。大概分析过程如下: 该文件对提交的src变量提交并验证后存储到服务器上
$src = get_request ('src', '');
利用parse_url ($src)将src进行url分割,然后进行验证
global $allowedSites; // work out file details $filename = 'external_' . md5 ($src); $local_filepath = DIRECTORY_CACHE . '/' . $filename; // only do this stuff the file doesn't already exist if (!file_exists ($local_filepath)) { if (strpos (strtolower ($src), 'http://') !== false || strpos (strtolower ($src), 'https://') !== false) { if (!validate_url ($src)) { display_error ('invalid url'); } $url_info = parse_url ($src); if (count (explode ('.', $url_info['path'])) > 2) { display_error ('source filename invalid'); } if (($url_info['host'] == 'www.youtube.com' || $url_info['host'] == 'youtube.com') && preg_match ('/v=([^&]+)/i', $url_info['query'], $matches)) { $v = $matches[1]; $src = 'http://img.youtube.com/vi/' . $v . '/0.jpg'; $url_info['host'] = 'img.youtube.com'; //如果来源是youtube,则修改之前存储的host } $isAllowedSite = false; // check allowed sites (if required) if (ALLOW_EXTERNAL) { //ALLOW_EXTERNAL默认为false $isAllowedSite = true; } else { foreach ($allowedSites as $site) { if (strpos (strtolower ($url_info['host']), $site) !== false) //在$url_info['host'])查找是否存在$site $isAllowedSite = true; //当为true就继续下一步的存储 } } }
其中$allowedSites数组在文件头定义如下
$allowedSites = array ( 'flickr.com', 'picasa.com', 'img.youtube.com', );
进过一系列的验证如果$isAllowedSite如果为真就开始存储这个文件到服务器上
if ($isAllowedSite) { if (function_exists ('curl_init')) { global $fh; $fh = fopen ($local_filepath, 'w'); $ch = curl_init ($src); curl_setopt ($ch, CURLOPT_TIMEOUT, CURL_TIMEOUT); curl_setopt ($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0'); curl_setopt ($ch, CURLOPT_URL, $src); curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt ($ch, CURLOPT_HEADER, 0); curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt ($ch, CURLOPT_FILE, $fh); curl_setopt ($ch, CURLOPT_WRITEFUNCTION, 'curl_write'); // error so die if (curl_exec ($ch) === FALSE) { unlink ($local_filepath); touch ($local_filepath); display_error ('error reading file ' . $src . ' from remote host: ' . curl_error ($ch)); } curl_close ($ch); fclose ($fh); // check it's actually an image $file_infos = getimagesize ($local_filepath); // no mime type or invalid mime type if (empty ($file_infos['mime']) || !preg_match ("/jpg|jpeg|gif|png/i", $file_infos['mime'])) { unlink ($local_filepath); touch ($local_filepath); display_error ('remote file not a valid image'); } } else { if (!$img = file_get_contents ($src)) { display_error ('remote file for ' . $src . ' can not be accessed. It is likely that the file permissions are restricted'); } if (file_put_contents ($local_filepath, $img) == FALSE) { display_error ('error writing temporary file'); } } if (!file_exists ($local_filepath)) { display_error ('local file for ' . $src . ' can not be created'); } $src = $local_filepath; } else { display_error ('remote host "' . $url_info['host'] . '" not allowed'); }
使用了strpos验证是否匹配列表中的条件应该是考虑到其分域名问题,不过也形成了一些安全隐患,例如picasa.com.yourdomain.com也是符合这个匹配条件的。关于存储文件的位置信息在如下代码中
$filename = 'external_' . md5 ($src); $local_filepath = DIRECTORY_CACHE . '/' . $filename;
不过通过修改这个匹配规则或直接删除$isAllowedSite所有数组元素都可以临时解决该问题。 备注:发现一些timthumb.php版本中$isAllowedSite数组元素和存储文件方式不太一致,不过 漏洞 产生原因与此关系不是很大
查看更多关于WordPress中timthumb.php远程文件存储漏洞分析 - 网站的详细内容...