Skip to content

modus refactor #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pyc
.idea
6 changes: 6 additions & 0 deletions modus_operandi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.pyc
.idea
modus/bin/
modus/include/
modus/lib/
modus/lib64
107 changes: 107 additions & 0 deletions modus_operandi/CHANGELOG
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# --------------------------------------------------------------------------------------
# modus.py - 'modus operandi' project
# --------------------------------------------------------------------------------------
# based mostly on bugs described at code610.blogspot.com
# --------------------------------------------------------------------------------------
#
# last updates: (v0.8)
#
# 31.01.2018 / 21:18 -- complete refactor, multiprocessing and thread pools
# due to use of python-magic an libmagic this version won't work under windows
# -- support for dynamic plugin load
# -- support select plugins dir
# -- csv output
#
# last updates: (v0.7)
#
# 10.01.2018 / 12:36 -- added: piwigo.check_xss, piwigo.check_sqli_pwg_realesc
# 10.01.2018 / 12:20 -- added: dlink.check_xss module
#
# 10.01.2018 / 11:12 -- added module:
# -- basic_traversal (checking.py)
# -- basic_unserialize (checking.py)
# -- check_fileinc (wordpress.py)
# -- check_rce (wordpress.py)
#
# 10.01.2018 / 11:07 -- added modules:
# -- horde -- find few types of XSS (based on Horde 5.2.2x)
# -- wordpress -- find few XSS and SQLi bugs (based on Wordpress 4.x)
# -- genix -- find few basic XSS (based on GenixCMS 1.1.5)
# -- basic -- few sample test for XSS and SQLi
# --
#
# 09.01.2018 / 10:00 -- rewrited again to v0.7
#
# last updates: (v0.5)
#
# 03.01.2018 / 10:59 -- modified wordpress-declar modules...
# 02.01.2018 / 18:45 -- modified: wordpress_sqli_get* modules; breaktime;]
# 02.01.2018 / 16:42 -- added: wordpress_sqli_get_row_declar_where
# 02.01.2018 / 16:25 -- added: wordpress_sqli_get_results_declar_2ndParam
# 02.01.2018 / 16:11 -- modified: added missing HTTP methods to parse
# -- wordpress_sqli_declar renamed to
# wordpress_sqli_get_results_declar. splited again.
# 02.01.2018 / 14:20 -- modified wordpress_sqli_declar to properly
# spot sqli bug as well as verify buggy declaration in the
# source. TODO: prepare more checks; split checks to f()'s
#
# 02.01.2018 / 08:58 -- added: basic_traversal()
# 01.01.2018 / 23:51 -- notes:TODO:wordpres_* to modify...
# 01.01.2018 / 17:57 -- added:
# -- wordpress_fileinc | A
# -- wordpress_rce() | A
# -- detailed time (.now())
#
# 01.01.2018 / 15:43 -- added/modified:
# -- dlink_xss - quick test to grab some XSS (based on dir300) | A
# -- piwigo_xss - find XSS bugs in Piwigo code (based on 2.9.2) | A
# -- piwigo_sqli - to check for sqli bugs (based on 2.9.2) | A
# -- genix_sqli - check for sqli bugs in genix (1.1.5) | A
# -- genix_xss - find XSS bugs (based on genix1.1.5) | A
# -- horde_xss_setget -- XSS when set/get is not sanitized | A
# -- horde_xss_setdef - based on CVE-2017-1690[6-8] | A
# -- wordpress_sqli - to find more WPplugin bugs... | A
# -- wordpress_sqli_declar - to find WP plugin bugs (4.x) | A
# -- basic_xss - to find XSS bugs (methods) | M
# -- basic_fileinc_declar - include bugs with declaration(s) | M

