我正在清理一个运行基于PHP的CMS的被黑网站。网站上的每个PHP文件都在文件第一行的开头插入了以下字符串:
<?php /**/ eval(base64_decode("aWYoZnVuY3Rpb25"));?>
(为清楚起见,我已截断了base64字符串。)
我的目标是通过bash脚本删除此字符串。我首先确保我可以遍历所有文件。
#!/bin/bash
# de-malware-ifier
for i in $(find ~/Sites/www.domain.com -name '*.php'); do
echo "file $i"
done
这可以正常工作,打印出数百个受感染文件的文件名。
然后我尝试修改bash脚本以替换每个这些文件的邪恶字符串:
#!/bin/bash
# de-malware-ifier
for i in $(find ~/Sites/www.domain.com -name '*.php'); do
echo "file $i"
evil='<?php /**/ eval(base64_decode("aWYoZnVuY3Rpb25"));?>'
sed 's/$evil//'
done
但是,运行此脚本会挂起第一个文件。为什么这个脚本挂起,我应该如何修改这个脚本来给我想要的结果呢?
我在Mac OSX上。
答案 0 :(得分:1)
它挂起的原因是因为你没有给sed一个文件名,所以它正在等待stdin的输入。
要编辑文件,您应该使用:
sed -i bak 's/foo/bar/' "$i"
请注意,这还不足以修复您的脚本。其他问题包括:
fgrep -v
。$evil
不会在单引号中展开。使用双引号。答案 1 :(得分:0)
Sed缺少输入。
试试这个:
#!/bin/bash
# de-malware-ifier
for i in $(find ~/Sites/www.domain.com -name '*.php'); do
echo "file $i"
evil='<?php \/\*\*\/ eval(base64_decode("aWYoZnVuY3Rpb25"));?>'
sed -i "s/$evil//" $i
done
PS:我不确定你是否需要逃避其他事情&#34; $ evil&#34;。
答案 2 :(得分:0)
正如其他人指出的那样,你错过了sed命令的文件名,但是不要尝试使用sed,因为sed不能对字符串进行操作,只能对RE进行操作。如果他们能够提供一个标志告诉sed将其视为搜索模式,那么GNU的家伙们不会浪费时间在sed的化妆品-i
选项上,而是会做得更好。一个字符串而不是一个正则表达式。
无论如何 - 试试这个:
tmp="/usr/tmp/tmp$$"
trap 'rm -f "$tmp"; exit' 0
find ~/Sites/www.domain.com -name '*.php' |
while IFS= read -r i; do
echo "file $i"
evil='<?php /**/ eval(base64_decode("aWYoZnVuY3Rpb25"));?>'
awk -v evil="$evil" 's=index($0,evil){$0 = substr($0,1,s-1) substr($0,s+length(evil)} 1' "$i" > "$tmp" $$ mv "$tmp" "$i"
done
我还修复了文件名的循环。永远不要使用for i in $(...)
因为包含任何空格的文件名都会失败。如果您的文件名包含换行符,我发布的循环将失败。
如果你想避免手动指定tmp文件,GNU awk会有一个-i inplace
标志。
答案 3 :(得分:0)
使用sed流编辑器从每个PHP文件第一行的开头删除<?php /**/ eval(base64_decode("aWYoZnVuY3Rpb25"));?>
。
流编辑器具有隐式和显式的行寻址。如果您省略行地址(数字,正则表达式或两者的组合),则将处理整个文件。
要点1:
如果只想定位第一行,则应明确指定它。
sed -i '1s/<pattern>/<substitution>/' <filename>
但是,由于您试图清除文件中的“ evil”,因此您可能希望在第一行中的任何位置(全局)删除“ evil”。
sed -i '1s/<pattern>/<substitution>/g' <filename>
要点2:
您要处理的“邪恶”使用非字母数字字符,因此必须警惕在各种情况下将其用作输入。为了使用正则表达式搜索正则表达式元字符(?,+,*,[,] 、.等),您必须:
用反斜杠转义元字符以避免模式
冲突(例如:\?
)或
更改正则表达式模式定界符以避免模式冲突,或者
两者(在这种情况下,您应该这样做)。
在sed中,您可以通过在模式开始前转义字符来更改正则表达式模式定界符。
示例:
sed -i '1s\#<pattern>#<substitution>#g' <filename>
要点3:
您可以将sed中的正则表达式作为<pattern>
来搜索字符串!根据定义,最基本的模式是字符序列。但是,必须遵守上述第二点,并在必要时转义任何正则表达式元字符或默认的模式定界符/。
您的邪恶,我是说正则表达式模式,其中包含正则表达式元字符和默认的模式定界符!
<?php /**/ eval(base64_decode("aWYoZnVuY3Rpb25"));?>
我将规定以下内容。注意,我现在使用双引号,因为我希望外壳程序在执行sed
之前进行变量插值。另外,由于我将正则表达式模式定界符更改为#
,因此不需要转义与该微块引号相关联的两个正斜杠。 :-)
#!/bin/bash
function evilRemover ()
{
pattern='\<\?php /\*\*/ eval\(base64_decode\("aWYoZnVuY3Rpb25"\)\);\?\>'
local IFS="\n"
for filename in "$@"; do
sed -i "1s\#${pattern}##g" "$filename"
done
}
evilRemover $(find ~/Sites/www.domain.com -name '*.php' -print)
注意:我会弯腰,说任何在文件名中添加空格的人都应该考虑使用下划线_
。
先生。上面的@Ed Morton尝试警告单词拆分的可能性,但是如果您将列表传递给上述函数,"$@"
应该阻止它。
文件名中隐藏的非打印字符可能很难处理,但是此特定解决方案应该可以高度确定地解决您的问题(99.9999%)。
更一般地:
#!/bin/bash
function deleteWordsFromLine ()
{
lineNumber=$1
pattern=$2
local IFS="\n"
shift 2
for filename in "$@"; do
sed -i "${lineNumber}s\#${pattern}##g" "$filename"
done
}
targetLine=1
word='\<\?php /\*\*/ eval\(base64_decode\("aWYoZnVuY3Rpb25"\)\);\?\>'
filenames=$(find ~/Sites/www.domain.com -name '*.php' -print)
deleteWordsFromLine $targetLine $word $filenames
如果最好删除所有文件的第一行...
#!/bin/bash
function deleteLine ()
{
lineNumber=$1
local IFS="\n"
shift 1
for filename in "$@"; do
sed -i "${lineNumber}d" "$filename"
done
}
targetLine=1
filenames=$(find ~/Sites/www.domain.com -name '*.php' -print)
deleteLine $targetLine $filenames
最终通知:
请务必以足够的权限执行该解决方案,否则find
命令将以以下格式将消息返回到stderr
。
find: '/some/dir/file.php': Permission denied