4

Files on my server have been corrupted. I want to remove a 13000 character string from all PHP files containing it.

The string looks like:

?php if(!isset($GLOBALS["\x61\156\x75\156\x61"])) { $ua=strtolower($_SERVER[ ... $qhroczocgv=$qjhvvbyvyv; $qhroczocgv=(729-608); $boxknervrr=$qhroczocgv-1; ?>

With ellipses inserted for brevity.

When I search for the string using grep, I get a

grep: Invalid back reference" despite escaping \![]$

How do I first find all files with the entire string and then how do I remove the text from every file?

  • 6
    That isn't corrupted. That's "obfuscated" code! Burn your server and start over! DON'T. USE. IT. AGAIN. I hope I'm being just paranoid. – Ismael Miguel Oct 13 '15 at 22:36
  • 1
    Obfuscated aka somebody got into your server and maliciously inserted that code into your PHP files. You have a security loophole somewhere – andrewtweber Oct 14 '15 at 0:54
  • 1
    You should at the same time ask about how to assess the current impact/damage on your environment, and future remediation over at Security.SE. – h.j.k. Oct 14 '15 at 3:00
  • What everyone else says plus this advice. If you keep a clean backup or have your source code in a repository such as GitHub, you could quickly solve this infection by just destroying the infected code and deploying a clean code base. That said the infection came from somewhere. So even if you deploy new code it still doesn't mean that your server is safe. Meaning if this is PHP code you need to plug up the holes. If this is a WordPress site, you would need to upgrade WordPress to patch the holes. Plug-ins as well. Don't patch? It's just a matter of time before this happens again. – JakeGould Oct 14 '15 at 7:30
  • Ah, found it on Security.SE. – h.j.k. Oct 16 '15 at 10:23
5

Assuming you have decent coding conventions, just delete any line greater than a certain size:

shopt -s extglob nullglob
sed -i.bak -r '/.{10000}/d' **/*.php

for @wildcard:

find . -name '*.php' -print0 | while IFS= read -rd "" file; do
    before=$(wc -l < "$file")
    after=$(sed -r '/.{10000}/d' "$file" | wc -l)
    case $(( diff = before - after )) in
        0) :;;  # no-op
        *) echo "will remove $diff lines from $file";;
    esac
done
  • +1 for simplicity. I'd recommend printing the lines first and filtering it through uniq to obviate (make unnecessary) any assumptions about coding conventions. Not familiar enough with sed to say how to do that, but maybe you can edit your answer to say? – Wildcard Oct 13 '15 at 20:56
  • @Wildcard, how's that? – glenn jackman Oct 13 '15 at 22:06
  • I meant something simpler, in line with your original answer: grep -he '.\{10000\}' **/*.php | sort | uniq followed by the sed line you originally specified. I somehow thought you could use sed to simply print out lines like this; maybe not. – Wildcard Oct 13 '15 at 22:16
2

Try with fgrep or, equivalently, grep -F. This will interpret the pattern as a fixed string.

You can also put that single string in a file (by itself) and use grep -f filename to specify the file. You'll still need the -F flag though.

See man grep for other options; there are several that could be useful.

For various ways of delete a line matching a given pattern, please see https://stackoverflow.com/a/5413132/5419599.

Putting it together, one approach is:

  1. Put that line of text in a file by itself. Call it "patternfile".
  2. Run grep -lrFf patternfile . > filelist
  3. Edit filelist to remove the line ./patternfile
  4. Run for i in $(cat filelist) ; do grep -vf patternfile $i > temp && chmod --reference=$i temp && mv temp $i ; done

In step 2, the grep options are: -l to list matching files; -r to recurse into subdirectories; -F to use a fixed string as a pattern to match; -f to use the file patternfile as the pattern to match; then of course > filelist to make a file containing the list of matching files.

In step 4, grep simply uses the -v flag to print nonmatching lines, and then chmod ensures you won't have permissions problems, and mv puts the file back in place.

There may be better ways but I think this will suffice.

EDIT: If you're running this as root, and not all these files are owned by root, do the following revised version of step 4:

for i in $(cat filelist) ; do grep -vf patternfile $i > temp && chown --reference=$i temp && chmod --reference=$i temp && mv temp $i ; done

The original step 4 is fine if you are the owner of all the files.

0

Assuming this string is fairly unique in your files, you could use some regex to find a few notable parts of the string and a find with a sed replace to switch it out

 find . -name "*.php" -exec sed -i 's/?php.*strtolower.*qhroczocgv.*boxknervrr.*-1; ?>//g' {} \;

That will leave a blank line in its place though.

Do you know if this string will be the only thing on a line that it is present on? if so you could try to short hand it with:

find . -name "*.php" -exec sed -i 's/?php.*strtolower.*qhroczocgv.*boxknervrr.*-1; ?>//g' {} \;

Which will look for a string that has php, any number of other characters followed by strtolower any number of other characters followed by qhroczocgv followed by any number of other characters followed by boxknervrr followed by any number of other characters, and then deletes the entire line.

Also in the find portion, . is of course representing current directory, but you could switch that out for the directory of your choice.

  • Note the danger if the string contains any single quotes. – Wildcard Oct 13 '15 at 20:45
  • Ah, yes, I hadn't looked closely enough. Single quotes wouldn't cause trouble but as you say, non-corrupted matching lines would be a big problem. – Wildcard Oct 13 '15 at 20:51
  • 1
    Yeah, Fixed it so it should snipe out just the string itself and leave everything before and after. But overall I like your idea of using a pattern file, pretty slick. I'll leave this up for completeness though, maybe it will help. – Gravy Oct 13 '15 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.