来源: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));