WordPress第三方主题文件漏洞分析

出自sebug security vulnerability(SSV) DB
跳转到: 导航, 搜索

国外博客最近公布了关于wordpress主题的一个0day,这些主题都使用了timthumb.php这个文件,该文件用于处理图片的显示效果等,原文地址可以参见:http://sebug.net/exploit/20811/ 。这里具体分析下漏洞的成因,其实在国外这篇文章也有分析,只是不是很清晰

appdir

PHP WordPress

漏洞成因

大家可以首先打开这个链接,版本控制,可以看到:

   r143 		stronger website domain checks (don’t allow http://wordpress.com.hacker.com/)
   作者在这里标明了已经修复了这个漏洞,那么我们就对比下r143和r142两个版本的区别。

对比分析这两个版本,发现结果如下:

  1. foreach ($allowedSites as $site) { 	 
  2. 	if (strpos (strtolower ($url_info['host']), $site) !== false) { 		      
  3. 		$isAllowedSite = true;  
  4. }
  1. foreach ($allowedSites as $site) {
  2. 	if (strpos (strtolower ($url_info['host'] ./), $site) !== false) {
  3. 		$isAllowedSite = true;
  4. }

对了一个’/',在匹配的时候限制了白名单域名只能在地址的最后面,这样就限制了可以在任意的域名前面加上白名单的域名,举个例子来说,我们假如有域名xyz.com,那么我们可以随便添加二、三级域名。先看看timthumb限制了哪些域名。

  1. // external domains that are allowed to be displayed on your website
  2. $allowedSites = array (
  3.         ‘flickr.com’,
  4.         ‘picasa.com’,
  5.         ‘blogger.com’,
  6.         ‘wordpress.com’,
  7.         ‘img.youtube.com’,
  8.         ‘upload.wikimedia.org’,
  9. );

这样的话我们就可以添加blogger.com.xyz.com,成功绕过白名单的检测。看到这里我不禁想到我上次分析的百度贴吧flash过滤机制研究,也存在一定的问题,因此在匹配或者是搜索的时候需要特别注意。

利用

如果大家看了原文的留言,别人给出了利用方法(不过我没域名测试),这里我们还是对源码进行一下分析。

首先是引入url的地方:

  1. // sort out image source
  2. $src = get_request (‘src’,);
  3. if ($src ==|| strlen ($src) <= 3) {
  4.     display_error (‘no image specified’);
  5. }
  6. </pre></code>
  7.  
  8. get_request函数:
  9. <code><pre>
  10. /**
  11.  *
  12.  * @param <type> $property
  13.  * @param <type> $default
  14.  * @return <type>
  15.  */
  16. function get_request ($property, $default = 0) {
  17.     if (isset ($_GET[$property])) {
  18.         return $_GET[$property];
  19.     } else {
  20.         return $default;
  21.     }
  22. }

然后是文件检查:

  1. // clean params before use
  2. $src = clean_source ($src);
  3. // get mime type of src
  4. $mime_type = mime_type ($src);
  5. // used for external websites only
  6. $external_data_string =;
  7. // generic file handle for reading and writing to files
  8. $fh =;
  9. // check to see if this image is in the cache already
  10. // if already cached then display the image and die
  11. check_cache ($mime_type);

其中clean_source函数中调用check_external函数实现了写文件操作。

  1. **
  2.  *
  3.  * @global array $allowedSites
  4.  * @param string $src
  5.  * @return string
  6.  */
  7. function check_external ($src) {
  8.         global $allowedSites;
  9.         // work out file details
  10.         $fileDetails = pathinfo ($src);
  11.         $filename = ‘external_’ . md5 ($src);  //注意这个地方是文件生成后的文件名
  12.         $local_filepath = DIRECTORY_CACHE ./. $filename ... strtolower ($fileDetails['extension']);
  13.         // only do this stuff the file doesn’t already exist
  14.         if (!file_exists ($local_filepath)) {
  15.                 if (strpos (strtolower ($src), ‘http://’) !== false || strpos (strtolower ($src), ‘https://’) !== false) {
  16.                         if (!validate_url ($src)) {
  17.                                 display_error (‘invalid url’);
  18.                         }
  19.                         $url_info = parse_url ($src);
  20.                         // convert youtube video urls
  21.                         // need to tidy up the code
  22.                         if ($url_info['host'] == ‘www.youtube.com’ || $url_info['host'] == ‘youtube.com’) {
  23.                                 parse_str ($url_info['query']);
  24.                                 if (isset ($v)) {
  25.                                         $src = ‘http://img.youtube.com/vi/’ . $v . ‘/0.jpg’;
  26.                                         $url_info['host'] = ‘img.youtube.com’;
  27.                                 }
  28.                         }
  29.                         // check allowed sites (if required)
  30.                         if (ALLOW_EXTERNAL) {
  31.                                 $isAllowedSite = true;
  32.                         } else {
  33.                                 $isAllowedSite = false;  //注意这个地方是重点
  34.                                 foreach ($allowedSites as $site) {
  35.                                         if (strpos (strtolower ($url_info['host']), $site) !== false) {
  36.                                                 $isAllowedSite = true;
  37.                                         }
  38.                                 }
  39.                         }
  40.                         // if allowed  //判断正确了就直接写文件了。
  41.                         if ($isAllowedSite) {
  42.                                 if (function_exists (curl_init)) {
  43.                                         global $fh;
  44.                                         $fh = fopen ($local_filepath, ‘w’);
  45.                                         $ch = curl_init ($src);
  46.                                         curl_setopt ($ch, CURLOPT_TIMEOUT, CURL_TIMEOUT);
  47.                                         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);
  48.                                         curl_setopt ($ch, CURLOPT_URL, $src);
  49.                                         curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
  50.                                         curl_setopt ($ch, CURLOPT_HEADER, 0);
  51.                                         curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  52.                                         curl_setopt ($ch, CURLOPT_FILE, $fh);
  53.                                         curl_setopt ($ch, CURLOPT_WRITEFUNCTION, ‘curl_write’);
  54.                                         // error so die
  55.                                         if (curl_exec ($ch) === FALSE) {
  56.                                                 unlink ($local_filepath);
  57.                                                 touch ($local_filepath);
  58.                                                 display_error (‘error reading file. $src . ‘ from remote host:. curl_error ($ch));
  59.                                         }
  60.                                         curl_close ($ch);
  61.                                         fclose ($fh);
  62.                 } else {
  63.                                         if (!$img = file_get_contents ($src)) {
  64.                                                 display_error (‘remote file for. $src . ‘ can not be accessed. It is likely that the file);
  65.                                         }
  66.                                         if (file_put_contents ($local_filepath, $img) == FALSE) {
  67.                                                 display_error (‘error writing temporary file);
  68.                                         }
  69.                                 }
  70.                                 if (!file_exists ($local_filepath)) {
  71.                                         display_error (‘local file for. $src . ‘ can not be created’);
  72.                                 }
  73.                                 $src = $local_filepath;
  74.                         } else {
  75.                                 display_error (‘remote host “‘ . $url_info['host'] . ‘” not allowed’);
  76.                         }
  77.                 }
  78.     } else {
  79.                 $src = $local_filepath;
  80.         }
  81.     return $src;
  82. }
个人工具
名字空间
变换
导航
工具箱