diff --git a/core/console.py b/core/console.py index 808abb3f..5015c2fc 100644 --- a/core/console.py +++ b/core/console.py @@ -11,6 +11,7 @@ import os import sys +import ast import glob import time import codecs @@ -505,7 +506,7 @@ def show_task(self, count=10): if st: if self.show_index <= index < count: self.show_index += 1 - parameter_config = " ".join(eval(st.parameter_config)).replace('\\', '/') + parameter_config = " ".join(ast.literal_eval(st.parameter_config)).replace('\\', '/') sts_table.add_row( [st.id, st.task_name, parameter_config, str(st.last_scan_time)[:19], st.is_finished]) @@ -819,7 +820,7 @@ def command_set(self, *args, **kwargs): option_name = param[0] option_value = param[1] - option_value = eval(option_value) if option_value in ['True', 'False', 'None'] else option_value + option_value = ast.literal_eval(option_value) if option_value in ['True', 'False', 'None'] else option_value if option_name not in self.configurable_options: logger.warn("[Console] You can't edit option {}.".format(option_name)) @@ -854,7 +855,7 @@ def command_set(self, *args, **kwargs): logger.error("[Console] you can only set option in {}.".format(option_list)) return - option_value = eval(option_value) if option_value in ['True', 'False'] else option_value + option_value = ast.literal_eval(option_value) if option_value in ['True', 'False'] else option_value if option_value in self.result_option_list[option_name]: self.result_options[option_name] = option_value @@ -882,7 +883,7 @@ def command_set(self, *args, **kwargs): logger.error("[Console] you can only set option in {}.".format(list(self.scan_options))) return - option_value = eval(option_value) if option_value in ['True', 'False', 'None'] else option_value + option_value = ast.literal_eval(option_value) if option_value in ['True', 'False', 'None'] else option_value if option_value in self.scan_option_list[option_name]: self.scan_options[option_name] = option_value @@ -1140,7 +1141,7 @@ def command_show(self, *args, **kwargs): for t in ts: if t.tam_type == 'Filter-Function': - filter_func[t.tam_key] = eval(t.tam_value) + filter_func[t.tam_key] = ast.literal_eval(t.tam_value) elif t.tam_type == 'Input-Control': input_control.append(t.tam_value) @@ -1333,7 +1334,7 @@ def command_config(self, *args, **kwargs): for t in ts: if t.tam_type == 'Filter-Function': - self.config_dict['filter_func'][t.tam_key] = eval(t.tam_value) + self.config_dict['filter_func'][t.tam_key] = ast.literal_eval(t.tam_value) elif t.tam_type == 'Input-Control': self.config_dict['input_control'].append(t.tam_value) diff --git a/core/plugins/phpunserializechain/main.py b/core/plugins/phpunserializechain/main.py index b8fc7cae..0c3ad531 100644 --- a/core/plugins/phpunserializechain/main.py +++ b/core/plugins/phpunserializechain/main.py @@ -11,6 +11,7 @@ import re +import ast import traceback from utils.log import logger, logger_console @@ -355,10 +356,10 @@ def get_params_from_sink_node(self, node_name): result = [node_name] elif node_name.startswith('Array-'): - result = eval(node_name[5:]) + result = ast.literal_eval(node_name[5:]) elif node_name[0] == '(' and node_name[0] == ')': - result = list(eval(result)) + result = list(ast.literal_eval(result)) return result @@ -421,7 +422,7 @@ def check_danger_sink(self, node): } if node.node_type == 'FunctionCall' and node.source_node in self.danger_function: - sink_node = eval(node.sink_node) if node.sink_node.startswith('(') else (node.sink_node) + sink_node = ast.literal_eval(node.sink_node) if node.sink_node.startswith('(') else (node.sink_node) if len(sink_node) >= len(self.danger_function[node.source_node]): @@ -570,7 +571,7 @@ def check_param_controllable(self, param_name, now_node): # 暂时简单的认为这样可控 return True elif param_name.startswith('Array-'): - arraylist = eval(param_name[6:]) + arraylist = ast.literal_eval(param_name[6:]) for key in arraylist: if key.startswith('Variable-$this'): @@ -603,9 +604,9 @@ def check_param_controllable(self, param_name, now_node): node_type='Foreach').order_by('-id') for back_node in back_nodes: - if param_name == eval(back_node.sink_node)[-1]: + if param_name == ast.literal_eval(back_node.sink_node)[-1]: # 找到参数赋值 - new_param_name = self.deep_get_node_name(eval(back_node.sink_node)[0]) + new_param_name = self.deep_get_node_name(ast.literal_eval(back_node.sink_node)[0]) # 递归继续 return self.check_param_controllable(new_param_name, back_node) @@ -852,7 +853,7 @@ def find_prototype_class(self, now_class, find_method_name, unserchain, define_p deepth += 1 if nc: - now_class_extend_classs = eval(nc.sink_node) + now_class_extend_classs = ast.literal_eval(nc.sink_node) if len(now_class_extend_classs) > 0: # len > 0代表当前类存在原型类,所以向上寻找类的方法 @@ -900,7 +901,7 @@ def find_prototype_class(self, now_class, find_method_name, unserchain, define_p nc2s = self.dataflow_db.objects.filter(node_type='newClass', sink_node__contains=now_class_name) for nc2 in nc2s: - now_class_extend_classs = eval(nc2.sink_node) + now_class_extend_classs = ast.literal_eval(nc2.sink_node) if len(now_class_extend_classs) > 0 and now_class_name in now_class_extend_classs: child_class = self.deep_get_node_name(nc2.source_node) new_child_class_name = child_class diff --git a/core/pretreatment.py b/core/pretreatment.py index b25d69d8..0cc56e78 100644 --- a/core/pretreatment.py +++ b/core/pretreatment.py @@ -17,6 +17,7 @@ from utils.log import logger from Kunlun_M.const import ext_dict +from utils.file import un_zip import gc import os @@ -31,51 +32,6 @@ could_ast_pase_lans = ["php", "chromeext", "javascript", "html"] -def un_zip(target_path): - """ - 解压缩目标压缩包 - 实现新需求,解压缩后相应的js文件做代码格式化 - :return: - """ - - logger.info("[Pre][Unzip] Upzip file {}...".format(target_path)) - - if not os.path.isfile(target_path): - logger.warn("[Pre][Unzip] Target file {} is't exist...pass".format(target_path)) - return False - - zip_file = zipfile.ZipFile(target_path) - target_file_path = target_path + "_files/" - - if os.path.isdir(target_file_path): - logger.debug("[Pre][Unzip] Target files {} is exist...continue".format(target_file_path)) - return target_file_path - else: - os.mkdir(target_file_path) - - for names in zip_file.namelist(): - zip_file.extract(names, target_file_path) - - # 对其中部分文件中为js的时候,将js代码格式化便于阅读 - if names.endswith(".js"): - file_path = os.path.join(target_file_path, names) - file = codecs.open(file_path, 'r+', encoding='utf-8', errors='ignore') - file_content = file.read() - file.close() - - new_file = codecs.open(file_path, 'w+', encoding='utf-8', errors='ignore') - - opts = jsbeautifier.default_options() - opts.indent_size = 2 - - new_file.write(jsbeautifier.beautify(file_content, opts)) - new_file.close() - - zip_file.close() - - return target_file_path - - class Pretreatment: def __init__(self): diff --git a/docker/Dockerfile b/docker/Dockerfile index 8bd9b4d9..f106fd5c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -41,8 +41,10 @@ RUN /etc/init.d/nginx start RUN python3 -m pip install supervisor COPY /docker/supervisord.conf /etc/ +COPY /docker/start.sh / +RUN chmod +x /start.sh RUN /usr/local/bin/supervisord && /usr/local/bin/supervisorctl start all -ENTRYPOINT ["/usr/local/bin/supervisord", "-n"] +ENTRYPOINT ["/start.sh"] EXPOSE 80 \ No newline at end of file diff --git a/docker/nginx.conf b/docker/nginx.conf index 1402716e..889482f9 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -42,7 +42,7 @@ http { client_max_body_size 5120M; location /static/ { - alias /home/kunlun-m/static; + alias /home/kunlun-m/static/; } location / { diff --git a/docker/start.sh b/docker/start.sh new file mode 100644 index 00000000..c02e1fb8 --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +service nginx start + +/usr/local/bin/supervisord -n \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 00000000..aebdabb0 Binary files /dev/null and b/static/favicon.ico differ diff --git a/templates/dashboard/base.html b/templates/dashboard/base.html index 4eaeb44a..0845d99a 100644 --- a/templates/dashboard/base.html +++ b/templates/dashboard/base.html @@ -10,6 +10,7 @@ KunLun-MA | {% block title %}index{% endblock %} {% load static %} + diff --git a/templates/index.html b/templates/index.html index 58b015c5..22aaaef6 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,7 +5,7 @@ {% load static %} - + diff --git a/templates/login.html b/templates/login.html index 26ce123a..41d4ff58 100644 --- a/templates/login.html +++ b/templates/login.html @@ -5,7 +5,7 @@ {% load static %} - + diff --git a/templates/register.html b/templates/register.html index db085de5..a54c34cb 100644 --- a/templates/register.html +++ b/templates/register.html @@ -5,7 +5,7 @@ {% load static %} - + diff --git a/utils/file.py b/utils/file.py index 28f214c1..4648dbe9 100644 --- a/utils/file.py +++ b/utils/file.py @@ -15,10 +15,12 @@ import re import os import time +import json import codecs +import zipfile import traceback +import jsbeautifier from utils.log import logger -from core.pretreatment import ast_object from Kunlun_M.const import ext_dict, default_black_list, IGNORE_LIST from Kunlun_M.settings import IGNORE_PATH @@ -54,6 +56,79 @@ def file_list_parse(filelist, language=None): return result +def un_zip(target_path): + """ + 解压缩目标压缩包 + 实现新需求,解压缩后相应的js文件做代码格式化 + :return: + """ + + logger.info("[Pre][Unzip] Upzip file {}...".format(target_path)) + + if not os.path.isfile(target_path): + logger.warn("[Pre][Unzip] Target file {} is't exist...pass".format(target_path)) + return False + + zip_file = zipfile.ZipFile(target_path) + target_file_path = target_path + "_files/" + + if os.path.isdir(target_file_path): + logger.debug("[Pre][Unzip] Target files {} is exist...continue".format(target_file_path)) + return target_file_path + else: + os.mkdir(target_file_path) + + for names in zip_file.namelist(): + zip_file.extract(names, target_file_path) + + # 对其中部分文件中为js的时候,将js代码格式化便于阅读 + if names.endswith(".js"): + file_path = os.path.join(target_file_path, names) + file = codecs.open(file_path, 'r+', encoding='utf-8', errors='ignore') + file_content = file.read() + file.close() + + new_file = codecs.open(file_path, 'w+', encoding='utf-8', errors='ignore') + + opts = jsbeautifier.default_options() + opts.indent_size = 2 + + new_file.write(jsbeautifier.beautify(file_content, opts)) + new_file.close() + + zip_file.close() + + return target_file_path + + +def get_manifest_from_crt(file_path): + """ + 从chrome插件种获取manifest.json 文件内容 + :param file_path: + :return: + """ + target_files_path = un_zip(file_path) + + # 分析manifest.json + manifest_path = os.path.join(target_files_path, "manifest.json") + manifest = "" + + if os.path.isfile(manifest_path): + fi = codecs.open(manifest_path, "r", encoding='utf-8', errors='ignore') + manifest_content = fi.read() + fi.close() + + try: + manifest = json.loads(manifest_content, encoding='utf-8') + + except json.decoder.JSONDecodeError: + logger.warning( + "[Pretreatment][Chrome Ext] File {} parse error...".format(target_files_path)) + return "" + + return manifest + + def get_line(file_path, line_rule): """ 搜索指定文件的指定行到指定行的内容 @@ -454,13 +529,11 @@ def special_crx_keyword_match(self, keyword, match, unmatch): if not ffile_path: continue - ffile_object = ast_object.get_object(ffile_path) + manifest = get_manifest_from_crt(ffile_path) - if not ffile_object or 'manifest' not in ffile_object: + if not manifest: continue - manifest = ffile_object['manifest'] - target_files_path = ffile_object['target_files_path'] keywords = keyword.split('.') value_list = self.keyword_object_parse(keywords, manifest) diff --git a/web/backend/views.py b/web/backend/views.py index 0df0cb40..e9fc1d54 100644 --- a/web/backend/views.py +++ b/web/backend/views.py @@ -6,6 +6,7 @@ # @Contact : lorexxar@gmail.com import os +import ast import codecs import json from django.contrib.auth.decorators import login_required @@ -44,7 +45,7 @@ def tasklog(req, task_id): ResultFlow = get_resultflow_class(task_id) rfs = ResultFlow.objects.all() - task.parameter_config = " ".join(eval(task.parameter_config)).replace('\\', '/') + task.parameter_config = " ".join(ast.literal_eval(task.parameter_config)).replace('\\', '/') resultflowdict = {} for rf in rfs: diff --git a/web/dashboard/controller/tasks.py b/web/dashboard/controller/tasks.py index 3c059f21..45416d30 100644 --- a/web/dashboard/controller/tasks.py +++ b/web/dashboard/controller/tasks.py @@ -4,6 +4,9 @@ # @Author : LoRexxar # @File : tasks.py # @Contact : lorexxar@gmail.com + +import ast + from django.contrib.auth.decorators import login_required from django.http import JsonResponse, HttpResponseNotFound from django.views.generic import TemplateView @@ -29,7 +32,7 @@ def get_context_data(self, **kwargs): for task in context['tasks']: task.is_finished = int(task.is_finished) - task.parameter_config = " ".join(eval(task.parameter_config)).replace('\\', '/') + task.parameter_config = " ".join(ast.literal_eval(task.parameter_config)).replace('\\', '/') return context @@ -50,7 +53,7 @@ def get(request, task_id): newevilfuncs = NewEvilFunc.objects.filter(scan_task_id=task_id).all() task.is_finished = int(task.is_finished) - task.parameter_config = " ".join(eval(task.parameter_config)).replace('\\', '/') + task.parameter_config = " ".join(ast.literal_eval(task.parameter_config)).replace('\\', '/') for taskresult in taskresults: taskresult.is_unconfirm = int(taskresult.is_unconfirm) diff --git a/web/dashboard/views.py b/web/dashboard/views.py index b9277cd3..a54dc6d1 100644 --- a/web/dashboard/views.py +++ b/web/dashboard/views.py @@ -5,6 +5,9 @@ # @File : views.py # @Contact : lorexxar@gmail.com + +import ast + from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from web.index.models import ScanTask @@ -16,7 +19,7 @@ def index(req): tasks = ScanTask.objects.all().order_by("-id") for task in tasks: task.is_finished = int(task.is_finished) - task.parameter_config = " ".join(eval(task.parameter_config)).replace('\\', '/') + task.parameter_config = " ".join(ast.literal_eval(task.parameter_config)).replace('\\', '/') data = {'tasks': tasks}