I have a malware java script that was added to the end of every post. I tried using an SQL statement in phpMyAdmin. Here is a shortened version of that:
SET @virus = "<script>var _0x2cf4=['MSIE"+CHAR(59)+"','OPR','Chromium','Chrome','ppkcookie','location','','onload','getElementById'...(and a lot more obfuscated script)...;
UPDATE
wp_posts
SET
post_content =
REPLACE
(
post_content,
@virus COLLATE utf8mb4_unicode_520_ci,
''
);
This was not initially successful because many false matches caused deletions all over the website. I didn't have time to find out why, so I shortened the search text to be only the beginning of the script, which broke the malware and rendered it merely ugly. This got me out of trouble, but obviously it's not the best solution.
How should I do this? I have tried to tokenize the post content, using the text of the malware script as the token:
<?php
ignore_user_abort(true);
set_time_limit(30);
$path = $_SERVER['DOCUMENT_ROOT'];
require( $path.'/wp-load.php' );
$post_table = $wpdb->prefix . 'posts';
$meta_table = $wpdb->prefix . 'postmeta';
$starting_virus_text = "<script>['MSIE;','OPR','Chromium','Chrome','ppkcookie','location','";
$args = array(
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => -1,
);
$posts = get_posts($args);
echo 'Post count is '.count($posts).'<br>';
foreach($posts as $post){
$new_content = strtok($post->post_content, $starting_virus_text);
if ($post->ID == 192) { // my home page, just for example
echo 'POST 192<br><pre>';
var_dump($post->post_content);
}
if (strlen($new_content) > 20){
echo $new_content;
die;
}
}
echo '<br>Done.';
?>
This was also unsuccessful. The token is never found in the post content, even though it is there. Should I do a character-ny-character type examination and store post_content in an intermediate variable? Any advice is appreciated.
I have a malware java script that was added to the end of every post. I tried using an SQL statement in phpMyAdmin. Here is a shortened version of that:
SET @virus = "<script>var _0x2cf4=['MSIE"+CHAR(59)+"','OPR','Chromium','Chrome','ppkcookie','location','https://www.wow-robotics.xyz','onload','getElementById'...(and a lot more obfuscated script)...;
UPDATE
wp_posts
SET
post_content =
REPLACE
(
post_content,
@virus COLLATE utf8mb4_unicode_520_ci,
''
);
This was not initially successful because many false matches caused deletions all over the website. I didn't have time to find out why, so I shortened the search text to be only the beginning of the script, which broke the malware and rendered it merely ugly. This got me out of trouble, but obviously it's not the best solution.
How should I do this? I have tried to tokenize the post content, using the text of the malware script as the token:
<?php
ignore_user_abort(true);
set_time_limit(30);
$path = $_SERVER['DOCUMENT_ROOT'];
require( $path.'/wp-load.php' );
$post_table = $wpdb->prefix . 'posts';
$meta_table = $wpdb->prefix . 'postmeta';
$starting_virus_text = "<script>['MSIE;','OPR','Chromium','Chrome','ppkcookie','location','https://www.wow-robotics.xy";
$args = array(
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => -1,
);
$posts = get_posts($args);
echo 'Post count is '.count($posts).'<br>';
foreach($posts as $post){
$new_content = strtok($post->post_content, $starting_virus_text);
if ($post->ID == 192) { // my home page, just for example
echo 'POST 192<br><pre>';
var_dump($post->post_content);
}
if (strlen($new_content) > 20){
echo $new_content;
die;
}
}
echo '<br>Done.';
?>
This was also unsuccessful. The token is never found in the post content, even though it is there. Should I do a character-ny-character type examination and store post_content in an intermediate variable? Any advice is appreciated.
Share Improve this question asked Jan 29, 2020 at 19:35 ElkratElkrat 1381 silver badge9 bronze badges3 Answers
Reset to default 1Is the malware in each post in the database? If so, something is inserting it, so you need to fix that first. Lots of googles/bings/ducks on how to remove malware from a WP site. (I have my own procedure here that I use for clients: https://www.securitydawg/recovering-from-a-hacked-wordpress-site/ ; there are others.)
I'd do a thorough cleanup of the site; changing credentials (everywhere, not just WP), strong passwords throughout; look for unauthorized files; check wp-config.php and other files for inserted code; a fresh install of plugins/themes from known good sources (via FTP); update everything; update your PHP version; and more.
And then look at the wp-posts table for inserted content. The script code might be different for each post, so you may need to manually fix things.
And, of course, a backup of your database before any major changes.
The answer my friend, was blowing in the regex of str_repl() - using the opening and closing tags to excise the malware script from post_content
First I find a few specific posts in the database with samples of the malware, to get the beginning string of it, in my case:
<script>;','OPR'
Then I wrote the following script to give me the precise length of the string and to see that it's stable over my sample rows in the database:
ignore_user_abort(true);
set_time_limit(30);
$path = $_SERVER['DOCUMENT_ROOT'];
require( $path.'/wp-load.php' );
$test_posts = array(1,15002,9664,3342);
foreach ($test_posts as $post){
$post = get_post($post);
$content = $post->post_content;
$strlen = strlen($content);
// to delete a script tag... preg_replace('/(<script>.+?)+(<\/script>)/i', '', $string);
// starting virus text is <script>;','OPR' must use backslash to escape the folling characters: \ ^ . $ | ( ) [ ] * + ? { } ,
$new_content = preg_replace('/(<script>;\'\,\'OPR\'.+?)+(<\/script>)/i', '', $content);
$new_strlen = strlen($new_content);
$difference = $strlen - $new_strlen;
echo 'Post '.$post->ID.': '.$strlen.'-'.$new_strlen.'=<b>'.($difference).'</b><br>';
}
echo '<br>Done.';
I saw the same number in all the entries: 2409. So I plug 2409 into my second script that actually does the post updating:
ignore_user_abort(true);
set_time_limit(90);
$path = $_SERVER['DOCUMENT_ROOT'];
require( $path.'/wp-load.php' );
$args = array(
'post_type' => array(
'post',
'page',
'revision',
'item',
'wpsl_stores',
'nav_menu_item',
'accordion_menu',
'et_pb_layout',
'scheduled-action',
'wpsl_stores',
'popup_theme',
),
'post_status' => array('publish', 'draft', 'inherit'),
'posts_per_page' => -1,
);
$posts = get_posts($args);
echo 'Post count is '.count($posts).'<br>';
$post_of_interest = 1;
foreach($posts as $post){
$content = $post->post_content;
$strlen = strlen($content);
// starting virus text is <script>;','OPR'
$new_content = preg_replace('/(<script>;\'\,\'OPR\'.+?)+(<\/script>)/i', '', $content);
$new_strlen = strlen($new_content);
if (($strlen - $new_strlen) == 2409){ // I know my target string has a specific number of characters
echo '<br><b>*** HIT '.$post->ID.' ***</b><br>';
} else {
echo '-';
}
if($post->ID == $post_of_interest){
echo '<br>'.$post_of_interest.' original strlen='.$strlen.' New strlen='.$new_strlen.' Difference='.($strlen - $new_strlen).'<br>';
}
$args = array(
'ID' => $post->ID,
'post_content' => $new_content,
);
wp_update_post( $args );
}
echo '<br>Done.';
Hope this helps someone.
A better approach to cleaning Wordpress posts is to delete all script tags along with their contents. I happen to know there should be no scripts inside my post content so this is safe for me. Make a quick backup of your database first, so you no cry.
Also if you pay attention to what the function author did, you can use this to strip any tags out of html. Alternatively, you can strip all tags EXCEPT the one you specify by leaving the last parameter false. I set it to true, which is what makes it strip ONLY the specified tag.
The function is from https://gist.github/marcanuy/7651298
BTW if anyone can chime in on how these people are injecting these scripts, I would be very grateful. I had this happen on a site that I've hardened - moved wp-config above the public html directory, changed permissions, moved the login address, etc. It's unnerving, because while the spammy stuff makes me look bad, the real problem is that it demonstrates a security vulnerability.
<?php
ignore_user_abort(true);
set_time_limit(90);
$path = $_SERVER['DOCUMENT_ROOT'];
require( $path.'/wp-load.php' );
$args = array(
'post_type' => array( // add all your post types here, but be careful: it will strip all scripts from their content
'post',
'page',
'revision',
'item',
'wpsl_stores',
'nav_menu_item',
'accordion_menu',
'et_pb_layout',
'scheduled-action',
'wpsl_stores',
'popup_theme',
),
'post_status' => array('publish', 'draft', 'inherit'),
'posts_per_page' => -1,
);
$posts = get_posts($args);
echo 'Post count is '.count($posts).'<br>';
foreach($posts as $post){
$content = $post->post_content;
$new_content = strip_tags_content($content, '<script>', true);
$args = array(
'ID' => $post->ID,
'post_content' => $new_content,
);
wp_update_post( $args );
}
echo '<br>Done.';
function strip_tags_content($text, $tags = '', $invert = FALSE) {
preg_match_all('/<(.+?)[\s]*\/?[\s]*>/si', trim($tags), $tags);
$tags = array_unique($tags[1]);
if(is_array($tags) AND count($tags) > 0) {
if($invert == FALSE) {
return preg_replace('@<(?!(?:'. implode('|', $tags) .')\b)(\w+)\b.*?>.*?</\1>@si', '', $text);
}
else {
return preg_replace('@<('. implode('|', $tags) .')\b.*?>.*?</\1>@si', '', $text);
}
}
elseif($invert == FALSE) {
return preg_replace('@<(\w+)\b.*?>.*?</\1>@si', '', $text);
}
return $text;
}
?>