# 01.01.2018 / 15:23 -- some small changes in function below; still TODO
# 01.01.2018 / 11:40 -- few modification of base_fileinc_declar; TODO
# 01.01.2018 / 11:06 -- continue...
# 29.12.2017 / 12:55 -- rewrited: basic_xss(_declar), basic_fileinc
# 29.12.2017 / 09:03 -- new idea for modus skeleton (v0.5)
# 28.12.2017 / 10:40 -- edited wordpress modules
# 28.12.2017 / 07:54 -- added: basic path/dir traversal check
# 27.12.2017 / 13:32 -- added: basic file include check
# 27.12.2017 / 13:13 -- added: basic unserialize check
# 26.12.2017 / 20:35 -- retesting zabbix (3.4.4) module
# 24.12.2017 / 08:47 -- added: wordpress_sqli_declar draft
# 24.12.2017 / 01:02 -- added: wordpress_sqli module draft
# 24.12.2017 / 00:40 -- few modification of basix_xss*-module(s)
# 23.12.2017 / 17:31 -- added: basic_sqli, basic_xss updated
# 23.12.2017 / 13:01 -- added: horde_xss_setget, basic_xss
# basic_xss_declar
# 23.12.2017 / 12:01 -- first stages ready (v0.4)
# 23.12.2017 / 11:06 -- rewriting the whole code again to v0.4
# 22.12.2017 / 10:22 -- preparing tests for new zabbix(3.4.4)
# 21.12.2017 / 17:08 -- checking dlink's bug modified
# 20.12.2017 / 15:31 -- few updates to verify bugs in dlink's
# 12.12.2017 / 09:29 -- recheck to verify horde bugs;
# / 12:04 -- 2 new sqli bugs in Horde5.2.x found
# / 17:54 -- reediting xss_piwigo
# 11.12.2017 / 19:04 -- 'the easiest solution is in the Code' - enlil
# 10.12.2017 / 12:08 -- updated sqli_piwigo module
# 10.12.2017 / 07:46 -- updating new modules
# 09.12.2017 / 11:42 -- modified to v0.3
# 08.12.2017 / 15:00 -- added: more test to sqli @piwigo
# -- added: tests for xss @piwigo
# 06.12.2017 / 18:59 -- testing horde with current modules
# 05.12.2017 / 00:08 -- rewriting the whole code to v0.2
# 30.11.2017 / 23:57 -- see: readme.txt
# 26.11.2017 / 20:11 -- rewrited few lines...
#

# more:
# code610.blogspot.com
#

65 changes: 65 additions & 0 deletions modus_operandi/modus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
modus.py - v0.8

small script I called modus.py from 'modus operandi'.
maybe you will find it useful.
for some reason, try to keep it private for now.
thanks.

more: code610.blogspot.com

