Skip to content

Instantly share code, notes, and snippets.

@jesselang
Last active October 6, 2015 17:57
Show Gist options
  • Save jesselang/1667c4a0a1bde618773e to your computer and use it in GitHub Desktop.
Save jesselang/1667c4a0a1bde618773e to your computer and use it in GitHub Desktop.
Script to assist in cleaning up one type of injection from PHP malware
#!/usr/bin/env python
# Script to assist in cleaning up one type of injection from PHP malware.
# Author: Jesse Lang | http://jesselang.com/
# Use it with "find" like this:
# $ for FILE in $(find -iname *.php); do python malware_cleanup.py $FILE; done
# This script takes one argument, a file path.
# The file is opened and scanned for a match.
# The user is given the option of cleaning or deleting the file, or doing nothing.
# If repairing the file would result in an empty file, the file is deleted instead.
# Example output:
# ./infected/sketchys/sket.php matched "eval (base64_decode (..."
# File size: 163 bytes
# Match size: 163 bytes
# Last modified: Tue Jul 29 08:26:57 2014
# Re(V)iew, re(P)air, (D)elete, or (S)kip? [V/P/D/S]:
import os
import re
import shutil
import sys
import time
from stat import ST_SIZE, ST_MTIME
# Constants.
MAX_FILE_READ = 1024 * 1024 # 1 megabyte.
try:
filename = sys.argv[1]
except IndexError:
print 'Error: This script takes one argument, a file path.'
sys.exit(1)
php_eval_decode_pattern_string = r'''<\?\s*?php\s*?(eval\s*?\(.*decode\s*?\().*?\?>'''
php_eval_decode_pattern = re.compile(php_eval_decode_pattern_string)
file = open(filename, 'r')
match = php_eval_decode_pattern.match(file.read(MAX_FILE_READ))
if match:
info = os.fstat(file.fileno())
print '%s matched "%s..."' % (filename, match.group(1))
print 'File size: %10d bytes' % info[ST_SIZE]
print 'Match size: %10d bytes' % len(match.group(0))
print 'Last modified: %s' % time.asctime(time.localtime(info[ST_MTIME]))
deciding = True
while deciding:
input = raw_input('Re(V)iew, re(P)air, (D)elete, or (S)kip? [V/P/D/S]: ')
if input.lower() == 'v':
os.system('less -p\'%s\' "%s"' % (php_eval_decode_pattern_string, sys.argv[1]))
elif input.lower() == 'p':
if info[ST_SIZE] == len(match.group(0)):
print 'Since the repaired file would be empty, deleting instead.'
file.close()
os.remove(filename)
deciding = False
else:
shutil.copyfile(filename, '%s-orig' % filename)
file.seek(0)
repaired_data = php_eval_decode_pattern.sub('', file.read(MAX_FILE_READ))
file.close()
file = open(filename, 'w')
file.write(repaired_data)
deciding = False
elif input.lower() == 'd':
confirm = raw_input('Are you sure you want to delete this file? [Y/N]: ')
if confirm.lower() == 'y':
file.close()
os.remove(filename)
deciding = False
elif input.lower() == 's':
deciding = False
file.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment