Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 36 additions & 34 deletions shelldetect.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# encoding: utf-8
"""
Shell Detector v1.0
Shell Detector v1.0
Shell Detector is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>

Special thanks to JetBRAINS for PyCharm license
Expand All @@ -11,33 +11,33 @@
import sys
import re
import os
import optparse
import argparse
import base64
import stat
import fnmatch
import time
from hashlib import md5
import urllib2
import urllib.request, urllib.error
import cgi
import datetime


class PhpSerializer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)

def unserialize(self, s):
return PhpSerializer._unserialize_var(self, s)[0]
return self._unserialize_var(s)[0]

def _unserialize_var(self, s):
return (
{'i': PhpSerializer._unserialize_int
, 'b': PhpSerializer._unserialize_bool
, 'd': PhpSerializer._unserialize_double
, 'n': PhpSerializer._unserialize_null
, 's': PhpSerializer._unserialize_string
, 'a': PhpSerializer._unserialize_array
}[s[0].lower()](self, s[2:]))
{'i': self._unserialize_int
, 'b': self._unserialize_bool
, 'd': self._unserialize_double
, 'n': self._unserialize_null
, 's': self._unserialize_string
, 'a': self._unserialize_array
}[s[0].lower()](s[2:]))

def _unserialize_int(self, s):
x = s.partition(';')
Expand All @@ -63,7 +63,7 @@ def _unserialize_array(self, s):
a, k, s = {}, None, s[1:]

for i in range(0, int(l) * 2):
(v, s) = PhpSerializer._unserialize_var(self, s)
(v, s) = self._unserialize_var(s)

if k:
a[k] = v
Expand Down Expand Up @@ -122,25 +122,25 @@ def __init__(self, options):
if options.format is not None:
self._report_format = options.format"""

self._remotefingerprint = options.remote.lower() in ("yes", "true", "t", "1")
self._remotefingerprint = options.remote

def remote(self):
if self._remotefingerprint is True:
self.alert('Please note we are using remote shell database', 'yellow')
url = 'https://raw.github.com/emposha/PHP-Shell-Detector/master/shelldetect.db'
self._fingerprints = urllib2.urlopen(url).read()
self._fingerprints = urllib.request.urlopen(url).read()
try:
self._fingerprints = base64.decodestring(bytes(self._fingerprints))
self._fingerprints = base64.b64decode(self._fingerprints)
serial = PhpSerializer()
self._fingerprints = serial.unserialize(str(self._fingerprints))
self._fingerprints = serial.unserialize(self._fingerprints.decode())
except IOError as e:
print("({})".format(e))
else:
if os.path.isfile("shelldetect.db"):
try:
self._fingerprints = base64.decodestring(str(open('shelldetect.db', 'r').read()))
self._fingerprints = base64.b64decode(open('shelldetect.db', 'rb').read())
serial = PhpSerializer()
self._fingerprints = serial.unserialize(str(self._fingerprints))
self._fingerprints = serial.unserialize(self._fingerprints.decode())
except IOError as e:
print("({})".format(e))

Expand All @@ -166,7 +166,7 @@ def anaylize(self):
_match = _regex.findall(_content)
if _match:
self.getfileinfo(_filename)
if self._showlinenumbers is True:
if self._showlinenumbers:
_lines = _content.split("\n")
_linecounter = 1
for _line in _lines:
Expand All @@ -184,14 +184,14 @@ def anaylize(self):
def _get_precomputed_fingerprints(self):
if not hasattr(self, '_precomputed_fingerprints'):
self._precomputed_fingerprints = []
for fingerprint, shellname in self._fingerprints.iteritems():
for fingerprint, shellname in self._fingerprints.items():
if fingerprint == "version":
continue
if 'bb:' in fingerprint:
fingerprint = base64.decodestring(bytes(fingerprint.replace('bb:', '')))
fingerprint = base64.b64decode(bytes(fingerprint.replace('bb:', '')))
self._precomputed_fingerprints.append((re.compile(re.escape(fingerprint)), shellname))
return self._precomputed_fingerprints

def fingerprint(self, _filename, _content):
for _regex, shellname in self._get_precomputed_fingerprints():
_match = _regex.findall(base64.b64encode(_content))
Expand Down Expand Up @@ -237,7 +237,8 @@ def version(self):
except ValueError:
_version = 0
try:
_server_version = urllib2.urlopen('https://raw.github.com/emposha/PHP-Shell-Detector/master/version/db').read()
_server_version = urllib.request.urlopen('https://raw.github.com/emposha/PHP-Shell-Detector/master/version/db').read()
_server_version = _server_version.decode()
except ValueError:
_server_version = 0

Expand All @@ -248,8 +249,8 @@ def version(self):
self.alert('New version of shells signature database found. Please update!', 'red')

try:
_app_server_version = urllib2.urlopen('https://raw.github.com/emposha/Shell-Detector/master/version/app').read()
except urllib2.HTTPError:
_app_server_version = urllib.request.urlopen('https://raw.github.com/emposha/Shell-Detector/master/version/app').read()
except urllib.error.HTTPError:
_app_server_version = 0

if _app_server_version == 0:
Expand Down Expand Up @@ -301,9 +302,9 @@ def alert(self, _content, _color='', _class='info', _html=False, _flag=False):
}[_color]

if self.supports_color() is True:
print _color_result + _content + '\033[0m'
print(_color_result + _content + '\033[0m')
else:
print _content
print(_content)

if _flag is True:
self.output(_content, _class, _html)
Expand Down Expand Up @@ -337,12 +338,13 @@ def flush(self):
file.write(self._output)

#Start
parser = optparse.OptionParser()
parser.add_option('--extension', '-e', type="string", default="php,txt,asp", help="file extensions that should be scanned, comma separated")
parser.add_option('--linenumbers', '-l', default=True, help="show line number where suspicious function used")
parser.add_option('--directory', '-d', type="string", help="specify directory to scan")
parser.add_option('--remote', '-r', default="False", help="get shells signatures db by remote")
(options, args) = parser.parse_args()
parser = argparse.ArgumentParser()
parser.add_argument('--extension', '-e', type=str, default="php,txt,asp", help="file extensions that should be scanned, comma separated")
parser.add_argument('--linenumbers', '-l', default=True, help="show line number where suspicious function used (default)", action='store_true')
parser.add_argument('--no-linenumbers', '-L', help="suppress line numbers where suspicious function used", action='store_false', dest='linenumbers')
parser.add_argument('--directory', '-d', type=str, help="specify directory to scan")
parser.add_argument('--remote', '-r', default=False, action='store_true', help="get shells signatures db by remote")
options = parser.parse_args()

if len(sys.argv) == 1:
parser.print_usage()
Expand Down