last update(s): please see CHANGELOG file.
have fun
"""
import sys
from os.path import dirname
from os.path import abspath
import signal
from modus.cli import parse_params
from modus.ctx import ECTX
from modus.walker import SourceWalker
from modus.worker import run_worker_processes
from modus.analyzer import register_plugins


def manager_signal_handler():
signal.signal(signal.SIGINT, signal.SIG_IGN)


def setup_analyzers(plugins_dir):
return [A() for A in register_plugins(plugins_dir)]


def main():
ctx = ECTX()
parse_params(ctx)

source_walker = SourceWalker(ctx)
analyzers = setup_analyzers(ctx[ECTX.plugins_repo])

try:

run_worker_processes(
ctx,
source_walker,
analyzers
)

finally:
ctx.work_queue.close()
ctx.work_queue.cancel_join_thread()


if __name__ == "__main__":
sys.path.append(
dirname(dirname(abspath(__file__)))
)
try:
main()
except KeyboardInterrupt:
sys.stderr.write("Terminated\n")
finally:
sys.stdout.flush()
sys.stderr.flush()
Empty file.
76 changes: 76 additions & 0 deletions modus_operandi/modus/analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod
import os
import imp
import re
from os import path


class PluginRegistry(ABCMeta):
plugins = []

def __init__(cls, name, bases, attrs):
if name != 'Analyzer':
PluginRegistry.plugins.append(cls)


class Analyzer(object):
__metaclass__ = PluginRegistry
MAX_DISPLAY_LINE_LEN = 120

def __init__(self):
self.description = self.search_description()

def fit_line(self, line):
if Analyzer.MAX_DISPLAY_LINE_LEN < len(line):
out_str = line[:Analyzer.MAX_DISPLAY_LINE_LEN] + "...".lstrip()
else:
out_str = line[:-1].lstrip()
return out_str.replace('\n', ' ').replace('\r', '')

def scan(self, file_info):
file_path = file_info[0]
mime_type = file_info[1]
results = []
with open(file_path, 'rb') as f:
lines = f.readlines()
for tag, matcher in self.description.iteritems():
regex = re.compile(matcher[0])
for line_num, line in enumerate(lines):
if re.search(regex, line):
results.append(
(tag,
matcher[0],
mime_type,
path.realpath(file_path),
line_num + 1,
self.fit_line(line),
matcher[1]
)
)
if results:
return results

@abstractmethod
def search_description(self):
pass

@abstractmethod
def target_mime_wildcards(self):
pass


def register_plugins(plugins_dir):
for filename in os.listdir(plugins_dir):
modname, ext = os.path.splitext(filename)
if ext == '.py':
f, path, descr = imp.find_module(modname, [plugins_dir])
if f:
_ = imp.load_module(modname, f, path, descr)
return PluginRegistry.plugins


if __name__ == "__main__":
pass
107 changes: 107 additions & 0 deletions modus_operandi/modus/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

import argparse

from ctx import ECTX
from ctx import get_default_plugins_path
from analyzer import register_plugins
import sys

LINE_WITH = 80


class ListModules(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if namespace.repo:
plugins_dir = namespace.repo
else:
plugins_dir = get_default_plugins_path()
plugins = register_plugins(plugins_dir)
for plugin_class in plugins:
print(plugin_class.__name__)
print(plugin_class.__doc__)
print(" Mime types:")
for m in plugin_class().target_mime_wildcards():
print("\t%s" % m)
print("\n Search description:")
regex_dict = plugin_class().search_description()
for k, v in regex_dict.iteritems():
print(" TAG: %s" % k)
print("\tRegex: %s" % v[0])
print("\tComment: %s" % v[1])
print("-" * LINE_WITH)
sys.exit(0)


def stb(v):
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')


def parse_params(ctx):
banner = '\t[--] modus v0.7 [--]\n'

parser = argparse.ArgumentParser(description=banner)
parser.add_argument(
ECTX.start_path,
metavar="PATH",
type=str,
help='Source files directory.'
)
parser.add_argument(
'-%s' % ECTX.wildcard[0],
'--%s' % ECTX.wildcard,
metavar="REGEXP",
type=str,
help='File name wildcard. Default value is "%s".' % ctx['filter']
)
parser.add_argument(
'-%s' % ECTX.workers_num[0],
'--%s' % ECTX.workers_num,
metavar="NUMBER",
type=int,
help='Worker processes. Default value is (%d - %d). '
'Max possible value is (%d * %d)' % (ctx['cpu'], ctx['mps'], ctx['cpu'], ctx['mpl'])
)
parser.add_argument(
'-%s' % ECTX.parallelism_level[0],
'--%s' % ECTX.parallelism_level,
metavar="NUMBER",
type=int,
help='Internal worker parallelism. Default value is %d. '
'Value in inclusive range <%d-%d>.' % (ctx['threads'], ctx['lwp'], ctx['hwp'])
)
parser.add_argument(
'-%s' % ECTX.plugins_repo[0],
'--%s' % ECTX.plugins_repo,
metavar="PATH",
type=str,
help='Custom plugins dir. Default value is %s. ' % (ctx['repo'])
)
parser.add_argument(
'-%s' % ECTX.strict_mime_check[0],
'--%s' % ECTX.strict_mime_check,
metavar="BOOL",
type=stb,
help='Strict mime match. WARNING! Disabling this will run all enabled analyzers '
'against all types of files ignoring potential '
'extension-content inconsistency Default value is %s. ' % (ctx['smc'])
)
parser.add_argument(
'-l',
'--list',
action=ListModules,
nargs=0,
help="List available checks."
)

ctx.update(parser.parse_args().__dict__)


if __name__ == "__main__":
pass
Loading