Skip to content

Commit

Permalink
Merge pull request #193 from LoRexxar/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
LoRexxar authored Sep 30, 2021
2 parents 86c3965 + 085873b commit e8f00fe
Show file tree
Hide file tree
Showing 16 changed files with 305 additions and 40 deletions.
10 changes: 5 additions & 5 deletions Kunlun_M/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,11 @@
}

VENDOR_ECOSYSTEM = {
"java": {"depsdev": "maven"},
'golang': {"depsdev": "go"},
'python': {"ossindex": "pypi"},
'php': {"ossindex": "composer"},
'nodejs': {"depsdev": "npm"},
"java": {"depsdev": "maven", "murphysec": "java"},
'golang': {"depsdev": "go", "murphysec": "golang"},
'python': {"ossindex": "pypi", "murphysec": "python"},
'php': {"ossindex": "composer", "murphysec": "php"},
'nodejs': {"depsdev": "npm", "murphysec": "js"},
}

VENDOR_VUL_LEVEL = ['None', 'low', 'low', 'low', 'medium', 'medium', 'medium', 'medium', 'high', 'high', 'high']
Expand Down
3 changes: 3 additions & 0 deletions Kunlun_M/settings.py.bak
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,6 @@ REMOTE_URL_APITOKEN = "secret_api_token_in_server"
# vendor vuln scan
WITH_VENDOR = True
ACTIVE_SCA_SYSTEM = ['depsdev', 'ossindex']
# ['depsdev', 'ossindex', 'murphysec']
MURPHYSEC_TOKEN = ""

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,5 @@ KunLun-M 是 404Team [星链计划](https://github.com/knownsec/404StarLink-Proj
次要贡献者:
- Dubhe [Sndav](https://github.com/Sndav)
- [#jax777](https://github.com/jax777)
- [lavon321](https://github.com/lavon321)
- [akkuman](https://github.com/akkuman)
121 changes: 117 additions & 4 deletions core/core_engine/php/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
is_repair_functions = [] # 修复函数初始化
is_controlled_params = []
scan_chain = [] # 回溯链变量
all_nodes = []
BASE_FUNCTIONCALL_LIST = ['FunctionCall', 'MethodCall', 'StaticMethodCall', 'ObjectProperty']
SPECIAL_FUNCTIONCALL_LIST = ['Eval', 'Echo', 'Print', 'Return', 'Break', 'Include',
'Require', 'Exit', 'Throw', 'Unset', 'Continue', 'Yield', 'Silence']
Expand Down Expand Up @@ -428,6 +429,9 @@ def is_controllable(expr, flag=None): # 获取表达式中的变量,看是否
if isinstance(expr, php.Variable):
expr = expr.name

if isinstance(expr, php.Cast):
expr = expr.expr.name

if expr in controlled_params: # 当为可控变量时 返回1
logger.debug('[AST] is_controllable --> {expr}'.format(expr=expr))
if flag:
Expand Down Expand Up @@ -511,6 +515,26 @@ def function_back(param, nodes, function_params, vul_function=None, file_path=No
vul_function=vul_function, file_path=file_path,
isback=isback, parent_node=parent_node)

return is_co, cp, expr_lineno

if isinstance(node, php.Class):
class_nodes = node.nodes

for class_node in class_nodes:
if isinstance(class_node, php.Method) and class_node.name == function_name:
method_nodes = class_node.nodes

for method_node in method_nodes:

if isinstance(method_node, php.Return):
return_node = method_node.node
# return_param = return_node.node

is_co, cp, expr_lineno = parameters_back(return_node, method_nodes, function_params,
vul_function=vul_function, file_path=file_path,
isback=isback, parent_node=parent_node)
return is_co, cp, expr_lineno

return is_co, cp, expr_lineno


Expand Down Expand Up @@ -643,7 +667,12 @@ def new_class_back(param, nodes, vul_function=None, file_path=None, isback=None)
:return:
"""
param = param.name
param_name = param.name
if hasattr(param, "name"):
# param_name = param.name
param_name = get_node_name(param)
else:
param_name = param

param_params = param.params

is_co = -1
Expand Down Expand Up @@ -753,7 +782,7 @@ def parameters_back(param, nodes, function_params=None, lineno=0,
cp = param
return is_co, cp, expr_lineno

if param_name == param_node and not isinstance(param_expr, list): # 找到变量的来源,开始继续分析变量的赋值表达式是否可控
if param_name == param_node and not isinstance(param_expr, list) and not isinstance(param_expr, php.TernaryOp): # 找到变量的来源,开始继续分析变量的赋值表达式是否可控
logger.debug(
"[AST] Find {}={} in line {}, start ast for param {}".format(param_name, param_expr, expr_lineno,
param_expr))
Expand All @@ -777,6 +806,21 @@ def parameters_back(param, nodes, function_params=None, lineno=0,
param = node.expr
else:
param = php.Variable(param_expr) # 每次找到一个污点的来源时,开始跟踪新污点,覆盖旧污点
param_name = param_expr

if param_name == param_node and isinstance(param_expr, php.TernaryOp):
terna1 = param_expr.iftrue
terna2 = param_expr.iffalse
param_ex = param_expr.expr
logger.debug("[AST] Find {} from TernaryOp from ?{}:{} in line {}.".format(param_name, terna1, terna2,
node.lineno))

file_path = os.path.normpath(file_path)
code = "{}={}?{}:{}".format(param_name, param_ex, terna1, terna2)
scan_chain.append(('TernaryOp', code, file_path, node.lineno))

param = node.expr
is_co = 3

if param_name == param_node and isinstance(node.expr, php.FunctionCall): # 当变量来源是函数时,处理函数内容
function_name = node.expr.name
Expand Down Expand Up @@ -1213,6 +1257,42 @@ def parameters_back(param, nodes, function_params=None, lineno=0,
code = "New {} param back from For".format(param)
scan_chain.append(('NewForBack', code, file_path, node.lineno))

elif isinstance(node, php.Foreach):
if param_name == node.valvar.name.name:
if isinstance(node.expr, php.ArrayOffset):
param_expr = node.expr.node
else:
param_expr = node.expr.name
expr_lineno = node.lineno
# 找到变量的来源,开始继续分析变量的赋值表达式是否可控
logger.debug(
"[AST] Find foreach {} as {} in line {}, start ast for param {}".format(param_expr, param_name, expr_lineno,param_expr))

file_path = os.path.normpath(file_path)
code = "foreach ({} as {})".format(param_expr, param_name)
scan_chain.append(('Foreach', code, file_path, node.lineno))
param = php.Variable(param_expr) # 每次找到一个污点的来源时,开始跟踪新污点,覆盖旧污点
param_name = param_expr
else:
foreach_nodes = node.node.nodes
foreach_node_lineno = node.node.lineno
logger.debug("[AST] Find foreach, start ast in foreach")

is_co, cp, expr_lineno = parameters_back(param, foreach_nodes, function_params, foreach_node_lineno,
function_flag=1, vul_function=vul_function, file_path=file_path,
isback=isback, parent_node=node)
function_flag = 0

if is_co in [-1, 1, 2]: # 目标确定直接返回
return is_co, cp, expr_lineno, param_name, param

if _is_co == 3 and cp != param:
param = _cp

file_path = os.path.normpath(file_path)
code = "New {} param back from Foreach".format(param)
scan_chain.append(('NewForBack', code, file_path, node.lineno))

if is_co == 3 or int(lineno) == node.lineno: # 当is_co为True时找到可控,停止递归
is_co, cp, expr_lineno = parameters_back(param, nodes[:-1], function_params, lineno,
function_flag=function_flag, vul_function=vul_function,
Expand Down Expand Up @@ -2030,6 +2110,39 @@ def analysis(nodes, vul_function, back_node, vul_lineno, file_path=None, functio
nodes = get_silence_params(node)
analysis(nodes, vul_function, back_node, vul_lineno, file_path)

elif isinstance(node, php.AssignOp):
if isinstance(node.right, php.FunctionCall) or isinstance(node.right, php.MethodCall) or isinstance(node.right,
php.StaticMethodCall):
anlysis_function(node.right, back_node, vul_function, function_params, vul_lineno, file_path=file_path)

if isinstance(node.right, php.Eval):
analysis_eval(node.right, vul_function, back_node, vul_lineno, function_params, file_path=file_path)

if isinstance(node.right, php.Silence):
buffer_.append(node.right)
analysis(buffer_, vul_function, back_node, vul_lineno, file_path, function_params)

elif isinstance(node, php.BinaryOp):
if isinstance(node.left, php.FunctionCall) or isinstance(node.right, php.MethodCall) or isinstance(node.right, php.StaticMethodCall):
anlysis_function(node.left, back_node, vul_function, function_params, vul_lineno, file_path=file_path)

if isinstance(node.left, php.Eval):
analysis_eval(node.left, vul_function, back_node, vul_lineno, function_params, file_path=file_path)

if isinstance(node.left, php.Silence):
buffer_.append(node.left)
analysis(buffer_, vul_function, back_node, vul_lineno, file_path, function_params)

if isinstance(node.right, php.FunctionCall) or isinstance(node.right, php.MethodCall) or isinstance(node.right, php.StaticMethodCall):
anlysis_function(node.right, back_node, vul_function, function_params, vul_lineno, file_path=file_path)

if isinstance(node.right, php.Eval):
analysis_eval(node.right, vul_function, back_node, vul_lineno, function_params, file_path=file_path)

if isinstance(node.right, php.Silence):
buffer_.append(node.right)
analysis(buffer_, vul_function, back_node, vul_lineno, file_path, function_params)

elif isinstance(node, php.Eval):
analysis_eval(node, vul_function, back_node, vul_lineno, function_params, file_path=file_path)

Expand All @@ -2039,7 +2152,7 @@ def analysis(nodes, vul_function, back_node, vul_lineno, file_path=None, functio
elif isinstance(node, php.If): # 函数调用在if-else语句中时
analysis_if_else(node, back_node, vul_function, vul_lineno, function_params, file_path=file_path)

elif isinstance(node, php.While) or isinstance(node, php.DoWhile) or isinstance(node, php.For): # 函数调用在循环中
elif isinstance(node, php.While) or isinstance(node, php.DoWhile) or isinstance(node, php.For) or isinstance(node, php.Foreach): # 函数调用在循环中
if isinstance(node.node, php.Block):
analysis(node.node.nodes, vul_function, back_node, vul_lineno, file_path, function_params)

Expand All @@ -2056,7 +2169,7 @@ def analysis(nodes, vul_function, back_node, vul_lineno, file_path=None, functio
analysis(node.nodes, vul_function, function_body, vul_lineno, function_params=function_params,
file_path=file_path)

elif isinstance(node, php.Class):
elif isinstance(node, php.Class) or isinstance(node, php.Trait):
analysis(node.nodes, vul_function, back_node, vul_lineno, file_path, function_params)

elif node_typename in FUNCTIONCALL_LIST:
Expand Down
35 changes: 21 additions & 14 deletions core/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,25 +893,32 @@ def scan(self):
controlled_params=self.controlled_list, svid=self.cvi)
logger.debug('[AST] [RET] {c}'.format(c=result))
if len(result) > 0:
if result[0]['code'] == 1: # 函数参数可控
return True, 'Function-param-controllable', result[0]['chain']
result_code_list = []

elif result[0]['code'] == 2: # 漏洞修复
return False, 'Function-param-controllable but fixed', result[0]['chain']
for r in result:
result_code_list.append(r['code'])

elif result[0]['code'] == 3: # 疑似漏洞
if self.is_unconfirm:
return True, 'Unconfirmed Function-param-controllable', result[0]['chain']
else:
return False, 'Unconfirmed Function-param-controllable', result[0]['chain']
if r['code'] == 1: # 函数参数可控
return True, 'Function-param-controllable', r['chain']

elif result[0]['code'] == -1: # 函数参数不可控
return False, 'Function-param-uncon', result[0]['chain']
for r in result:
if r['code'] == 4: # 新规则生成
return False, 'New Core', r['source']

elif result[0]['code'] == 4: # 新规则生成
return False, 'New Core', result[0]['source']
for r in result:
if r['code'] == 3: # 疑似漏洞
if self.is_unconfirm:
return True, 'Unconfirmed Function-param-controllable', r['chain']
else:
return False, 'Unconfirmed Function-param-controllable', r['chain']

logger.debug('[AST] [CODE] {code}'.format(code=result[0]['code']))
elif r['code'] == 2: # 漏洞修复
return False, 'Function-param-controllable but fixed', r['chain']

else: # 函数参数不可控
return False, 'Function-param-uncon', r['chain']

logger.debug('[AST] [CODE] {code}'.format(code=result_code_list))
else:
logger.debug(
'[AST] Parser failed / vulnerability parameter is not controllable {r}'.format(
Expand Down
6 changes: 3 additions & 3 deletions core/vendors.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def check_and_save_result(task_id, language, vendor_name, vendor_version):
cvi_id=VENDOR_CVIID,
language=language,
vulfile_path="VendorVul:{}".format(vv.id),
source_code="{}".format(vv.reference),
source_code="{}".format(vv.reference[:180]),
result_type=vendor_source_match,
is_unconfirm=False,
is_active=True
Expand All @@ -150,7 +150,7 @@ def check_and_save_result(task_id, language, vendor_name, vendor_version):
if sr:
node_source = vv.description
rf = ResultFlow(vul_id=sr.id, node_type='sca_scan',
node_content=vv.title, node_path=vv.reference,
node_content=vv.title, node_path=vv.reference[:280],
node_source=node_source, node_lineno=0)
rf.save()

Expand All @@ -169,7 +169,7 @@ def get_and_save_vendor_vuls(task_id, vendor_name, vendor_version, language, ext
if not settings.WITH_VENDOR and task_id:
return False

logger.info("[Vendor Vuls] Spider {} Vendor {} Vul.".format(language, vendor_name))
logger.info("[Vendor Vuls] Spider {} Vendor {} Version {} Vul.".format(language, vendor_name, vendor_version))

_vendor = {"name": vendor_name, "version": vendor_version}
for vuln in get_vulns(language, _vendor["name"], _vendor["version"]):
Expand Down
2 changes: 1 addition & 1 deletion core/vuln_apis/depsdev.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ def __get_affected_versions(package_name, source, source_id):
if len(pkg["versionsAffected"]) > 0:
for version in pkg["versionsAffected"]:
result.append(version["version"])
return result
return result
79 changes: 79 additions & 0 deletions core/vuln_apis/murphysec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env python
# encoding: utf-8
'''
@author: LoRexxar
@contact: lorexxar@gmail.com
@file: mofei.py
@time: 2021/9/27 11:47
@desc:
'''

import json
import requests

from Kunlun_M.settings import MURPHYSEC_TOKEN
from utils.log import logger

__MURPHYSECAPI = "https://api.murphysec.com/cert/v1/check"
__MURPHYSECVULAPI = "https://api.murphysec.com/cert/v1/latest"


def get_vulns_from_murphysec(language, package_name, version):
datas = {
"comp_name": package_name,
"version": version,
"language": language,
"filter":{
"level": "严重|高危|中危"
}
}

headers = {
"Authorization": "Bearer {}".format(MURPHYSEC_TOKEN),
"Content-Type": "application/json"
}
result = []

r = requests.post(url=__MURPHYSECAPI, headers=headers, data=json.dumps(datas))

if r.status_code == 200:
data = json.loads(r.content)

if data['code'] == 400:
logger.warning("[Vendor][Murphysec Scan] QPS limit.")
return result

elif data['code'] == 401:
logger.error("[Vendor][Murphysec Scan] Api Token error.")

else:
vuls = data['data']

for vul in vuls:
vuln = {}
vuln["vuln_id"] = vul['no']
vuln["title"] = vul['title']
# reference
urls = []
for u in vul['references']:
urls.append(u["url"])

vuln["reference"] = json.dumps(urls)
vuln["description"] = vul['description']
# get cve
cves = [vul['cve_id'], vul['cnvd_id']]
vuln["cves"] = json.dumps(cves)
# get severity
vuln["severity"] = int(vul['cvss'])

# affected_versions
# affected_versions = []
# for av in vul['effect']:
# affected_versions.append(av['version_end_excluding'])

vuln["affected_versions"] = [version]

result.append(vuln)

return result
6 changes: 5 additions & 1 deletion templates/dashboard/projects/project_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ <h3 class="box-title">Results</h3>
{% endif %}

{% if taskresult.result_type == 'vendor_source_match' %}
<td><a href="{{ taskresult.source_code }}">{{ taskresult.source_code }}</a></td>
<td>
{% for rs in taskresult.source_code %}
<a href="{{ rs }}">{{ rs }}</a><br>
{% endfor %}
</td>
{% else %}
<td>{{ taskresult.source_code }}</td>
{% endif %}
Expand Down
Loading

0 comments on commit e8f00fe

Please sign in to comment.